This rule finds accesses through a pointer of a memory location that has already been freed (i.e. through a dangling pointer). Such memory blocks have already been released to the dynamic memory manager, and modifying them can lead to anything -from a segfault to memory corruption that would cause subsequent calls to the dynamic memory manger to behave +from a segfault to memory corruption that would cause subsequent calls to the dynamic memory manager to behave erratically, to a possible security vulnerability.
diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll index 6f3f4d43e9aa..218a54b36c51 100644 --- a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll +++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll @@ -51,5 +51,7 @@ predicate tooFewArguments(FunctionCall fc, Function f) { hasDefiniteNumberOfParameters(fde) | fde.getNumberOfParameters() > fc.getNumberOfArguments() - ) + ) and + // Don't report on implicit function declarations, as these are likely extraction errors. + not f.getADeclarationEntry().isImplicit() } diff --git a/cpp/ql/src/Telemetry/CompilerErrors.ql b/cpp/ql/src/Telemetry/CompilerErrors.ql deleted file mode 100644 index 0f5166f9e9f5..000000000000 --- a/cpp/ql/src/Telemetry/CompilerErrors.ql +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @name Compiler errors - * @description A count of all compiler errors, grouped by error text. - * @kind metric - * @tags summary telemetry - * @id cpp/telemetry/compiler-errors - */ - -import Metrics - -from CppMetrics::ErrorCount m -where RankMetricint? or
- /// System.Nullable.
+ /// System.Nullable<int>.
/// System.Nullable.
+ /// Holds if this type is System.Nullable<T>.
/// System.Span.
+ /// Holds if this type is System.Span<T>.
/// System.Span.
+ /// Holds if this type is of the form System.Span<byte>.
/// System.ReadOnlySpan.
+ /// Holds if this type is System.ReadOnlySpan<T>.
/// System.ReadOnlySpan.
+ /// Holds if this type is of the form System.ReadOnlySpan<byte>.
/// expressions and expr_location are populated by the constructor
/// (should not fail), so even if expression-type specific population fails (e.g., in
/// standalone extraction), the expression created via
- ///
- /// IEnumerable<string?> // false
- /// IEnumerable<string?>? // true
+ /// IEnumerable<string?> // false
+ /// IEnumerable<string?>? // true
/// string? // true
/// string[] // true
/// string?[] // false
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs
index f2fc4b85d7f5..141bded87acf 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs
@@ -86,7 +86,7 @@ private bool IsImplicitOperator(out ITypeSymbol containingType)
/// Logs an error if the name is not found.
///
/// Extractor context.
- /// The method name.
+ /// The method symbol.
/// The converted name.
private static string OperatorSymbol(Context cx, IMethodSymbol method)
{
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs
index 8d819d715f9f..67bb2808ae62 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs
@@ -152,7 +152,7 @@ internal void AddFreshLabel(Entity entity)
///
/// Enqueue the given action to be performed later.
///
- /// The action to run.
+ /// The action to run.
public void PopulateLater(Action a, bool preserveDuplicationKey = true)
{
var key = preserveDuplicationKey ? GetCurrentTagStackKey() : null;
@@ -598,7 +598,6 @@ public Entities.Location CreateLocation(Microsoft.CodeAnalysis.Location? locatio
///
/// Register a program entity which can be bound to comments.
///
- /// Extractor context.
/// Program entity.
/// Location of the entity.
public void BindComments(Entity entity, Microsoft.CodeAnalysis.Location? l)
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/TrapWriter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/TrapWriter.cs
index 4830c3209c26..42e933c8eaf4 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/TrapWriter.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/TrapWriter.cs
@@ -171,7 +171,7 @@ private static bool TryMove(string sourceFile, string destFile)
///
/// Close the trap file, and move it to the right place in the trap directory.
/// If the file exists already, rename it to allow the new file (ending .trap.gz)
- /// to sit alongside the old file (except if is true,
+ /// to sit alongside the old file (except if is true,
/// in which case only the existing file is kept).
///
public void Dispose()
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Trap/EscapingTextWriter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Trap/EscapingTextWriter.cs
index 63f5e81c3586..28e1d0bf146c 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Trap/EscapingTextWriter.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Trap/EscapingTextWriter.cs
@@ -8,7 +8,7 @@ namespace Semmle.Extraction.CSharp
{
///
/// A `TextWriter` object that wraps another `TextWriter` object, and which
- /// HTML escapes the characters `&`, `{`, `}`, `"`, `@`, and `#`, before
+ /// HTML escapes the characters &, {, }, ", @, and #, before
/// writing to the underlying object.
///
public sealed class EscapingTextWriter : TextWriter
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Trap/TrapExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/Trap/TrapExtensions.cs
index 787ba62e3e82..22e38bac51c0 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Trap/TrapExtensions.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Trap/TrapExtensions.cs
@@ -226,7 +226,7 @@ public static TextWriter AppendList(this EscapingTextWriter trapFile, string
///
/// Builds a trap builder using a separator and an action for each item in the list.
///
- /// The type of the items.
+ /// The type of the items.
/// The trap builder to append to.
/// The separator string (e.g. ",")
/// The list of items.
@@ -251,7 +251,7 @@ public static T1 BuildList(this T1 trapFile, string separator, IEnumerab
///
/// Builds a trap builder using a separator and an action for each item in the list.
///
- /// The type of the items.
+ /// The type of the items.
/// The trap builder to append to.
/// The separator string (e.g. ",")
/// The list of items.
diff --git a/csharp/extractor/Semmle.Util/CanonicalPathCache.cs b/csharp/extractor/Semmle.Util/CanonicalPathCache.cs
index a79854333ac7..d3cbf41fa101 100644
--- a/csharp/extractor/Semmle.Util/CanonicalPathCache.cs
+++ b/csharp/extractor/Semmle.Util/CanonicalPathCache.cs
@@ -208,7 +208,7 @@ public enum Symlinks
/// Create cache with a given capacity.
///
/// The algorithm for determining the canonical path.
- /// The size of the cache.
+ /// The size of the cache.
public CanonicalPathCache(int maxCapacity, PathStrategy pathStrategy)
{
if (maxCapacity <= 0)
@@ -230,7 +230,6 @@ public CanonicalPathCache(int maxCapacity, PathStrategy pathStrategy)
///
///
/// Size of the cache.
- /// Policy for following symlinks.
/// A new CanonicalPathCache.
public static CanonicalPathCache Create(ILogger logger, int maxCapacity)
{
diff --git a/csharp/extractor/Semmle.Util/CommandBuilder.cs b/csharp/extractor/Semmle.Util/CommandBuilder.cs
index 3d8f907f8669..1b6cd3176c43 100644
--- a/csharp/extractor/Semmle.Util/CommandBuilder.cs
+++ b/csharp/extractor/Semmle.Util/CommandBuilder.cs
@@ -62,7 +62,6 @@ public CommandBuilder CallBatFile(string batFile, string? argumentsOpt = null)
///
/// The argument to append.
/// Whether to always quote the argument.
- /// Whether to escape for cmd.exe
///
///
/// This implementation is copied from
diff --git a/csharp/ql/integration-tests/all-platforms/dotnet_build/test.py b/csharp/ql/integration-tests/all-platforms/dotnet_build/test.py
index c32d966acb4a..75ba09477fa0 100644
--- a/csharp/ql/integration-tests/all-platforms/dotnet_build/test.py
+++ b/csharp/ql/integration-tests/all-platforms/dotnet_build/test.py
@@ -1,7 +1,6 @@
import os
def check_build_out(msg, s):
- lines = s.splitlines()
lines = s.splitlines()
assert (
any (("[build-stdout]" in line) and (msg in line) for line in lines)
diff --git a/csharp/ql/lib/change-notes/2024-11-26-model-microsoft.jsinterop.ijsruntime.md b/csharp/ql/lib/change-notes/2024-11-26-model-microsoft.jsinterop.ijsruntime.md
new file mode 100644
index 000000000000..a99f9c8e0fd3
--- /dev/null
+++ b/csharp/ql/lib/change-notes/2024-11-26-model-microsoft.jsinterop.ijsruntime.md
@@ -0,0 +1,5 @@
+---
+category: minorAnalysis
+---
+* Added `js-interop` sinks for the `InvokeAsync` and `InvokeVoidAsync` methods of `Microsoft.JSInterop.IJSRuntime`, which can run arbitrary JavaScript.
+
diff --git a/csharp/ql/lib/ext/Microsoft.JSInterop.model.yml b/csharp/ql/lib/ext/Microsoft.JSInterop.model.yml
new file mode 100644
index 000000000000..78f5c0964c15
--- /dev/null
+++ b/csharp/ql/lib/ext/Microsoft.JSInterop.model.yml
@@ -0,0 +1,7 @@
+extensions:
+ - addsTo:
+ pack: codeql/csharp-all
+ extensible: sinkModel
+ data:
+ - ["Microsoft.JSInterop", "JSRuntimeExtensions", True, "InvokeAsync", "", "", "Argument[1]", "js-injection", "manual"]
+ - ["Microsoft.JSInterop", "JSRuntimeExtensions", True, "InvokeVoidAsync", "", "", "Argument[1]", "js-injection", "manual"]
diff --git a/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected b/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected
index 7b6e623288f3..efb6d30e660d 100644
--- a/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected
+++ b/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected
@@ -256,6 +256,12 @@ sink
| Microsoft.EntityFrameworkCore;RelationalDatabaseFacadeExtensions;ExecuteSqlRawAsync;(Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade,System.String,System.Object[]);Argument[1];sql-injection;manual |
| Microsoft.EntityFrameworkCore;RelationalDatabaseFacadeExtensions;ExecuteSqlRawAsync;(Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade,System.String,System.Threading.CancellationToken);Argument[1];sql-injection;manual |
| Microsoft.EntityFrameworkCore;RelationalQueryableExtensions;FromSqlRaw;(Microsoft.EntityFrameworkCore.DbSet,System.String,System.Object[]);Argument[1];sql-injection;manual |
+| Microsoft.JSInterop;JSRuntimeExtensions;InvokeAsync;(Microsoft.JSInterop.IJSRuntime,System.String,System.Object[]);Argument[1];js-injection;manual |
+| Microsoft.JSInterop;JSRuntimeExtensions;InvokeAsync;(Microsoft.JSInterop.IJSRuntime,System.String,System.Threading.CancellationToken,System.Object[]);Argument[1];js-injection;manual |
+| Microsoft.JSInterop;JSRuntimeExtensions;InvokeAsync;(Microsoft.JSInterop.IJSRuntime,System.String,System.TimeSpan,System.Object[]);Argument[1];js-injection;manual |
+| Microsoft.JSInterop;JSRuntimeExtensions;InvokeVoidAsync;(Microsoft.JSInterop.IJSRuntime,System.String,System.Object[]);Argument[1];js-injection;manual |
+| Microsoft.JSInterop;JSRuntimeExtensions;InvokeVoidAsync;(Microsoft.JSInterop.IJSRuntime,System.String,System.Threading.CancellationToken,System.Object[]);Argument[1];js-injection;manual |
+| Microsoft.JSInterop;JSRuntimeExtensions;InvokeVoidAsync;(Microsoft.JSInterop.IJSRuntime,System.String,System.TimeSpan,System.Object[]);Argument[1];js-injection;manual |
| ServiceStack.Messaging;BackgroundMqClient;SendAllOneWay;(System.Collections.Generic.IEnumerable);Argument[1].Element;file-content-store;manual |
| ServiceStack.Messaging;BackgroundMqClient;SendOneWay;(System.Object);Argument[0];file-content-store;manual |
| ServiceStack.Messaging;BackgroundMqClient;SendOneWay;(System.String,System.Object);Argument[1];file-content-store;manual |
diff --git a/csharp/ql/test/resources/stubs/System.Web.cs b/csharp/ql/test/resources/stubs/System.Web.cs
index d876a83e318f..d1942c07dc1d 100644
--- a/csharp/ql/test/resources/stubs/System.Web.cs
+++ b/csharp/ql/test/resources/stubs/System.Web.cs
@@ -390,6 +390,8 @@ public class JavaScriptSerializer
public JavaScriptSerializer() => throw null;
public JavaScriptSerializer(System.Web.Script.Serialization.JavaScriptTypeResolver resolver) => throw null;
public object DeserializeObject(string input) => throw null;
+ public T Deserialize (string input) => throw null;
+ public object Deserialize(string input, Type targetType) => throw null;
}
// Generated from `System.Web.Script.Serialization.JavaScriptTypeResolver` in `System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35`
diff --git a/docs/codeql/codeql-language-guides/codeql-for-go.rst b/docs/codeql/codeql-language-guides/codeql-for-go.rst
index 360ff6bb82f2..30799c146a8e 100644
--- a/docs/codeql/codeql-language-guides/codeql-for-go.rst
+++ b/docs/codeql/codeql-language-guides/codeql-for-go.rst
@@ -12,6 +12,7 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
codeql-library-for-go
abstract-syntax-tree-classes-for-working-with-go-programs
modeling-data-flow-in-go-libraries
+ customizing-library-models-for-go
- :doc:`Basic query for Go code `: Learn to write and run a simple CodeQL query.
@@ -23,3 +24,5 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
- :doc:`Modeling data flow in Go libraries `: When analyzing a Go program, CodeQL does not examine the source code for external packages.
To track the flow of untrusted data through a library, you can create a model of the library.
+
+- :doc:`Customizing library models for Go `: You can model frameworks and libraries that your codebase depends on using data extensions and publish them as CodeQL model packs.
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-go.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-go.rst
new file mode 100644
index 000000000000..c5b74ccd73ae
--- /dev/null
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-go.rst
@@ -0,0 +1,427 @@
+.. _customizing-library-models-for-go:
+
+Customizing library models for Go
+=================================
+
+You can model the methods and functions that control data flow in any framework or library. This is especially useful for custom frameworks or niche libraries, that are not supported by the standard CodeQL libraries.
+
+.. include:: ../reusables/beta-note-customizing-library-models.rst
+
+About this article
+------------------
+
+This article contains reference material about how to define custom models for sources, sinks, and flow summaries for Go dependencies in data extension files.
+
+About data extensions
+---------------------
+
+You can customize analysis by defining models (summaries, sinks, and sources) of your code's Go dependencies in data extension files. Each model defines the behavior of one or more elements of your library or framework, such as functions, methods, and fields. When you run dataflow analysis, these models expand the potential sources and sinks tracked by dataflow analysis and improve the precision of results.
+
+Most of the security queries search for paths from a source of untrusted input to a sink that represents a vulnerability. This is known as taint tracking. Each source is a starting point for dataflow analysis to track tainted data and each sink is an end point.
+
+Taint tracking queries also need to know how data can flow through elements that are not included in the source code. These are modeled as summaries. A summary model enables queries to synthesize the flow behavior through elements in dependency code that is not stored in your repository.
+
+Syntax used to define an element in an extension file
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Each model of an element is defined using a data extension where each tuple constitutes a model.
+A data extension file to extend the standard Go queries included with CodeQL is a YAML file with the form:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible:
+ data:
+ -
+ -
+ - ...
+
+Each YAML file may contain one or more top-level extensions.
+
+- ``addsTo`` defines the CodeQL pack name and extensible predicate that the extension is injected into.
+- ``data`` defines one or more rows of tuples that are injected as values into the extensible predicate. The number of columns and their types must match the definition of the extensible predicate.
+
+Data extensions use union semantics, which means that the tuples of all extensions for a single extensible predicate are combined, duplicates are removed, and all of the remaining tuples are queryable by referencing the extensible predicate.
+
+Publish data extension files in a CodeQL model pack to share
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can group one or more data extension files into a CodeQL model pack and publish it to the GitHub Container Registry. This makes it easy for anyone to download the model pack and use it to extend their analysis. For more information, see `Creating a CodeQL model pack `__ and `Publishing and using CodeQL packs `__ in the CodeQL CLI documentation.
+
+Extensible predicates used to create custom models in Go
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The CodeQL library for Go analysis exposes the following extensible predicates:
+
+- ``sourceModel(package, type, subtypes, name, signature, ext, output, kind, provenance)``. This is used to model sources of potentially tainted data. The ``kind`` of the sources defined using this predicate determine which threat model they are associated with. Different threat models can be used to customize the sources used in an analysis. For more information, see ":ref:`Threat models `."
+- ``sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance)``. This is used to model sinks where tainted data may be used in a way that makes the code vulnerable.
+- ``summaryModel(package, type, subtypes, name, signature, ext, input, output, kind, provenance)``. This is used to model flow through elements.
+- ``neutralModel(package, type, name, signature, kind, provenance)``. This is similar to a summary model but used to model the flow of values that have only a minor impact on the dataflow analysis. Manual neutral models (those with a provenance such as ``manual`` or ``ai-manual``) can be used to override generated summary models (those with a provenance such as ``df-generated``), so that the summary model will be ignored. Other than that, neutral models have no effect.
+
+The extensible predicates are populated using the models defined in data extension files.
+
+Examples of custom model definitions
+------------------------------------
+
+The examples in this section are taken from the standard CodeQL Go query pack published by GitHub. They demonstrate how to add tuples to extend extensible predicates that are used by the standard queries.
+
+Example: Taint sink in the ``database/sql`` package
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This example shows how the Go query pack models the argument of the ``Prepare`` method as a SQL injection sink.
+This is the ``Prepare`` method of the ``DB`` type in the ``database/sql`` package which creates a prepared statement.
+
+.. code-block:: go
+
+ func Tainted(db *sql.DB, name string) {
+ stmt, err := db.Prepare("SELECT * FROM users WHERE name = " + name) // The argument to this method is a SQL injection sink.
+ ...
+ }
+
+We need to add a tuple to the ``sinkModel``\(package, type, subtypes, name, signature, ext, input, kind, provenance) extensible predicate by updating a data extension file.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["database/sql", "DB", True, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"]
+
+Since we want to add a new sink, we need to add a tuple to the ``sinkModel`` extensible predicate.
+The first five values identify the function (in this case a method) to be modeled as a sink.
+
+- The first value ``database/sql`` is the package name.
+- The second value ``DB`` is the name of the type that the method is associated with.
+- The third value ``True`` is a flag that indicates whether or not the sink also applies to subtypes. This includes when the subtype embeds the given type, so that the method or field is promoted to be a method or field of the subtype. For interface methods it also includes types which implement the interface type.
+- The fourth value ``Prepare`` is the method name.
+- The fifth value ``""`` is the input type signature. For Go it should always be an empty string. It is needed for other languages where multiple functions may have the same name and they need to be distinguished by the number and types of the arguments.
+
+The sixth value should be left empty and is out of scope for this documentation.
+The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the sink.
+
+- The seventh value ``Argument[0]`` is the ``access path`` to the first argument passed to the method, which means that this is the location of the sink.
+- The eighth value ``sql-injection`` is the kind of the sink. The sink kind is used to define the queries where the sink is in scope. In this case - the SQL injection queries.
+- The ninth value ``manual`` is the provenance of the sink, which is used to identify the origin of the sink.
+
+Example: Taint source from the ``net/http`` package
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This example shows how the Go query pack models the return value from the ``FormValue`` method as a ``remote`` source.
+This is the ``FormValue`` method of the ``Request`` type which is located in the ``net/http`` package.
+
+.. code-block:: go
+
+ func Tainted(r *http.Request) {
+ name := r.FormValue("name") // The return value of this method is a source of tainted data.
+ ...
+ }
+
+
+We need to add a tuple to the ``sourceModel``\(package, type, subtypes, name, signature, ext, output, kind, provenance) extensible predicate by updating a data extension file.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sourceModel
+ data:
+ - ["net/http", "Request", True, "FormValue", "", "", "ReturnValue", "remote", "manual"]
+
+
+Since we are adding a new source, we need to add a tuple to the ``sourceModel`` extensible predicate.
+The first five values identify the function to be modeled as a source.
+
+- The first value ``net/http`` is the package name.
+- The second value ``Request`` is the type name, since the function is a method of the ``Request`` type.
+- The third value ``True`` is a flag that indicates whether or not the sink also applies to subtypes. This includes when the subtype embeds the given type, so that the method or field is promoted to be a method or field of the subtype. For interface methods it also includes types which implement the interface type.
+- The fourth value ``FormValue`` is the function name.
+- The fifth value ``""`` is the input type signature. For Go it should always be an empty string. It is needed for other languages where multiple functions may have the same name and they need to be distinguished by the number and types of the arguments.
+
+The sixth value should be left empty and is out of scope for this documentation.
+The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the source.
+
+- The seventh value ``ReturnValue`` is the access path to the return of the method, which means that it is the return value that should be considered a source of tainted input.
+- The eighth value ``remote`` is the kind of the source. The source kind is used to define the threat model where the source is in scope. ``remote`` applies to many of the security related queries as it means a remote source of untrusted data. As an example the SQL injection query uses ``remote`` sources. For more information, see ":ref:`Threat models `."
+- The ninth value ``manual`` is the provenance of the source, which is used to identify the origin of the source.
+
+Example: Add flow through the ``Max`` function
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This example shows how the Go query pack models flow through a function for a simple case.
+This pattern covers many of the cases where we need to summarize flow through a function that is stored in a library or framework outside the repository.
+
+.. code-block:: go
+
+ func ValueFlow {
+ a := []int{1, 2, 3}
+ max := slices.Max(a) // There is value flow from the elements of `a` to `max`.
+ ...
+ }
+
+We need to add a tuple to the ``summaryModel``\(package, type, subtypes, name, signature, ext, input, output, kind, provenance) extensible predicate by updating a data extension file:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: summaryModel
+ data:
+ - ["slices", "", False, "Max", "", "", "Argument[0].ArrayElement", "ReturnValue", "value", "manual"]
+
+Since we are adding flow through a method, we need to add tuples to the ``summaryModel`` extensible predicate.
+The first row defines flow from the first argument (``a`` in the example) to the return value (``max`` in the example).
+
+The first five values identify the function to be modeled as a summary.
+
+- The first value ``slices`` is the package name.
+- The second value ``""`` is left blank, since the function is not a method of a type.
+- The third value ``False`` is a flag that indicates whether or not the sink also applies to subtypes. This has no effect for non-method functions.
+- The fourth value ``Max`` is the function name.
+- The fifth value ``""`` is the input type signature. For Go it should always be an empty string. It is needed for other languages where multiple functions may have the same name and they need to be distinguished by the number and types of the arguments.
+
+The sixth value should be left empty and is out of scope for this documentation.
+The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the summary.
+
+- The seventh value is the access path to the input (where data flows from). ``Argument[0].ArrayElement`` is the access path to the array elements of the first argument (the elements of the slice in the example).
+- The eighth value ``ReturnValue`` is the access path to the output (where data flows to), in this case ``ReturnValue``, which means that the input flows to the return value.
+- The ninth value ``value`` is the kind of the flow. ``value`` flow indicates an entire value is moved, ``taint`` means that taint is propagated through the call.
+- The tenth value ``manual`` is the provenance of the summary, which is used to identify the origin of the summary.
+
+Example: Add flow through the ``Concat`` function
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This example shows how the Go query pack models flow through a function for a simple case.
+This pattern covers many of the cases where we need to summarize flow through a function that is stored in a library or framework outside the repository.
+
+.. code-block:: go
+
+ func ValueFlow {
+ a := []int{1, 2, 3}
+ b := []int{4, 5, 6}
+ c := slices.Concat(a, b) // There is taint flow from `a` and `b` to `c`.
+ ...
+ }
+
+We need to add a tuple to the ``summaryModel``\(package, type, subtypes, name, signature, ext, input, output, kind, provenance) extensible predicate by updating a data extension file:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: summaryModel
+ data:
+ - ["slices", "", False, "Concat", "", "", "Argument[0].ArrayElement.ArrayElement", "ReturnValue.ArrayElement", "value", "manual"]
+
+Since we are adding flow through a method, we need to add tuples to the ``summaryModel`` extensible predicate.
+The first row defines flow from the arguments (``a`` and ``b`` in the example) to the return value (``c`` in the example).
+
+The first five values identify the function to be modeled as a summary.
+
+- The first value ``slices`` is the package name.
+- The second value ``""`` is left blank, since the function is not a method of a type.
+- The third value ``False`` is a flag that indicates whether or not the sink also applies to subtypes. This has no effect for non-method functions.
+- The fourth value ``Max`` is the function name.
+- The fifth value ``""`` is the input type signature. For Go it should always be an empty string. It is needed for other languages where multiple functions may have the same name and they need to be distinguished by the number and types of the arguments.
+
+The sixth value should be left empty and is out of scope for this documentation.
+The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the summary.
+
+- The seventh value is the access path to the input (where data flows from). ``Argument[0].ArrayElement.ArrayElement`` is the access path to the array elements of the array elements of the first argument. Note that a variadic parameter of type `...T` is treated as if it has type `[]T` and arguments corresponding to the variadic parameter are accessed as elements of this slice.
+- The eighth value ``ReturnValue.ArrayElement`` is the access path to the output (where data flows to), in this case ``ReturnValue.ArrayElement``, which means that the input flows to the array elements of the return value.
+- The ninth value ``value`` is the kind of the flow. ``value`` flow indicates an entire value is moved, ``taint`` means that taint is propagated through the call.
+- The tenth value ``manual`` is the provenance of the summary, which is used to identify the origin of the summary.
+
+Example: Add flow through the ``Join`` function
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This example shows how the Go query pack models flow through a method for a simple case.
+This pattern covers many of the cases where we need to summarize flow through a function that is stored in a library or framework outside the repository.
+
+.. code-block:: go
+
+ func TaintFlow() {
+ elems := []string{"Hello", "World"}
+ sep := " "
+ t := strings.Join(elems, sep) // There is taint flow from elems and sep to t.
+ ...
+ }
+
+We need to add tuples to the ``summaryModel``\(package, type, subtypes, name, signature, ext, input, output, kind, provenance) extensible predicate by updating a data extension file:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: summaryModel
+ data:
+ - ["strings", "", False, "Join", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
+ - ["strings", "", False, "Join", "", "", "Argument[1]", "ReturnValue", "taint", "manual"]
+
+Since we are adding flow through a method, we need to add tuples to the ``summaryModel`` extensible predicate.
+Each tuple defines flow from one argument to the return value.
+The first row defines flow from the first argument (``elems`` in the example) to the return value (``t`` in the example) and the second row defines flow from the second argument (``sep`` in the example) to the return value (``t`` in the example).
+
+The first five values identify the function to be modeled as a summary.
+These are the same for both of the rows above as we are adding two summaries for the same method.
+
+- The first value ``strings`` is the package name.
+- The second value ``""`` is left blank, since the function is not a method of a type.
+- The third value ``False`` is a flag that indicates whether or not the sink also applies to subtypes. This has no effect for non-method functions.
+- The fourth value ``Join`` is the function name.
+- The fifth value ``""`` is the input type signature. For Go it should always be an empty string. It is needed for other languages where multiple functions may have the same name and they need to be distinguished by the number and types of the arguments.
+
+The sixth value should be left empty and is out of scope for this documentation.
+The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the summary.
+
+- The seventh value is the access path to the input (where data flows from). ``Argument[0]`` is the access path to the first argument (``elems`` in the example) and ``Argument[1]`` is the access path to the second argument (``sep`` in the example).
+- The eighth value ``ReturnValue`` is the access path to the output (where data flows to), in this case ``ReturnValue``, which means that the input flows to the return value.
+- The ninth value ``taint`` is the kind of the flow. ``taint`` means that taint is propagated through the call.
+- The tenth value ``manual`` is the provenance of the summary, which is used to identify the origin of the summary.
+
+It would also be possible to merge the two rows into one by using ".." to indicate a range in the seventh value. This would be useful if the method has many arguments and the flow is the same for all of them.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: summaryModel
+ data:
+ - ["strings", "", False, "Join", "", "", "Argument[0..1]", "ReturnValue", "taint", "manual"]
+
+This row defines flow from both the first and the second argument to the return value. The seventh value ``Argument[0..1]`` is shorthand for specifying an access path to both ``Argument[0]`` and ``Argument[1]``.
+
+Example: Add flow through the ``Hostname`` method
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This example shows how the Go query pack models flow through a method for a simple case.
+
+.. code-block:: go
+
+ func TaintFlow(u *url.URL) {
+ host := u.Hostname() // There is taint flow from u to host.
+ ...
+ }
+
+We need to add a tuple to the ``summaryModel``\(package, type, subtypes, name, signature, ext, input, output, kind, provenance) extensible predicate by updating a data extension file:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: summaryModel
+ data:
+ - ["net/url", "URL", True, "Hostname", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
+
+Since we are adding flow through a method, we need to add tuples to the ``summaryModel`` extensible predicate.
+Each tuple defines flow from one argument to the return value.
+The first row defines flow from the qualifier of the method call (``u`` in the example) to the return value (``host`` in the example).
+
+The first five values identify the function (in this case a method) to be modeled as a summary.
+
+- The first value ``net/url`` is the package name.
+- The second value ``URL`` is the receiver type.
+- The third value ``True`` is a flag that indicates whether or not the sink also applies to subtypes. This includes when the subtype embeds the given type, so that the method or field is promoted to be a method or field of the subtype. For interface methods it also includes types which implement the interface type.
+- The fourth value ``Hostname`` is the method name.
+- The fifth value ``""`` is the input type signature. For Go it should always be an empty string. It is needed for other languages where multiple functions may have the same name and they need to be distinguished by the number and types of the arguments.
+
+The sixth value should be left empty and is out of scope for this documentation.
+The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the summary.
+
+- The seventh value is the access path to the input (where data flows from). ``Argument[receiver]`` is the access path to the receiver (``u`` in the example).
+- The eighth value ``ReturnValue`` is the access path to the output (where data flows to), in this case ``ReturnValue``, which means that the input flows to the return value. When there are multiple return values, use ``ReturnValue[i]`` to refer to the ``i`` th return value (starting from 0).
+- The ninth value ``taint`` is the kind of the flow. ``taint`` means that taint is propagated through the call.
+- The tenth value ``manual`` is the provenance of the summary, which is used to identify the origin of the summary.
+
+Example: Accessing the ``Body`` field of an HTTP request
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This example shows how we can model a field read as a source of tainted data.
+
+.. code-block:: go
+
+ func TaintFlow(w http.ResponseWriter, r *http.Request) {
+ body := r.Body // The Body field of an HTTP request is a source of tainted data.
+ ...
+ }
+
+We need to add a tuple to the ``sourceModel``\(package, type, subtypes, name, signature, ext, output, kind, provenance) extensible predicate by updating a data extension file.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sourceModel
+ data:
+ - ["net/http", "Request", True, "Body", "", "", "", "remote", "manual"]
+
+Since we are adding a new source, we need to add a tuple to the ``sourceModel`` extensible predicate.
+The first five values identify the field to be modeled as a source.
+
+- The first value ``net/http`` is the package name.
+- The second value ``Request`` is the name of the type that the field is associated with.
+- The third value ``True`` is a flag that indicates whether or not the sink also applies to subtypes. For fields this means when the field is accessed as a promoted field in another type.
+- The fourth value ``Body`` is the field name.
+- The fifth value ``""`` is the input type signature. For Go it should always be an empty string. It is needed for other languages where multiple functions may have the same name and they need to be distinguished by the number and types of the arguments.
+
+The sixth value should be left empty and is out of scope for this documentation.
+The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the source.
+
+- The seventh value ``""`` is left blank. Leaving the access path of a source model blank indicates that it is a field access.
+- The eighth value ``remote`` is the source kind. This indicates that the source is a remote source of untrusted data.
+- The ninth value ``manual`` is the provenance of the source, which is used to identify the origin of the source.
+
+Package versions
+~~~~~~~~~~~~~~~~
+
+When the major version number is greater than 1 it is included in the package import path. It usually looks like ``/v2`` after the module import path. This is called the major version suffix. We normally want our models to apply to all versions of a package. Rather than having to repeat models with the package column changed to include all available versions, we can just use the package name without the major version suffix and this will be matched to any version. So models with ``github.com/couchbase/gocb`` in the package column will match packages imported from ``github.com/couchbase/gocb`` and ``github.com/couchbase/gocb/v2`` (or any other version).
+
+Note that packages hosted at ``gopkg.in`` use a slightly different syntax: the major version suffix looks like ``.v2``, and it is present even for version 1. This is also supported. So models with ``gopkg.in/yaml`` in the package column will match packages imported from ``gopkg.in/yaml.v1``, ``gopkg.in/yaml.v2`` and ``gopkg.in/yaml.v3``.
+
+To write models that only apply to ``github.com/couchbase/gocb/v2``, it is sufficient to include the major version suffix (``/v2``) in the package column. To write models that only apply to ``github.com/couchbase/gocb``, you may prefix the package column with ``fixed-version:``. For example, here are two models for a method that has changed name from v1 to v2.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["fixed-version:github.com/couchbase/gocb", "Cluster", True, "ExecuteAnalyticsQuery", "", "", "Argument[0]", "nosql-injection", "manual"]
+ - ["github.com/couchbase/gocb/v2", "Cluster", True, "AnalyticsQuery", "", "", "Argument[0]", "nosql-injection", "manual"]
+
+Package grouping
+~~~~~~~~~~~~~~~~
+
+Since Go uses URLs for package identifiers, it is possible for packages to be imported with different paths. For example, the ``glog`` package can be imported using both the ``github.com/golang/glog`` and ``gopkg.in/glog`` paths.
+
+To handle this, the CodeQL Go library uses a mapping from the package path to a group name for the package. This mapping can be specified using the ``packageGrouping`` extensible predicate, and then the models for the APIs in the package
+will use the the prefix ``group:`` followed by the group name in place of the package path.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/go
+ extensible: packageGrouping
+ data:
+ - ["glog", "github.com/golang/glog"]
+ - ["glog", "gopkg.in/glog"]
+ - addsTo:
+ pack: codeql/go
+ extensible: sinkModel
+ data:
+ - ["group:glog", "", False, "Info", "", "", "Argument[0]", "log-injection", "manual"]
+
+.. _threat-models-go:
+
+Threat models
+-------------
+
+.. include:: ../reusables/threat-model-description.rst
diff --git a/docs/codeql/ql-language-reference/modules.rst b/docs/codeql/ql-language-reference/modules.rst
index 75b61667246e..d0cbdd39e36d 100644
--- a/docs/codeql/ql-language-reference/modules.rst
+++ b/docs/codeql/ql-language-reference/modules.rst
@@ -431,10 +431,7 @@ The above query therefore evalutes to:
BigInt
======
-The built-in ``QlBuiltins`` module provides an **experimental** type ``BigInt`` of arbitrary-precision integers.
-
-This type is not available in the CodeQL CLI by default, but you can enable it by passing the ``--allow-experimental=bigint``
-option to the CodeQL CLI. Consequently, BigInts are currently disallowed in query results and dbscheme columns.
+The built-in ``QlBuiltins`` module provides a type ``BigInt`` of arbitrary-range integers.
Unlike ``int`` and ``float``, there is no automatic conversion between ``BigInt`` and other numeric types.
Instead, big integers can be constructed using the ``.toBigInt()`` methods of ``int`` and ``string``.
@@ -451,3 +448,5 @@ The other built-in operations are:
``rank``, ``unique``, ``any``.
* other: ``.pow(int)``, ``.abs()``, ``.gcd(BigInt)``, ``.minimum(BigInt)``,
``.maximum(BigInt)``.
+
+Note: big integers are currently disallowed in query results and dbscheme columns.
diff --git a/docs/codeql/ql-language-reference/ql-language-specification.rst b/docs/codeql/ql-language-reference/ql-language-specification.rst
index 60e5ece73303..4c8fd30076a8 100644
--- a/docs/codeql/ql-language-reference/ql-language-specification.rst
+++ b/docs/codeql/ql-language-reference/ql-language-specification.rst
@@ -445,7 +445,7 @@ An integer value is of type ``int``. Each value is a 32-bit two's complement int
A string is a finite sequence of 16-bit characters. The characters are interpreted as Unicode code points.
-A :ref:`big integer ` value is of type ``QlBuiltins::BigInt``. Each value is a signed arbitrary-precision integer.
+A :ref:`big integer ` value is of type ``QlBuiltins::BigInt``. Each value is a signed arbitrary-range integer.
The database includes a number of opaque entity values. Each such value has a type that is one of the database types, and an identifying integer. An entity value is written as the name of its database type followed by its identifying integer in parentheses. For example, ``@tree(12)``, ``@person(16)``, and ``@location(38132)`` are entity values. The identifying integers are left opaque to programmers in this specification, so an implementation of QL is free to use some other set of countable labels to identify its entities.
diff --git a/docs/codeql/ql-language-reference/types.rst b/docs/codeql/ql-language-reference/types.rst
index e14f542dcf88..d2d79fe8409b 100644
--- a/docs/codeql/ql-language-reference/types.rst
+++ b/docs/codeql/ql-language-reference/types.rst
@@ -52,7 +52,7 @@ independent of the database that you are querying.
QL has a range of built-in operations defined on primitive types. These are available by using dispatch on expressions of the appropriate type. For example, ``1.toString()`` is the string representation of the integer constant ``1``. For a full list of built-in operations available in QL, see the
section on `built-ins `__ in the QL language specification.
-Additionally, there is an experimental arbitrary-precision integer primitive type at :ref:`QlBuiltins::BigInt `. This type is not available in the CodeQL CLI by default, but you can enable it by passing the ``--allow-experimental=bigint`` option to the CodeQL CLI.
+Additionally, there is an arbitrary-range integer primitive type at :ref:`QlBuiltins::BigInt `.
.. index:: class
.. _classes:
diff --git a/go/documentation/library-coverage/coverage.csv b/go/documentation/library-coverage/coverage.csv
index 364e17aa94b6..cf5a8b715b0d 100644
--- a/go/documentation/library-coverage/coverage.csv
+++ b/go/documentation/library-coverage/coverage.csv
@@ -1,121 +1,142 @@
-package,sink,source,summary,sink:command-injection,sink:credentials-key,sink:jwt,sink:path-injection,sink:regex-use[0],sink:regex-use[1],sink:regex-use[c],sink:request-forgery,sink:request-forgery[TCP Addr + Port],sink:url-redirection,sink:url-redirection[0],sink:url-redirection[receiver],sink:xpath-injection,source:environment,source:file,source:remote,summary:taint,summary:value
-,,,8,,,,,,,,,,,,,,,,,3,5
-archive/tar,,,5,,,,,,,,,,,,,,,,,5,
-archive/zip,,,6,,,,,,,,,,,,,,,,,6,
-bufio,,,17,,,,,,,,,,,,,,,,,17,
-bytes,,,43,,,,,,,,,,,,,,,,,43,
-clevergo.tech/clevergo,1,,,,,,,,,,,,,,1,,,,,,
-compress/bzip2,,,1,,,,,,,,,,,,,,,,,1,
-compress/flate,,,4,,,,,,,,,,,,,,,,,4,
-compress/gzip,,,3,,,,,,,,,,,,,,,,,3,
-compress/lzw,,,1,,,,,,,,,,,,,,,,,1,
-compress/zlib,,,4,,,,,,,,,,,,,,,,,4,
-container/heap,,,5,,,,,,,,,,,,,,,,,5,
-container/list,,,20,,,,,,,,,,,,,,,,,20,
-container/ring,,,5,,,,,,,,,,,,,,,,,5,
-context,,,5,,,,,,,,,,,,,,,,,5,
-crypto,,,10,,,,,,,,,,,,,,,,,10,
-database/sql,,,11,,,,,,,,,,,,,,,,,11,
-encoding,,,77,,,,,,,,,,,,,,,,,77,
-errors,,,3,,,,,,,,,,,,,,,,,3,
-expvar,,,6,,,,,,,,,,,,,,,,,6,
-fmt,,,16,,,,,,,,,,,,,,,,,16,
-github.com/ChrisTrenkamp/goxpath,3,,,,,,,,,,,,,,,3,,,,,
-github.com/antchfx/htmlquery,4,,,,,,,,,,,,,,,4,,,,,
-github.com/antchfx/jsonquery,4,,,,,,,,,,,,,,,4,,,,,
-github.com/antchfx/xmlquery,8,,,,,,,,,,,,,,,8,,,,,
-github.com/antchfx/xpath,4,,,,,,,,,,,,,,,4,,,,,
-github.com/appleboy/gin-jwt,1,,,,1,,,,,,,,,,,,,,,,
-github.com/astaxie/beego,7,21,21,,,,5,,,,,,2,,,,,,21,21,
-github.com/beego/beego,14,42,42,,,,10,,,,,,4,,,,,,42,42,
-github.com/caarlos0/env,,5,2,,,,,,,,,,,,,,5,,,1,1
-github.com/clevergo/clevergo,1,,,,,,,,,,,,,,1,,,,,,
-github.com/codeskyblue/go-sh,4,,,4,,,,,,,,,,,,,,,,,
-github.com/couchbase/gocb,,,18,,,,,,,,,,,,,,,,,18,
-github.com/couchbaselabs/gocb,,,18,,,,,,,,,,,,,,,,,18,
-github.com/crankycoder/xmlpath,2,,,,,,,,,,,,,,,2,,,,,
-github.com/cristalhq/jwt,1,,,,1,,,,,,,,,,,,,,,,
-github.com/dgrijalva/jwt-go,3,,9,,2,1,,,,,,,,,,,,,,9,
-github.com/elazarl/goproxy,,2,2,,,,,,,,,,,,,,,,2,2,
-github.com/emicklei/go-restful,,7,,,,,,,,,,,,,,,,,7,,
-github.com/evanphx/json-patch,,,12,,,,,,,,,,,,,,,,,12,
-github.com/form3tech-oss/jwt-go,2,,,,2,,,,,,,,,,,,,,,,
-github.com/gin-gonic/gin,3,46,2,,,,3,,,,,,,,,,,,46,2,
-github.com/go-chi/chi,,3,,,,,,,,,,,,,,,,,3,,
-github.com/go-chi/jwtauth,1,,,,1,,,,,,,,,,,,,,,,
-github.com/go-jose/go-jose,3,,4,,2,1,,,,,,,,,,,,,,4,
-github.com/go-kit/kit/auth/jwt,1,,,,1,,,,,,,,,,,,,,,,
-github.com/go-pg/pg/orm,,,6,,,,,,,,,,,,,,,,,6,
-github.com/go-xmlpath/xmlpath,2,,,,,,,,,,,,,,,2,,,,,
-github.com/gobuffalo/envy,,7,,,,,,,,,,,,,,,7,,,,
-github.com/gobwas/ws,,2,,,,,,,,,,,,,,,,,2,,
-github.com/gofiber/fiber,5,,,,,,4,,,,,,,,1,,,,,,
-github.com/gogf/gf-jwt,1,,,,1,,,,,,,,,,,,,,,,
-github.com/going/toolkit/xmlpath,2,,,,,,,,,,,,,,,2,,,,,
-github.com/golang-jwt/jwt,3,,11,,2,1,,,,,,,,,,,,,,11,
-github.com/golang/protobuf/proto,,,4,,,,,,,,,,,,,,,,,4,
-github.com/gorilla/mux,,1,,,,,,,,,,,,,,,,,1,,
-github.com/gorilla/websocket,,3,,,,,,,,,,,,,,,,,3,,
-github.com/hashicorp/go-envparse,,1,,,,,,,,,,,,,,,1,,,,
-github.com/jbowtie/gokogiri/xml,4,,,,,,,,,,,,,,,4,,,,,
-github.com/jbowtie/gokogiri/xpath,1,,,,,,,,,,,,,,,1,,,,,
-github.com/joho/godotenv,,4,,,,,,,,,,,,,,,4,,,,
-github.com/json-iterator/go,,,4,,,,,,,,,,,,,,,,,4,
-github.com/kataras/iris/context,6,,,,,,6,,,,,,,,,,,,,,
-github.com/kataras/iris/middleware/jwt,2,,,,2,,,,,,,,,,,,,,,,
-github.com/kataras/iris/server/web/context,6,,,,,,6,,,,,,,,,,,,,,
-github.com/kataras/jwt,5,,,,5,,,,,,,,,,,,,,,,
-github.com/kelseyhightower/envconfig,,6,,,,,,,,,,,,,,,6,,,,
-github.com/labstack/echo,3,12,2,,,,2,,,,,,1,,,,,,12,2,
-github.com/lestrrat-go/jwx,2,,,,2,,,,,,,,,,,,,,,,
-github.com/lestrrat-go/libxml2/parser,3,,,,,,,,,,,,,,,3,,,,,
-github.com/lestrrat/go-jwx/jwk,1,,,,1,,,,,,,,,,,,,,,,
-github.com/masterzen/xmlpath,2,,,,,,,,,,,,,,,2,,,,,
-github.com/moovweb/gokogiri/xml,4,,,,,,,,,,,,,,,4,,,,,
-github.com/moovweb/gokogiri/xpath,1,,,,,,,,,,,,,,,1,,,,,
-github.com/ory/fosite/token/jwt,2,,,,2,,,,,,,,,,,,,,,,
-github.com/revel/revel,2,23,10,,,,1,,,,,,1,,,,,,23,10,
-github.com/robfig/revel,2,23,10,,,,1,,,,,,1,,,,,,23,10,
-github.com/santhosh-tekuri/xpathparser,2,,,,,,,,,,,,,,,2,,,,,
-github.com/sendgrid/sendgrid-go/helpers/mail,,,1,,,,,,,,,,,,,,,,,1,
-github.com/spf13/afero,34,,,,,,34,,,,,,,,,,,,,,
-github.com/square/go-jose,3,,4,,2,1,,,,,,,,,,,,,,4,
-github.com/valyala/fasthttp,35,50,5,,,,8,,,,17,8,2,,,,,,50,5,
-go.uber.org/zap,,,11,,,,,,,,,,,,,,,,,11,
-golang.org/x/crypto/ssh,4,,,4,,,,,,,,,,,,,,,,,
-golang.org/x/net/context,,,5,,,,,,,,,,,,,,,,,5,
-golang.org/x/net/html,,,16,,,,,,,,,,,,,,,,,16,
-golang.org/x/net/websocket,,2,,,,,,,,,,,,,,,,,2,,
-google.golang.org/protobuf/internal/encoding/text,,,1,,,,,,,,,,,,,,,,,1,
-google.golang.org/protobuf/internal/impl,,,2,,,,,,,,,,,,,,,,,2,
-google.golang.org/protobuf/proto,,,8,,,,,,,,,,,,,,,,,8,
-google.golang.org/protobuf/reflect/protoreflect,,,1,,,,,,,,,,,,,,,,,1,
-gopkg.in/couchbase/gocb,,,18,,,,,,,,,,,,,,,,,18,
-gopkg.in/go-jose/go-jose,3,,4,,2,1,,,,,,,,,,,,,,4,
-gopkg.in/go-xmlpath/xmlpath,2,,,,,,,,,,,,,,,2,,,,,
-gopkg.in/macaron,1,12,1,,,,,,,,,,,,1,,,,12,1,
-gopkg.in/square/go-jose,3,,4,,2,1,,,,,,,,,,,,,,4,
-gopkg.in/xmlpath,2,,,,,,,,,,,,,,,2,,,,,
-gopkg.in/yaml,,,9,,,,,,,,,,,,,,,,,9,
-html,,,8,,,,,,,,,,,,,,,,,8,
-io,5,4,34,,,,5,,,,,,,,,,,4,,34,
-k8s.io/api/core,,,10,,,,,,,,,,,,,,,,,10,
-k8s.io/apimachinery/pkg/runtime,,,47,,,,,,,,,,,,,,,,,47,
-launchpad.net/xmlpath,2,,,,,,,,,,,,,,,2,,,,,
-log,,,3,,,,,,,,,,,,,,,,,3,
-math/big,,,1,,,,,,,,,,,,,,,,,1,
-mime,,,14,,,,,,,,,,,,,,,,,14,
-net,2,16,100,,,,1,,,,,,,1,,,,,16,100,
-nhooyr.io/websocket,,2,,,,,,,,,,,,,,,,,2,,
-os,29,10,6,3,,,26,,,,,,,,,,7,3,,6,
-path,,,18,,,,,,,,,,,,,,,,,18,
-reflect,,,37,,,,,,,,,,,,,,,,,37,
-regexp,10,,20,,,,,3,3,4,,,,,,,,,,20,
-sort,,,1,,,,,,,,,,,,,,,,,1,
-strconv,,,9,,,,,,,,,,,,,,,,,9,
-strings,,,34,,,,,,,,,,,,,,,,,34,
-sync,,,34,,,,,,,,,,,,,,,,,34,
-syscall,5,2,8,5,,,,,,,,,,,,,2,,,8,
-text/scanner,,,3,,,,,,,,,,,,,,,,,3,
-text/tabwriter,,,1,,,,,,,,,,,,,,,,,1,
-text/template,,,6,,,,,,,,,,,,,,,,,6,
+package,sink,source,summary,sink:command-injection,sink:credentials-key,sink:jwt,sink:log-injection,sink:nosql-injection,sink:path-injection,sink:regex-use[0],sink:regex-use[1],sink:regex-use[c],sink:request-forgery,sink:request-forgery[TCP Addr + Port],sink:sql-injection,sink:url-redirection,sink:url-redirection[0],sink:url-redirection[receiver],sink:xpath-injection,source:environment,source:file,source:remote,source:stdin,summary:taint,summary:value
+,,,8,,,,,,,,,,,,,,,,,,,,,3,5
+archive/tar,,,5,,,,,,,,,,,,,,,,,,,,,5,
+archive/zip,,,6,,,,,,,,,,,,,,,,,,,,,6,
+bufio,,,17,,,,,,,,,,,,,,,,,,,,,17,
+bytes,,,43,,,,,,,,,,,,,,,,,,,,,43,
+clevergo.tech/clevergo,1,,,,,,,,,,,,,,,,,1,,,,,,,
+compress/bzip2,,,1,,,,,,,,,,,,,,,,,,,,,1,
+compress/flate,,,4,,,,,,,,,,,,,,,,,,,,,4,
+compress/gzip,,,3,,,,,,,,,,,,,,,,,,,,,3,
+compress/lzw,,,1,,,,,,,,,,,,,,,,,,,,,1,
+compress/zlib,,,4,,,,,,,,,,,,,,,,,,,,,4,
+container/heap,,,5,,,,,,,,,,,,,,,,,,,,,5,
+container/list,,,20,,,,,,,,,,,,,,,,,,,,,20,
+container/ring,,,5,,,,,,,,,,,,,,,,,,,,,5,
+context,,,5,,,,,,,,,,,,,,,,,,,,,5,
+crypto,,,10,,,,,,,,,,,,,,,,,,,,,10,
+database/sql,30,,11,,,,,,,,,,,,30,,,,,,,,,11,
+encoding,,,77,,,,,,,,,,,,,,,,,,,,,77,
+errors,,,3,,,,,,,,,,,,,,,,,,,,,3,
+expvar,,,6,,,,,,,,,,,,,,,,,,,,,6,
+fmt,3,,16,,,,3,,,,,,,,,,,,,,,,,16,
+github.com/ChrisTrenkamp/goxpath,3,,,,,,,,,,,,,,,,,,3,,,,,,
+github.com/Masterminds/squirrel,32,,,,,,,,,,,,,,32,,,,,,,,,,
+github.com/Sirupsen/logrus,145,,,,,,145,,,,,,,,,,,,,,,,,,
+github.com/antchfx/htmlquery,4,,,,,,,,,,,,,,,,,,4,,,,,,
+github.com/antchfx/jsonquery,4,,,,,,,,,,,,,,,,,,4,,,,,,
+github.com/antchfx/xmlquery,8,,,,,,,,,,,,,,,,,,8,,,,,,
+github.com/antchfx/xpath,4,,,,,,,,,,,,,,,,,,4,,,,,,
+github.com/appleboy/gin-jwt,1,,,,1,,,,,,,,,,,,,,,,,,,,
+github.com/astaxie/beego,71,21,21,,,,34,,5,,,,,,30,2,,,,,,21,,21,
+github.com/beego/beego,142,42,42,,,,68,,10,,,,,,60,4,,,,,,42,,42,
+github.com/caarlos0/env,,5,2,,,,,,,,,,,,,,,,,5,,,,1,1
+github.com/clevergo/clevergo,1,,,,,,,,,,,,,,,,,1,,,,,,,
+github.com/codeskyblue/go-sh,4,,,4,,,,,,,,,,,,,,,,,,,,,
+github.com/couchbase/gocb,8,,18,,,,,8,,,,,,,,,,,,,,,,18,
+github.com/couchbaselabs/gocb,8,,18,,,,,8,,,,,,,,,,,,,,,,18,
+github.com/crankycoder/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,
+github.com/cristalhq/jwt,1,,,,1,,,,,,,,,,,,,,,,,,,,
+github.com/davecgh/go-spew/spew,9,,,,,,9,,,,,,,,,,,,,,,,,,
+github.com/dgrijalva/jwt-go,3,,9,,2,1,,,,,,,,,,,,,,,,,,9,
+github.com/elazarl/goproxy,2,2,2,,,,2,,,,,,,,,,,,,,,2,,2,
+github.com/emicklei/go-restful,,7,,,,,,,,,,,,,,,,,,,,7,,,
+github.com/evanphx/json-patch,,,12,,,,,,,,,,,,,,,,,,,,,12,
+github.com/form3tech-oss/jwt-go,2,,,,2,,,,,,,,,,,,,,,,,,,,
+github.com/gin-gonic/gin,3,46,2,,,,,,3,,,,,,,,,,,,,46,,2,
+github.com/go-chi/chi,,3,,,,,,,,,,,,,,,,,,,,3,,,
+github.com/go-chi/jwtauth,1,,,,1,,,,,,,,,,,,,,,,,,,,
+github.com/go-gorm/gorm,13,,,,,,,,,,,,,,13,,,,,,,,,,
+github.com/go-jose/go-jose,3,,4,,2,1,,,,,,,,,,,,,,,,,,4,
+github.com/go-kit/kit/auth/jwt,1,,,,1,,,,,,,,,,,,,,,,,,,,
+github.com/go-pg/pg/orm,,,6,,,,,,,,,,,,,,,,,,,,,6,
+github.com/go-xmlpath/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,
+github.com/go-xorm/xorm,34,,,,,,,,,,,,,,34,,,,,,,,,,
+github.com/gobuffalo/envy,,7,,,,,,,,,,,,,,,,,,7,,,,,
+github.com/gobwas/ws,,2,,,,,,,,,,,,,,,,,,,,2,,,
+github.com/gofiber/fiber,5,,,,,,,,4,,,,,,,,,1,,,,,,,
+github.com/gogf/gf-jwt,1,,,,1,,,,,,,,,,,,,,,,,,,,
+github.com/gogf/gf/database/gdb,51,,,,,,,,,,,,,,51,,,,,,,,,,
+github.com/going/toolkit/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,
+github.com/golang-jwt/jwt,3,,11,,2,1,,,,,,,,,,,,,,,,,,11,
+github.com/golang/glog,90,,,,,,90,,,,,,,,,,,,,,,,,,
+github.com/golang/protobuf/proto,,,4,,,,,,,,,,,,,,,,,,,,,4,
+github.com/gorilla/mux,,1,,,,,,,,,,,,,,,,,,,,1,,,
+github.com/gorilla/websocket,,3,,,,,,,,,,,,,,,,,,,,3,,,
+github.com/hashicorp/go-envparse,,1,,,,,,,,,,,,,,,,,,1,,,,,
+github.com/jbowtie/gokogiri/xml,4,,,,,,,,,,,,,,,,,,4,,,,,,
+github.com/jbowtie/gokogiri/xpath,1,,,,,,,,,,,,,,,,,,1,,,,,,
+github.com/jinzhu/gorm,13,,,,,,,,,,,,,,13,,,,,,,,,,
+github.com/jmoiron/sqlx,12,,,,,,,,,,,,,,12,,,,,,,,,,
+github.com/joho/godotenv,,4,,,,,,,,,,,,,,,,,,4,,,,,
+github.com/json-iterator/go,,,4,,,,,,,,,,,,,,,,,,,,,4,
+github.com/kataras/iris/context,6,,,,,,,,6,,,,,,,,,,,,,,,,
+github.com/kataras/iris/middleware/jwt,2,,,,2,,,,,,,,,,,,,,,,,,,,
+github.com/kataras/iris/server/web/context,6,,,,,,,,6,,,,,,,,,,,,,,,,
+github.com/kataras/jwt,5,,,,5,,,,,,,,,,,,,,,,,,,,
+github.com/kelseyhightower/envconfig,,6,,,,,,,,,,,,,,,,,,6,,,,,
+github.com/labstack/echo,3,12,2,,,,,,2,,,,,,,1,,,,,,12,,2,
+github.com/lann/squirrel,32,,,,,,,,,,,,,,32,,,,,,,,,,
+github.com/lestrrat-go/jwx,2,,,,2,,,,,,,,,,,,,,,,,,,,
+github.com/lestrrat-go/libxml2/parser,3,,,,,,,,,,,,,,,,,,3,,,,,,
+github.com/lestrrat/go-jwx/jwk,1,,,,1,,,,,,,,,,,,,,,,,,,,
+github.com/masterzen/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,
+github.com/moovweb/gokogiri/xml,4,,,,,,,,,,,,,,,,,,4,,,,,,
+github.com/moovweb/gokogiri/xpath,1,,,,,,,,,,,,,,,,,,1,,,,,,
+github.com/ory/fosite/token/jwt,2,,,,2,,,,,,,,,,,,,,,,,,,,
+github.com/raindog308/gorqlite,24,,,,,,,,,,,,,,24,,,,,,,,,,
+github.com/revel/revel,2,23,10,,,,,,1,,,,,,,1,,,,,,23,,10,
+github.com/robfig/revel,2,23,10,,,,,,1,,,,,,,1,,,,,,23,,10,
+github.com/rqlite/gorqlite,24,,,,,,,,,,,,,,24,,,,,,,,,,
+github.com/santhosh-tekuri/xpathparser,2,,,,,,,,,,,,,,,,,,2,,,,,,
+github.com/sendgrid/sendgrid-go/helpers/mail,,,1,,,,,,,,,,,,,,,,,,,,,1,
+github.com/sirupsen/logrus,145,,,,,,145,,,,,,,,,,,,,,,,,,
+github.com/spf13/afero,34,,,,,,,,34,,,,,,,,,,,,,,,,
+github.com/square/go-jose,3,,4,,2,1,,,,,,,,,,,,,,,,,,4,
+github.com/uptrace/bun,63,,,,,,,,,,,,,,63,,,,,,,,,,
+github.com/valyala/fasthttp,35,50,5,,,,,,8,,,,17,8,,2,,,,,,50,,5,
+go.mongodb.org/mongo-driver/mongo,14,,,,,,,14,,,,,,,,,,,,,,,,,
+go.uber.org/zap,33,,11,,,,33,,,,,,,,,,,,,,,,,11,
+golang.org/x/crypto/ssh,4,,,4,,,,,,,,,,,,,,,,,,,,,
+golang.org/x/net/context,,,5,,,,,,,,,,,,,,,,,,,,,5,
+golang.org/x/net/html,,,16,,,,,,,,,,,,,,,,,,,,,16,
+golang.org/x/net/websocket,,2,,,,,,,,,,,,,,,,,,,,2,,,
+google.golang.org/protobuf/internal/encoding/text,,,1,,,,,,,,,,,,,,,,,,,,,1,
+google.golang.org/protobuf/internal/impl,,,2,,,,,,,,,,,,,,,,,,,,,2,
+google.golang.org/protobuf/proto,,,8,,,,,,,,,,,,,,,,,,,,,8,
+google.golang.org/protobuf/reflect/protoreflect,,,1,,,,,,,,,,,,,,,,,,,,,1,
+gopkg.in/Masterminds/squirrel,32,,,,,,,,,,,,,,32,,,,,,,,,,
+gopkg.in/couchbase/gocb,8,,18,,,,,8,,,,,,,,,,,,,,,,18,
+gopkg.in/glog,90,,,,,,90,,,,,,,,,,,,,,,,,,
+gopkg.in/go-jose/go-jose,3,,4,,2,1,,,,,,,,,,,,,,,,,,4,
+gopkg.in/go-xmlpath/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,
+gopkg.in/macaron,1,12,1,,,,,,,,,,,,,,,1,,,,12,,1,
+gopkg.in/square/go-jose,3,,4,,2,1,,,,,,,,,,,,,,,,,,4,
+gopkg.in/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,
+gopkg.in/yaml,,,9,,,,,,,,,,,,,,,,,,,,,9,
+gorm.io/gorm,13,,,,,,,,,,,,,,13,,,,,,,,,,
+html,,,8,,,,,,,,,,,,,,,,,,,,,8,
+io,5,4,34,,,,,,5,,,,,,,,,,,,4,,,34,
+k8s.io/api/core,,,10,,,,,,,,,,,,,,,,,,,,,10,
+k8s.io/apimachinery/pkg/runtime,,,47,,,,,,,,,,,,,,,,,,,,,47,
+k8s.io/klog,90,,,,,,90,,,,,,,,,,,,,,,,,,
+launchpad.net/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,
+log,20,,3,,,,20,,,,,,,,,,,,,,,,,3,
+math/big,,,1,,,,,,,,,,,,,,,,,,,,,1,
+mime,,,14,,,,,,,,,,,,,,,,,,,,,14,
+net,2,16,100,,,,,,1,,,,,,,,1,,,,,16,,100,
+nhooyr.io/websocket,,2,,,,,,,,,,,,,,,,,,,,2,,,
+os,29,11,6,3,,,,,26,,,,,,,,,,,7,3,,1,6,
+path,,,18,,,,,,,,,,,,,,,,,,,,,18,
+reflect,,,37,,,,,,,,,,,,,,,,,,,,,37,
+regexp,10,,20,,,,,,,3,3,4,,,,,,,,,,,,20,
+slices,,,17,,,,,,,,,,,,,,,,,,,,,,17
+sort,,,1,,,,,,,,,,,,,,,,,,,,,1,
+strconv,,,9,,,,,,,,,,,,,,,,,,,,,9,
+strings,,,34,,,,,,,,,,,,,,,,,,,,,34,
+sync,,,34,,,,,,,,,,,,,,,,,,,,,34,
+syscall,5,2,8,5,,,,,,,,,,,,,,,,2,,,,8,
+text/scanner,,,3,,,,,,,,,,,,,,,,,,,,,3,
+text/tabwriter,,,1,,,,,,,,,,,,,,,,,,,,,1,
+text/template,,,6,,,,,,,,,,,,,,,,,,,,,6,
+xorm.io/xorm,34,,,,,,,,,,,,,,34,,,,,,,,,,
diff --git a/go/documentation/library-coverage/coverage.rst b/go/documentation/library-coverage/coverage.rst
index 848d989d2d58..48f24355a333 100644
--- a/go/documentation/library-coverage/coverage.rst
+++ b/go/documentation/library-coverage/coverage.rst
@@ -9,27 +9,27 @@ Go framework & library support
Framework / library,Package,Flow sources,Taint & value steps,Sinks (total)
`Afero `_,``github.com/spf13/afero*``,,,34
`CleverGo `_,"``clevergo.tech/clevergo*``, ``github.com/clevergo/clevergo*``",,,2
- `Couchbase official client(gocb) `_,"``github.com/couchbase/gocb*``, ``gopkg.in/couchbase/gocb*``",,36,
- `Couchbase unofficial client `_,``github.com/couchbaselabs/gocb*``,,18,
+ `Couchbase official client(gocb) `_,"``github.com/couchbase/gocb*``, ``gopkg.in/couchbase/gocb*``",,36,16
+ `Couchbase unofficial client `_,``github.com/couchbaselabs/gocb*``,,18,8
`Echo `_,``github.com/labstack/echo*``,12,2,3
`Fiber `_,``github.com/gofiber/fiber*``,,,5
`Fosite `_,``github.com/ory/fosite*``,,,2
`Gin `_,``github.com/gin-gonic/gin*``,46,2,3
- `Glog `_,"``github.com/golang/glog*``, ``gopkg.in/glog*``, ``k8s.io/klog*``",,,
+ `Glog `_,"``github.com/golang/glog*``, ``gopkg.in/glog*``, ``k8s.io/klog*``",,,270
`Go JOSE `_,"``github.com/go-jose/go-jose*``, ``github.com/square/go-jose*``, ``gopkg.in/square/go-jose*``, ``gopkg.in/go-jose/go-jose*``",,16,12
`Go kit `_,``github.com/go-kit/kit*``,,,1
- `Go-spew `_,``github.com/davecgh/go-spew/spew*``,,,
+ `Go-spew `_,``github.com/davecgh/go-spew/spew*``,,,9
`Gokogiri `_,"``github.com/jbowtie/gokogiri*``, ``github.com/moovweb/gokogiri*``",,,10
`Iris `_,``github.com/kataras/iris*``,,,14
`Kubernetes `_,"``k8s.io/api*``, ``k8s.io/apimachinery*``",,57,
- `Logrus `_,"``github.com/Sirupsen/logrus*``, ``github.com/sirupsen/logrus*``",,,
+ `Logrus `_,"``github.com/Sirupsen/logrus*``, ``github.com/sirupsen/logrus*``",,,290
`Macaron `_,``gopkg.in/macaron*``,12,1,1
`Revel `_,"``github.com/revel/revel*``, ``github.com/robfig/revel*``",46,20,4
`SendGrid `_,``github.com/sendgrid/sendgrid-go*``,,1,
- `Standard library `_,"````, ``archive/*``, ``bufio``, ``bytes``, ``cmp``, ``compress/*``, ``container/*``, ``context``, ``crypto``, ``crypto/*``, ``database/*``, ``debug/*``, ``embed``, ``encoding``, ``encoding/*``, ``errors``, ``expvar``, ``flag``, ``fmt``, ``go/*``, ``hash``, ``hash/*``, ``html``, ``html/*``, ``image``, ``image/*``, ``index/*``, ``io``, ``io/*``, ``log``, ``log/*``, ``maps``, ``math``, ``math/*``, ``mime``, ``mime/*``, ``net``, ``net/*``, ``os``, ``os/*``, ``path``, ``path/*``, ``plugin``, ``reflect``, ``reflect/*``, ``regexp``, ``regexp/*``, ``slices``, ``sort``, ``strconv``, ``strings``, ``sync``, ``sync/*``, ``syscall``, ``syscall/*``, ``testing``, ``testing/*``, ``text/*``, ``time``, ``time/*``, ``unicode``, ``unicode/*``, ``unsafe``",32,587,51
+ `Standard library `_,"````, ``archive/*``, ``bufio``, ``bytes``, ``cmp``, ``compress/*``, ``container/*``, ``context``, ``crypto``, ``crypto/*``, ``database/*``, ``debug/*``, ``embed``, ``encoding``, ``encoding/*``, ``errors``, ``expvar``, ``flag``, ``fmt``, ``go/*``, ``hash``, ``hash/*``, ``html``, ``html/*``, ``image``, ``image/*``, ``index/*``, ``io``, ``io/*``, ``log``, ``log/*``, ``maps``, ``math``, ``math/*``, ``mime``, ``mime/*``, ``net``, ``net/*``, ``os``, ``os/*``, ``path``, ``path/*``, ``plugin``, ``reflect``, ``reflect/*``, ``regexp``, ``regexp/*``, ``slices``, ``sort``, ``strconv``, ``strings``, ``sync``, ``sync/*``, ``syscall``, ``syscall/*``, ``testing``, ``testing/*``, ``text/*``, ``time``, ``time/*``, ``unicode``, ``unicode/*``, ``unsafe``",33,604,104
`XPath `_,``github.com/antchfx/xpath*``,,,4
`appleboy/gin-jwt `_,``github.com/appleboy/gin-jwt*``,,,1
- `beego `_,"``github.com/astaxie/beego*``, ``github.com/beego/beego*``",63,63,21
+ `beego `_,"``github.com/astaxie/beego*``, ``github.com/beego/beego*``",63,63,213
`chi `_,``github.com/go-chi/chi*``,3,,
`cristalhq/jwt `_,``github.com/cristalhq/jwt*``,,,1
`fasthttp `_,``github.com/valyala/fasthttp*``,50,5,35
@@ -39,7 +39,7 @@ Go framework & library support
`go-sh `_,``github.com/codeskyblue/go-sh*``,,,4
`golang.org/x/crypto/ssh `_,``golang.org/x/crypto/ssh*``,,,4
`golang.org/x/net `_,``golang.org/x/net*``,2,21,
- `goproxy `_,``github.com/elazarl/goproxy*``,2,2,
+ `goproxy `_,``github.com/elazarl/goproxy*``,2,2,2
`gorilla/mux `_,``github.com/gorilla/mux*``,1,,
`gorilla/websocket `_,``github.com/gorilla/websocket*``,3,,
`goxpath `_,``github.com/ChrisTrenkamp/goxpath*``,,,3
@@ -59,7 +59,7 @@ Go framework & library support
`xmlquery `_,``github.com/antchfx/xmlquery*``,,,8
`xpathparser `_,``github.com/santhosh-tekuri/xpathparser*``,,,2
`yaml `_,``gopkg.in/yaml*``,,9,
- `zap `_,``go.uber.org/zap*``,,11,
- Others,"``github.com/caarlos0/env``, ``github.com/gobuffalo/envy``, ``github.com/hashicorp/go-envparse``, ``github.com/joho/godotenv``, ``github.com/kelseyhightower/envconfig``",23,2,
- Totals,,306,911,268
+ `zap `_,``go.uber.org/zap*``,,11,33
+ Others,"``github.com/Masterminds/squirrel``, ``github.com/caarlos0/env``, ``github.com/go-gorm/gorm``, ``github.com/go-xorm/xorm``, ``github.com/gobuffalo/envy``, ``github.com/gogf/gf/database/gdb``, ``github.com/hashicorp/go-envparse``, ``github.com/jinzhu/gorm``, ``github.com/jmoiron/sqlx``, ``github.com/joho/godotenv``, ``github.com/kelseyhightower/envconfig``, ``github.com/lann/squirrel``, ``github.com/raindog308/gorqlite``, ``github.com/rqlite/gorqlite``, ``github.com/uptrace/bun``, ``go.mongodb.org/mongo-driver/mongo``, ``gopkg.in/Masterminds/squirrel``, ``gorm.io/gorm``, ``xorm.io/xorm``",23,2,391
+ Totals,,307,928,1532
diff --git a/go/ql/lib/change-notes/2024-11-20-heuristic-logging-sinks.md b/go/ql/lib/change-notes/2024-11-20-heuristic-logging-sinks.md
new file mode 100644
index 000000000000..46f5988b3798
--- /dev/null
+++ b/go/ql/lib/change-notes/2024-11-20-heuristic-logging-sinks.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* A call to a method whose name starts with "Debug", "Error", "Fatal", "Info", "Log", "Output", "Panic", "Print", "Trace", "Warn" or "With" defined on an interface whose name ends in "logger" or "Logger" is now considered a LoggerCall. In particular, it is a sink for `go/clear-text-logging` and `go/log-injection`. This may lead to some more alerts in those queries.
diff --git a/go/ql/lib/ext/database.sql.driver.model.yml b/go/ql/lib/ext/database.sql.driver.model.yml
index c2d780bb7c86..10cfba9388a6 100644
--- a/go/ql/lib/ext/database.sql.driver.model.yml
+++ b/go/ql/lib/ext/database.sql.driver.model.yml
@@ -1,4 +1,14 @@
extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["database/sql/driver", "Execer", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["database/sql/driver", "ExecerContext", True, "ExecContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["database/sql/driver", "Conn", True, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["database/sql/driver", "ConnPrepareContext", True, "PrepareContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["database/sql/driver", "Queryer", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["database/sql/driver", "QueryerContext", True, "QueryContext", "", "", "Argument[1]", "sql-injection", "manual"]
- addsTo:
pack: codeql/go-all
extensible: summaryModel
diff --git a/go/ql/lib/ext/database.sql.model.yml b/go/ql/lib/ext/database.sql.model.yml
index e1083f6e49a6..5854ced527d5 100644
--- a/go/ql/lib/ext/database.sql.model.yml
+++ b/go/ql/lib/ext/database.sql.model.yml
@@ -1,4 +1,32 @@
extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["database/sql", "Conn", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["database/sql", "Conn", True, "ExecContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["database/sql", "Conn", True, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["database/sql", "Conn", True, "PrepareContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["database/sql", "Conn", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["database/sql", "Conn", True, "QueryContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["database/sql", "Conn", True, "QueryRow", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["database/sql", "Conn", True, "QueryRowContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["database/sql", "DB", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["database/sql", "DB", True, "ExecContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["database/sql", "DB", True, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["database/sql", "DB", True, "PrepareContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["database/sql", "DB", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["database/sql", "DB", True, "QueryContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["database/sql", "DB", True, "QueryRow", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["database/sql", "DB", True, "QueryRowContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["database/sql", "Tx", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["database/sql", "Tx", True, "ExecContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["database/sql", "Tx", True, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["database/sql", "Tx", True, "PrepareContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["database/sql", "Tx", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["database/sql", "Tx", True, "QueryContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["database/sql", "Tx", True, "QueryRow", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["database/sql", "Tx", True, "QueryRowContext", "", "", "Argument[1]", "sql-injection", "manual"]
- addsTo:
pack: codeql/go-all
extensible: summaryModel
diff --git a/go/ql/lib/ext/fmt.model.yml b/go/ql/lib/ext/fmt.model.yml
index cad64ce0fdf6..2baac68da3a0 100644
--- a/go/ql/lib/ext/fmt.model.yml
+++ b/go/ql/lib/ext/fmt.model.yml
@@ -1,4 +1,11 @@
extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["fmt", "", False, "Print", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["fmt", "", False, "Printf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["fmt", "", False, "Println", "", "", "Argument[0]", "log-injection", "manual"]
- addsTo:
pack: codeql/go-all
extensible: summaryModel
diff --git a/go/ql/lib/ext/github.com.beego.beego.client.orm.model.yml b/go/ql/lib/ext/github.com.beego.beego.client.orm.model.yml
new file mode 100644
index 000000000000..08c0572b894a
--- /dev/null
+++ b/go/ql/lib/ext/github.com.beego.beego.client.orm.model.yml
@@ -0,0 +1,42 @@
+extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: packageGrouping
+ data:
+ - ["beego-orm", "github.com/beego/beego/client/orm"]
+ - ["beego-orm", "github.com/astaxie/beego/orm"]
+ - ["beego-orm", "github.com/beego/beego/orm"]
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["group:beego-orm", "Condition", True, "Raw", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:beego-orm", "DB", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "DB", True, "ExecContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:beego-orm", "DB", True, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "DB", True, "PrepareContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:beego-orm", "DB", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "DB", True, "QueryContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:beego-orm", "DB", True, "QueryRow", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "DB", True, "QueryRowContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:beego-orm", "Ormer", True, "Raw", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "And", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "Delete", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "From", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "GroupBy", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "Having", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "In", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "InnerJoin", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "InsertInto", "", "", "Argument[0..1]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "LeftJoin", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "On", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "Or", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "OrderBy", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "RightJoin", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "Select", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "Set", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "Subquery", "", "", "Argument[0..1]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "Update", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "Values", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QueryBuilder", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:beego-orm", "QuerySeter", True, "FilterRaw", "", "", "Argument[1]", "sql-injection", "manual"]
diff --git a/go/ql/lib/ext/github.com.beego.beego.core.logs.model.yml b/go/ql/lib/ext/github.com.beego.beego.core.logs.model.yml
new file mode 100644
index 000000000000..b55c3be507dd
--- /dev/null
+++ b/go/ql/lib/ext/github.com.beego.beego.core.logs.model.yml
@@ -0,0 +1,34 @@
+extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: packageGrouping
+ data:
+ - ["beego-logs", "github.com/astaxie/beego/logs"]
+ - ["beego-logs", "github.com/beego/beego/logs"]
+ - ["beego-logs", "github.com/beego/beego/core/logs"]
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["group:beego-logs", "", False, "Alert", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "", False, "Critical", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "", False, "Debug", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "", False, "Emergency", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "", False, "Error", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "", False, "Info", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "", False, "Informational", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "", False, "Notice", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "", False, "Trace", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "", False, "Warn", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "", False, "Warning", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "BeeLogger", True, "Alert", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "BeeLogger", True, "Critical", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "BeeLogger", True, "Debug", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "BeeLogger", True, "Emergency", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "BeeLogger", True, "Error", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "BeeLogger", True, "Info", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "BeeLogger", True, "Informational", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "BeeLogger", True, "Notice", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "BeeLogger", True, "Trace", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "BeeLogger", True, "Warn", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego-logs", "BeeLogger", True, "Warning", "", "", "Argument[0..1]", "log-injection", "manual"]
diff --git a/go/ql/lib/ext/github.com.beego.beego.core.utils.model.yml b/go/ql/lib/ext/github.com.beego.beego.core.utils.model.yml
index 4eb0688e37e7..63c05b920401 100644
--- a/go/ql/lib/ext/github.com.beego.beego.core.utils.model.yml
+++ b/go/ql/lib/ext/github.com.beego.beego.core.utils.model.yml
@@ -6,6 +6,11 @@ extensions:
- ["beego-utils", "github.com/astaxie/beego/utils"]
- ["beego-utils", "github.com/beego/beego/utils"]
- ["beego-utils", "github.com/beego/beego/core/utils"]
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["group:beego-utils", "", False, "Display", "", "", "Argument[0]", "log-injection", "manual"]
- addsTo:
pack: codeql/go-all
extensible: summaryModel
diff --git a/go/ql/lib/ext/github.com.beego.beego.server.web.model.yml b/go/ql/lib/ext/github.com.beego.beego.server.web.model.yml
index 963000fffccd..0c539522c5a7 100644
--- a/go/ql/lib/ext/github.com.beego.beego.server.web.model.yml
+++ b/go/ql/lib/ext/github.com.beego.beego.server.web.model.yml
@@ -10,6 +10,18 @@ extensions:
pack: codeql/go-all
extensible: sinkModel
data:
+ # log-injection
+ - ["group:beego", "", False, "Alert", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego", "", False, "Critical", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego", "", False, "Debug", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego", "", False, "Emergency", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego", "", False, "Error", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego", "", False, "Info", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego", "", False, "Informational", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego", "", False, "Notice", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego", "", False, "Trace", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego", "", False, "Warn", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:beego", "", False, "Warning", "", "", "Argument[0..1]", "log-injection", "manual"]
# path-injection
- ["group:beego", "", False, "Walk", "", "", "Argument[1]", "path-injection", "manual"]
- ["group:beego", "Controller", True, "SaveToFile", "", "", "Argument[1]", "path-injection", "manual"]
diff --git a/go/ql/lib/ext/github.com.couchbase.gocb.model.yml b/go/ql/lib/ext/github.com.couchbase.gocb.model.yml
index ff0a4c22c8de..d17b53dd6da9 100644
--- a/go/ql/lib/ext/github.com.couchbase.gocb.model.yml
+++ b/go/ql/lib/ext/github.com.couchbase.gocb.model.yml
@@ -3,28 +3,43 @@ extensions:
pack: codeql/go-all
extensible: packageGrouping
data:
- - ["gocb", "github.com/couchbase/gocb"]
- - ["gocb", "gopkg.in/couchbase/gocb"]
- - ["gocb", "github.com/couchbaselabs/gocb"]
+ - ["gocb1", "fixed-version:github.com/couchbase/gocb"]
+ - ["gocb1", "fixed-version:gopkg.in/couchbase/gocb.v1"]
+ - ["gocb1", "fixed-version:github.com/couchbaselabs/gocb"]
+ - ["gocb2", "github.com/couchbase/gocb/v2"]
+ - ["gocb2", "gopkg.in/couchbase/gocb.v2"]
+ - ["gocb2", "github.com/couchbaselabs/gocb/v2"]
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["group:gocb1", "Bucket", True, "ExecuteN1qlQuery", "", "", "Argument[0]", "nosql-injection", "manual"]
+ - ["group:gocb1", "Bucket", True, "ExecuteAnalyticsQuery", "", "", "Argument[0]", "nosql-injection", "manual"]
+ - ["group:gocb1", "Cluster", True, "ExecuteN1qlQuery", "", "", "Argument[0]", "nosql-injection", "manual"]
+ - ["group:gocb1", "Cluster", True, "ExecuteAnalyticsQuery", "", "", "Argument[0]", "nosql-injection", "manual"]
+ - ["group:gocb2", "Cluster", True, "AnalyticsQuery", "", "", "Argument[0]", "nosql-injection", "manual"]
+ - ["group:gocb2", "Cluster", True, "Query", "", "", "Argument[0]", "nosql-injection", "manual"]
+ - ["group:gocb2", "Scope", True, "AnalyticsQuery", "", "", "Argument[0]", "nosql-injection", "manual"]
+ - ["group:gocb2", "Scope", True, "Query", "", "", "Argument[0]", "nosql-injection", "manual"]
- addsTo:
pack: codeql/go-all
extensible: summaryModel
data:
- - ["group:gocb", "", False, "NewAnalyticsQuery", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- - ["group:gocb", "", False, "NewN1qlQuery", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- - ["group:gocb", "AnalyticsQuery", True, "ContextId", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
- - ["group:gocb", "AnalyticsQuery", True, "Deferred", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
- - ["group:gocb", "AnalyticsQuery", True, "Pretty", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
- - ["group:gocb", "AnalyticsQuery", True, "Priority", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
- - ["group:gocb", "AnalyticsQuery", True, "RawParam", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
- - ["group:gocb", "AnalyticsQuery", True, "ServerSideTimeout", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
- - ["group:gocb", "N1qlQuery", True, "AdHoc", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
- - ["group:gocb", "N1qlQuery", True, "Consistency", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
- - ["group:gocb", "N1qlQuery", True, "ConsistentWith", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
- - ["group:gocb", "N1qlQuery", True, "Custom", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
- - ["group:gocb", "N1qlQuery", True, "PipelineBatch", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
- - ["group:gocb", "N1qlQuery", True, "PipelineCap", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
- - ["group:gocb", "N1qlQuery", True, "Profile", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
- - ["group:gocb", "N1qlQuery", True, "ReadOnly", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
- - ["group:gocb", "N1qlQuery", True, "ScanCap", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
- - ["group:gocb", "N1qlQuery", True, "Timeout", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
+ - ["group:gocb1", "", False, "NewAnalyticsQuery", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
+ - ["group:gocb1", "", False, "NewN1qlQuery", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
+ - ["group:gocb1", "AnalyticsQuery", True, "ContextId", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
+ - ["group:gocb1", "AnalyticsQuery", True, "Deferred", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
+ - ["group:gocb1", "AnalyticsQuery", True, "Pretty", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
+ - ["group:gocb1", "AnalyticsQuery", True, "Priority", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
+ - ["group:gocb1", "AnalyticsQuery", True, "RawParam", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
+ - ["group:gocb1", "AnalyticsQuery", True, "ServerSideTimeout", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
+ - ["group:gocb1", "N1qlQuery", True, "AdHoc", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
+ - ["group:gocb1", "N1qlQuery", True, "Consistency", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
+ - ["group:gocb1", "N1qlQuery", True, "ConsistentWith", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
+ - ["group:gocb1", "N1qlQuery", True, "Custom", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
+ - ["group:gocb1", "N1qlQuery", True, "PipelineBatch", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
+ - ["group:gocb1", "N1qlQuery", True, "PipelineCap", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
+ - ["group:gocb1", "N1qlQuery", True, "Profile", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
+ - ["group:gocb1", "N1qlQuery", True, "ReadOnly", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
+ - ["group:gocb1", "N1qlQuery", True, "ScanCap", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
+ - ["group:gocb1", "N1qlQuery", True, "Timeout", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
diff --git a/go/ql/lib/ext/github.com.davecgh.go-spew.spew.model.yml b/go/ql/lib/ext/github.com.davecgh.go-spew.spew.model.yml
new file mode 100644
index 000000000000..4b4996926e31
--- /dev/null
+++ b/go/ql/lib/ext/github.com.davecgh.go-spew.spew.model.yml
@@ -0,0 +1,14 @@
+extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["github.com/davecgh/go-spew/spew", "", False, "Dump", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["github.com/davecgh/go-spew/spew", "", False, "Errorf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["github.com/davecgh/go-spew/spew", "", False, "Fdump", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["github.com/davecgh/go-spew/spew", "", False, "Fprint", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["github.com/davecgh/go-spew/spew", "", False, "Fprintf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["github.com/davecgh/go-spew/spew", "", False, "Fprintln", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["github.com/davecgh/go-spew/spew", "", False, "Print", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["github.com/davecgh/go-spew/spew", "", False, "Printf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["github.com/davecgh/go-spew/spew", "", False, "Println", "", "", "Argument[0]", "log-injection", "manual"]
diff --git a/go/ql/lib/ext/github.com.elazarl.goproxy.model.yml b/go/ql/lib/ext/github.com.elazarl.goproxy.model.yml
index 20e4a26f1cdb..9dc8284ea58f 100644
--- a/go/ql/lib/ext/github.com.elazarl.goproxy.model.yml
+++ b/go/ql/lib/ext/github.com.elazarl.goproxy.model.yml
@@ -1,4 +1,10 @@
extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["github.com/elazarl/goproxy", "ProxyCtx", True, "Logf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["github.com/elazarl/goproxy", "ProxyCtx", True, "Warnf", "", "", "Argument[0..1]", "log-injection", "manual"]
- addsTo:
pack: codeql/go-all
extensible: summaryModel
diff --git a/go/ql/lib/ext/github.com.gogf.gf.database.gdb.model.yml b/go/ql/lib/ext/github.com.gogf.gf.database.gdb.model.yml
new file mode 100644
index 000000000000..030656c6eb8a
--- /dev/null
+++ b/go/ql/lib/ext/github.com.gogf.gf.database.gdb.model.yml
@@ -0,0 +1,57 @@
+extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ # These models are for v1. Some of them hold for v2, but we should model v2 properly.
+ - ["github.com/gogf/gf/database/gdb", "Core", True, "DoCommit", "", "", "Argument[2]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Core", True, "DoExec", "", "", "Argument[2]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Core", True, "DoGetAll", "", "", "Argument[2]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Core", True, "DoQuery", "", "", "Argument[2]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Core", True, "DoPrepare", "", "", "Argument[2]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Core", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Core", True, "GetAll", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Core", True, "GetArray", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Core", True, "GetCount", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Core", True, "GetOne", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Core", True, "GetScan", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Core", True, "GetStruct", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Core", True, "GetStructs", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Core", True, "GetValue", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Core", True, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Core", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Core", True, "Raw", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "DB", True, "DoCommit", "", "", "Argument[2]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "DB", True, "DoExec", "", "", "Argument[2]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "DB", True, "DoGetAll", "", "", "Argument[2]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "DB", True, "DoQuery", "", "", "Argument[2]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "DB", True, "DoPrepare", "", "", "Argument[2]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "DB", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "DB", True, "GetAll", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "DB", True, "GetArray", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "DB", True, "GetCount", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "DB", True, "GetOne", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "DB", True, "GetScan", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "DB", True, "GetStruct", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "DB", True, "GetStructs", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "DB", True, "GetValue", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "DB", True, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "DB", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "DB", True, "Raw", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Tx", True, "DoCommit", "", "", "Argument[2]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Tx", True, "DoExec", "", "", "Argument[2]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Tx", True, "DoGetAll", "", "", "Argument[2]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Tx", True, "DoQuery", "", "", "Argument[2]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Tx", True, "DoPrepare", "", "", "Argument[2]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Tx", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Tx", True, "GetAll", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Tx", True, "GetArray", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Tx", True, "GetCount", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Tx", True, "GetOne", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Tx", True, "GetScan", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Tx", True, "GetStruct", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Tx", True, "GetStructs", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Tx", True, "GetValue", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Tx", True, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Tx", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/gogf/gf/database/gdb", "Tx", True, "Raw", "", "", "Argument[0]", "sql-injection", "manual"]
diff --git a/go/ql/lib/ext/github.com.golang.glog.model.yml b/go/ql/lib/ext/github.com.golang.glog.model.yml
new file mode 100644
index 000000000000..275450f2c447
--- /dev/null
+++ b/go/ql/lib/ext/github.com.golang.glog.model.yml
@@ -0,0 +1,102 @@
+extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: packageGrouping
+ data:
+ - ["glog", "github.com/golang/glog"]
+ - ["glog", "gopkg.in/glog"]
+ - ["glog", "k8s.io/klog"]
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["group:glog", "", False, "Error", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "", False, "ErrorContext", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "", False, "ErrorContextDepth", "", "", "Argument[2]", "log-injection", "manual"]
+ - ["group:glog", "", False, "ErrorContextDepthf", "", "", "Argument[2..3]", "log-injection", "manual"]
+ - ["group:glog", "", False, "ErrorContextf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "", False, "ErrorDepth", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "", False, "ErrorDepthf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "", False, "Errorf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:glog", "", False, "Errorln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "", False, "Exit", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "", False, "ExitContext", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "", False, "ExitContextDepth", "", "", "Argument[2]", "log-injection", "manual"]
+ - ["group:glog", "", False, "ExitContextDepthf", "", "", "Argument[2..3]", "log-injection", "manual"]
+ - ["group:glog", "", False, "ExitContextf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "", False, "ExitDepth", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "", False, "ExitDepthf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "", False, "Exitf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:glog", "", False, "Exitln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "", False, "Fatal", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "", False, "FatalContext", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "", False, "FatalContextDepth", "", "", "Argument[2]", "log-injection", "manual"]
+ - ["group:glog", "", False, "FatalContextDepthf", "", "", "Argument[2..3]", "log-injection", "manual"]
+ - ["group:glog", "", False, "FatalContextf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "", False, "FatalDepth", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "", False, "FatalDepthf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "", False, "Fatalf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:glog", "", False, "Fatalln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "", False, "Info", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "", False, "InfoContext", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "", False, "InfoContextDepth", "", "", "Argument[2]", "log-injection", "manual"]
+ - ["group:glog", "", False, "InfoContextDepthf", "", "", "Argument[2..3]", "log-injection", "manual"]
+ - ["group:glog", "", False, "InfoContextf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "", False, "InfoDepth", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "", False, "InfoDepthf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "", False, "Infof", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:glog", "", False, "Infoln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "", False, "Warning", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "", False, "WarningContext", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "", False, "WarningContextDepth", "", "", "Argument[2]", "log-injection", "manual"]
+ - ["group:glog", "", False, "WarningContextDepthf", "", "", "Argument[2..3]", "log-injection", "manual"]
+ - ["group:glog", "", False, "WarningContextf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "", False, "WarningDepth", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "", False, "WarningDepthf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "", False, "Warningf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:glog", "", False, "Warningln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "Error", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "ErrorContext", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "ErrorContextDepth", "", "", "Argument[2]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "ErrorContextDepthf", "", "", "Argument[2..3]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "ErrorContextf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "ErrorDepth", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "ErrorDepthf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "Errorf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "Errorln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "Exit", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "ExitContext", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "ExitContextDepth", "", "", "Argument[2]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "ExitContextDepthf", "", "", "Argument[2..3]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "ExitContextf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "ExitDepth", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "ExitDepthf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "Exitf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "Exitln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "Fatal", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "FatalContext", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "FatalContextDepth", "", "", "Argument[2]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "FatalContextDepthf", "", "", "Argument[2..3]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "FatalContextf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "FatalDepth", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "FatalDepthf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "Fatalf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "Fatalln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "Info", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "InfoContext", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "InfoContextDepth", "", "", "Argument[2]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "InfoContextDepthf", "", "", "Argument[2..3]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "InfoContextf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "InfoDepth", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "InfoDepthf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "Infof", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "Infoln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "Warning", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "WarningContext", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "WarningContextDepth", "", "", "Argument[2]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "WarningContextDepthf", "", "", "Argument[2..3]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "WarningContextf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "WarningDepth", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "WarningDepthf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "Warningf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:glog", "Verbose", True, "Warningln", "", "", "Argument[0]", "log-injection", "manual"]
diff --git a/go/ql/lib/ext/github.com.jmoiron.sqlx.model.yml b/go/ql/lib/ext/github.com.jmoiron.sqlx.model.yml
new file mode 100644
index 000000000000..8c9d19b4b85b
--- /dev/null
+++ b/go/ql/lib/ext/github.com.jmoiron.sqlx.model.yml
@@ -0,0 +1,17 @@
+extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["github.com/jmoiron/sqlx", "DB", True, "Get", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/jmoiron/sqlx", "DB", True, "MustExec", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/jmoiron/sqlx", "DB", True, "NamedExec", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/jmoiron/sqlx", "DB", True, "NamedQuery", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/jmoiron/sqlx", "DB", True, "Queryx", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/jmoiron/sqlx", "DB", True, "Select", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/jmoiron/sqlx", "Tx", True, "Get", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/jmoiron/sqlx", "Tx", True, "MustExec", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/jmoiron/sqlx", "Tx", True, "NamedExec", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/jmoiron/sqlx", "Tx", True, "NamedQuery", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/jmoiron/sqlx", "Tx", True, "Queryx", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/jmoiron/sqlx", "Tx", True, "Select", "", "", "Argument[1]", "sql-injection", "manual"]
diff --git a/go/ql/lib/ext/github.com.mastermind.squirrel.model.yml b/go/ql/lib/ext/github.com.mastermind.squirrel.model.yml
new file mode 100644
index 000000000000..6f3c5830e457
--- /dev/null
+++ b/go/ql/lib/ext/github.com.mastermind.squirrel.model.yml
@@ -0,0 +1,51 @@
+extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: packageGrouping
+ data:
+ - ["squirrel", "github.com/Masterminds/squirrel"]
+ - ["squirrel", "gopkg.in/Masterminds/squirrel"]
+ - ["squirrel", "github.com/lann/squirrel"]
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["group:squirrel", "", False, "Delete", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "", False, "Expr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "", False, "Insert", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "", False, "Select", "", "", "Argument[0]", "sql-injection", "manual"] # TODO: when sources can have access paths, use .ArrayElement
+ - ["group:squirrel", "", False, "Update", "", "", "Argument[0]", "sql-injection", "manual"] # TODO: when sources can have access paths, use .ArrayElement
+
+ - ["group:squirrel", "DeleteBuilder", True, "From", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "DeleteBuilder", True, "OrderBy", "", "", "Argument[0]", "sql-injection", "manual"] # TODO: when sources can have access paths, use .ArrayElement
+ - ["group:squirrel", "DeleteBuilder", True, "Prefix", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "DeleteBuilder", True, "Suffix", "", "", "Argument[0]", "sql-injection", "manual"]
+ # DeleteBuilder.Where has to be modeled in QL to avoid FPs when a non-string argument is used
+
+ - ["group:squirrel", "InsertBuilder", True, "Columns", "", "", "Argument[0]", "sql-injection", "manual"] # TODO: when sources can have access paths, use .ArrayElement
+ - ["group:squirrel", "InsertBuilder", True, "Into", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "InsertBuilder", True, "Options", "", "", "Argument[0]", "sql-injection", "manual"] # TODO: when sources can have access paths, use .ArrayElement
+ - ["group:squirrel", "InsertBuilder", True, "Prefix", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "InsertBuilder", True, "Suffix", "", "", "Argument[0]", "sql-injection", "manual"]
+
+ - ["group:squirrel", "SelectBuilder", True, "CrossJoin", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "SelectBuilder", True, "Column", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "SelectBuilder", True, "Columns", "", "", "Argument[0]", "sql-injection", "manual"] # TODO: when sources can have access paths, use .ArrayElement
+ - ["group:squirrel", "SelectBuilder", True, "From", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "SelectBuilder", True, "GroupBy", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "SelectBuilder", True, "InnerJoin", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "SelectBuilder", True, "LeftJoin", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "SelectBuilder", True, "Options", "", "", "Argument[0]", "sql-injection", "manual"] # TODO: when sources can have access paths, use .ArrayElement
+ - ["group:squirrel", "SelectBuilder", True, "OrderBy", "", "", "Argument[0]", "sql-injection", "manual"] # TODO: when sources can have access paths, use .ArrayElement
+ - ["group:squirrel", "SelectBuilder", True, "Prefix", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "SelectBuilder", True, "RightJoin", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "SelectBuilder", True, "Suffix", "", "", "Argument[0]", "sql-injection", "manual"]
+ # SelectBuilder.Where has to be modeled in QL to avoid FPs when a non-string argument is used
+
+ - ["group:squirrel", "UpdateBuilder", True, "From", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "UpdateBuilder", True, "OrderBy", "", "", "Argument[0]", "sql-injection", "manual"] # TODO: when sources can have access paths, use .ArrayElement
+ - ["group:squirrel", "UpdateBuilder", True, "Prefix", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "UpdateBuilder", True, "Set", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "UpdateBuilder", True, "Suffix", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:squirrel", "UpdateBuilder", True, "Table", "", "", "Argument[0]", "sql-injection", "manual"]
+ # UpdateBuilder.Where has to be modeled in QL to avoid FPs when a non-string argument is used
diff --git a/go/ql/lib/ext/github.com.rqlite.gorqlite.model.yml b/go/ql/lib/ext/github.com.rqlite.gorqlite.model.yml
new file mode 100644
index 000000000000..62e24f2c920b
--- /dev/null
+++ b/go/ql/lib/ext/github.com.rqlite.gorqlite.model.yml
@@ -0,0 +1,35 @@
+extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: packageGrouping
+ data:
+ - ["gorqlite", "github.com/rqlite/gorqlite"]
+ - ["gorqlite", "github.com/raindog308/gorqlite"]
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["group:gorqlite", "Connection", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "QueryContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "QueryOne", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "QueryOneContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "QueryOneParameterized", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "QueryOneParameterizedContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "QueryParameterized", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "QueryParameterizedContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "Queue", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "QueueContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "QueueOne", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "QueueOneContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "QueueOneParameterized", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "QueueOneParameterizedContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "QueueParameterized", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "QueueParameterizedContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "Write", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "WriteContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "WriteOne", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "WriteOneContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "WriteOneParameterized", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "WriteOneParameterizedContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "WriteParameterized", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorqlite", "Connection", True, "WriteParameterizedContext", "", "", "Argument[1]", "sql-injection", "manual"]
diff --git a/go/ql/lib/ext/github.com.sirupsen.logrus.model.yml b/go/ql/lib/ext/github.com.sirupsen.logrus.model.yml
new file mode 100644
index 000000000000..06f9a54622b4
--- /dev/null
+++ b/go/ql/lib/ext/github.com.sirupsen.logrus.model.yml
@@ -0,0 +1,159 @@
+extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: packageGrouping
+ data:
+ - ["logrus", "github.com/sirupsen/logrus"]
+ - ["logrus", "github.com/Sirupsen/logrus"]
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["group:logrus", "", False, "Debug", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "DebugFn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Debugf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Debugln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Error", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "ErrorFn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Errorf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Errorln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Fatal", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "FatalFn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Fatalf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Fatalln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Info", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "InfoFn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Infof", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Infoln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Panic", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "PanicFn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Panicf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Panicln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Print", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "PrintFn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Printf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Println", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Trace", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "TraceFn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Tracef", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Traceln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Warn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "WarnFn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Warnf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Warnln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Warning", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "WarningFn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Warningf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "Warningln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "WithError", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "WithField", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "WithFields", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "", False, "WithTime", "", "", "Argument[0]", "log-injection", "manual"]
+
+ - ["group:logrus", "Entry", True, "Debug", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Debugf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Debugln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Error", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Errorf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Errorln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Fatal", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Fatalf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Fatalln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Info", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Infof", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Infoln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Log", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Logf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Logln", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Panic", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Panicf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Panicln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Print", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Printf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Println", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Trace", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Tracef", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Traceln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Warn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Warnf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Warnln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Warning", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Warningf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "Warningln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "WithError", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "WithField", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "WithFields", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Entry", True, "WithTime", "", "", "Argument[0]", "log-injection", "manual"]
+
+ - ["group:logrus", "FieldLogger", True, "Debug", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Debugf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Debugln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Error", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Errorf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Errorln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Fatal", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Fatalf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Fatalln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Info", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Infof", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Infoln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Panic", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Panicf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Panicln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Print", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Printf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Println", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Warn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Warnf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Warnln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Warning", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Warningf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "Warningln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "WithError", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "WithField", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "FieldLogger", True, "WithFields", "", "", "Argument[0]", "log-injection", "manual"]
+
+ - ["group:logrus", "Logger", True, "Debug", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "DebugFn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Debugf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Debugln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Error", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "ErrorFn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Errorf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Errorln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Fatal", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "FatalFn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Fatalf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Fatalln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Info", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "InfoFn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Infof", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Infoln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Log", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "LogFn", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Logf", "", "", "Argument[1..2]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Logln", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Panic", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "PanicFn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Panicf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Panicln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Print", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "PrintFn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Printf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Println", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Trace", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "TraceFn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Tracef", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Traceln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Warn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "WarnFn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Warnf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Warnln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Warning", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "WarningFn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Warningf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "Warningln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "WithError", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "WithField", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "WithFields", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["group:logrus", "Logger", True, "WithTime", "", "", "Argument[0]", "log-injection", "manual"]
diff --git a/go/ql/lib/ext/github.com.uptrace.bun.model.yml b/go/ql/lib/ext/github.com.uptrace.bun.model.yml
new file mode 100644
index 000000000000..a08adb07973d
--- /dev/null
+++ b/go/ql/lib/ext/github.com.uptrace.bun.model.yml
@@ -0,0 +1,68 @@
+extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["github.com/uptrace/bun", "", False, "NewRawQuery", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "AddColumnQuery", True, "ColumnExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "AddColumnQuery", True, "ModelTableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "AddColumnQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "Conn", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "Conn", True, "ExecContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "Conn", True, "NewRaw", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "Conn", True, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "Conn", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "Conn", True, "QueryRow", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "Conn", True, "PrepareContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "Conn", True, "QueryContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "Conn", True, "QueryRowContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "Conn", True, "Raw", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "CreateIndexQuery", True, "ColumnExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "CreateIndexQuery", True, "ModelTableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "CreateIndexQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "CreateIndexQuery", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "CreateIndexQuery", True, "WhereOr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "CreateTableQuery", True, "ColumnExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "CreateTableQuery", True, "ModelTableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "CreateTableQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "DB", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "DB", True, "ExecContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "DB", True, "NewRaw", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "DB", True, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "DB", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "DB", True, "QueryRow", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "DB", True, "PrepareContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "DB", True, "QueryContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "DB", True, "QueryRowContext", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "DB", True, "Raw", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "DeleteQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "DeleteQuery", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "DeleteQuery", True, "WhereOr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "DropColumnQuery", True, "ColumnExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "DropColumnQuery", True, "ModelTableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "DropColumnQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "DropTableQuery", True, "ModelTableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "DropTableQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "InsertQuery", True, "ColumnExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "InsertQuery", True, "ModelTableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "InsertQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "InsertQuery", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "InsertQuery", True, "WhereOr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "MergeQuery", True, "ModelTableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "MergeQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "RawQuery", True, "NewRaw", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "SelectQuery", True, "ColumnExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "SelectQuery", True, "DistinctOn", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "SelectQuery", True, "For", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "SelectQuery", True, "GroupExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "SelectQuery", True, "Having", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "SelectQuery", True, "ModelTableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "SelectQuery", True, "OrderExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "SelectQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "SelectQuery", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "SelectQuery", True, "WhereOr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "TruncateTableQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "UpdateQuery", True, "ModelTableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "UpdateQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "UpdateQuery", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["github.com/uptrace/bun", "UpdateQuery", True, "WhereOr", "", "", "Argument[0]", "sql-injection", "manual"]
diff --git a/go/ql/lib/ext/go.mongodb.org.mongo-driver.mongo.model.yml b/go/ql/lib/ext/go.mongodb.org.mongo-driver.mongo.model.yml
new file mode 100644
index 000000000000..6c2d4afdeae2
--- /dev/null
+++ b/go/ql/lib/ext/go.mongodb.org.mongo-driver.mongo.model.yml
@@ -0,0 +1,19 @@
+extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "Aggregate", "", "", "Argument[1]", "nosql-injection", "manual"]
+ - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "CountDocuments", "", "", "Argument[1]", "nosql-injection", "manual"]
+ - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "DeleteMany", "", "", "Argument[1]", "nosql-injection", "manual"]
+ - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "DeleteOne", "", "", "Argument[1]", "nosql-injection", "manual"]
+ - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "Distinct", "", "", "Argument[2]", "nosql-injection", "manual"]
+ - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "Find", "", "", "Argument[1]", "nosql-injection", "manual"]
+ - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "FindOne", "", "", "Argument[1]", "nosql-injection", "manual"]
+ - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "FindOneAndDelete", "", "", "Argument[1]", "nosql-injection", "manual"]
+ - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "FindOneAndReplace", "", "", "Argument[1]", "nosql-injection", "manual"]
+ - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "FindOneAndUpdate", "", "", "Argument[1]", "nosql-injection", "manual"]
+ - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "ReplaceOne", "", "", "Argument[1]", "nosql-injection", "manual"]
+ - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "UpdateMany", "", "", "Argument[1]", "nosql-injection", "manual"]
+ - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "UpdateOne", "", "", "Argument[1]", "nosql-injection", "manual"]
+ - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "Watch", "", "", "Argument[1]", "nosql-injection", "manual"]
diff --git a/go/ql/lib/ext/go.uber.org.zap.model.yml b/go/ql/lib/ext/go.uber.org.zap.model.yml
index 2ca7f7e8a804..e9fc971e5fa2 100644
--- a/go/ql/lib/ext/go.uber.org.zap.model.yml
+++ b/go/ql/lib/ext/go.uber.org.zap.model.yml
@@ -1,4 +1,41 @@
extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["go.uber.org/zap", "Logger", True, "DPanic", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "Logger", True, "Debug", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "Logger", True, "Error", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "Logger", True, "Fatal", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "Logger", True, "Info", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "Logger", True, "Named", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "Logger", True, "Panic", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "Logger", True, "Warn", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "Logger", True, "With", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "Logger", True, "WithOptions", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "DPanic", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "DPanicf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "DPanicw", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Debug", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Debugf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Debugw", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Error", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Errorf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Errorw", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Fatal", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Fatalf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Fatalw", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Info", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Infof", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Infow", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Named", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Panic", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Panicf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Panicw", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Warn", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Warnf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "Warnw", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["go.uber.org/zap", "SugaredLogger", True, "With", "", "", "Argument[0]", "log-injection", "manual"]
- addsTo:
pack: codeql/go-all
extensible: summaryModel
diff --git a/go/ql/lib/ext/gorm.io.gorm.model.yml b/go/ql/lib/ext/gorm.io.gorm.model.yml
new file mode 100644
index 000000000000..bfcf1fa66a76
--- /dev/null
+++ b/go/ql/lib/ext/gorm.io.gorm.model.yml
@@ -0,0 +1,25 @@
+extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: packageGrouping
+ data:
+ - ["gorm", "gorm.io/gorm"]
+ - ["gorm", "github.com/jinzhu/gorm"]
+ - ["gorm", "github.com/go-gorm/gorm"]
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["group:gorm", "DB", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorm", "DB", True, "Raw", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorm", "DB", True, "Order", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorm", "DB", True, "Not", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorm", "DB", True, "Or", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorm", "DB", True, "Select", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorm", "DB", True, "Table", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorm", "DB", True, "Group", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorm", "DB", True, "Having", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorm", "DB", True, "Joins", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorm", "DB", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorm", "DB", True, "Distinct", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:gorm", "DB", True, "Pluck", "", "", "Argument[0]", "sql-injection", "manual"]
diff --git a/go/ql/lib/ext/log.model.yml b/go/ql/lib/ext/log.model.yml
index 7f52a1733073..4d1df7cf0821 100644
--- a/go/ql/lib/ext/log.model.yml
+++ b/go/ql/lib/ext/log.model.yml
@@ -1,4 +1,28 @@
extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["log", "", False, "Fatal", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["log", "", False, "Fatalf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["log", "", False, "Fatalln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["log", "", False, "Output", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["log", "", False, "Panic", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["log", "", False, "Panicf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["log", "", False, "Panicln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["log", "", False, "Print", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["log", "", False, "Printf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["log", "", False, "Println", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["log", "Logger", True, "Fatal", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["log", "Logger", True, "Fatalf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["log", "Logger", True, "Fatalln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["log", "Logger", True, "Output", "", "", "Argument[1]", "log-injection", "manual"]
+ - ["log", "Logger", True, "Panic", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["log", "Logger", True, "Panicf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["log", "Logger", True, "Panicln", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["log", "Logger", True, "Print", "", "", "Argument[0]", "log-injection", "manual"]
+ - ["log", "Logger", True, "Printf", "", "", "Argument[0..1]", "log-injection", "manual"]
+ - ["log", "Logger", True, "Println", "", "", "Argument[0]", "log-injection", "manual"]
- addsTo:
pack: codeql/go-all
extensible: summaryModel
diff --git a/go/ql/lib/ext/os.model.yml b/go/ql/lib/ext/os.model.yml
index 3d87eefe43f7..2c1c64db93ac 100644
--- a/go/ql/lib/ext/os.model.yml
+++ b/go/ql/lib/ext/os.model.yml
@@ -53,6 +53,7 @@ extensions:
- ["os", "", False, "Open", "", "", "ReturnValue[0]", "file", "manual"]
- ["os", "", False, "OpenFile", "", "", "ReturnValue[0]", "file", "manual"]
- ["os", "", False, "ReadFile", "", "", "ReturnValue[0]", "file", "manual"]
+ - ["os", "", False, "Stdin", "", "", "", "stdin", "manual"]
- ["os", "", False, "UserCacheDir", "", "", "ReturnValue[0]", "environment", "manual"]
- ["os", "", False, "UserConfigDir", "", "", "ReturnValue[0]", "environment", "manual"]
- ["os", "", False, "UserHomeDir", "", "", "ReturnValue[0]", "environment", "manual"]
diff --git a/go/ql/lib/ext/slices.model.yml b/go/ql/lib/ext/slices.model.yml
new file mode 100644
index 000000000000..2023aeb3db23
--- /dev/null
+++ b/go/ql/lib/ext/slices.model.yml
@@ -0,0 +1,31 @@
+extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: summaryModel
+ data:
+ # All should be modeled when we have a way to model iterators
+ # AppendSec should be modeled when we have a way to model iterators
+ # Backward should be modeled when we have a way to model iterators
+ # Chunk should be modeled when we have a way to model iterators
+ - ["slices", "", False, "Clip", "", "", "Argument[0].ArrayElement", "ReturnValue.ArrayElement", "value", "manual"]
+ - ["slices", "", False, "Clone", "", "", "Argument[0].ArrayElement", "ReturnValue.ArrayElement", "value", "manual"]
+ # Collect should be modeled when we have a way to model iterators
+ - ["slices", "", False, "Compact", "", "", "Argument[0].ArrayElement", "ReturnValue.ArrayElement", "value", "manual"]
+ - ["slices", "", False, "CompactFunc", "", "", "Argument[0].ArrayElement", "ReturnValue.ArrayElement", "value", "manual"]
+ - ["slices", "", False, "Concat", "", "", "Argument[0].ArrayElement.ArrayElement", "ReturnValue.ArrayElement", "value", "manual"]
+ - ["slices", "", False, "Delete", "", "", "Argument[0].ArrayElement", "ReturnValue.ArrayElement", "value", "manual"]
+ - ["slices", "", False, "DeleteFunc", "", "", "Argument[0].ArrayElement", "ReturnValue.ArrayElement", "value", "manual"]
+ - ["slices", "", False, "Grow", "", "", "Argument[0].ArrayElement", "ReturnValue.ArrayElement", "value", "manual"]
+ - ["slices", "", False, "Insert", "", "", "Argument[0].ArrayElement", "ReturnValue.ArrayElement", "value", "manual"]
+ - ["slices", "", False, "Insert", "", "", "Argument[2].ArrayElement", "ReturnValue.ArrayElement", "value", "manual"]
+ - ["slices", "", False, "Max", "", "", "Argument[0].ArrayElement", "ReturnValue", "value", "manual"]
+ - ["slices", "", False, "MaxFunc", "", "", "Argument[0].ArrayElement", "ReturnValue", "value", "manual"]
+ - ["slices", "", False, "Min", "", "", "Argument[0].ArrayElement", "ReturnValue", "value", "manual"]
+ - ["slices", "", False, "MinFunc", "", "", "Argument[0].ArrayElement", "ReturnValue", "value", "manual"]
+ - ["slices", "", False, "Repeat", "", "", "Argument[0].ArrayElement", "ReturnValue.ArrayElement", "value", "manual"]
+ - ["slices", "", False, "Replace", "", "", "Argument[0].ArrayElement", "ReturnValue.ArrayElement", "value", "manual"]
+ - ["slices", "", False, "Replace", "", "", "Argument[3].ArrayElement", "ReturnValue.ArrayElement", "value", "manual"]
+ # Sorted should be modeled when we have a way to model iterators
+ # SortedFunc should be modeled when we have a way to model iterators
+ # SortedStableFunc should be modeled when we have a way to model iterators
+ # Values should be modeled when we have a way to model iterators
diff --git a/go/ql/lib/ext/xorm.io.xorm.model.yml b/go/ql/lib/ext/xorm.io.xorm.model.yml
new file mode 100644
index 000000000000..5cf1ac4f5d73
--- /dev/null
+++ b/go/ql/lib/ext/xorm.io.xorm.model.yml
@@ -0,0 +1,49 @@
+extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: packageGrouping
+ data:
+ - ["xorm", "xorm.io/xorm"]
+ - ["xorm", "github.com/go-xorm/xorm"]
+ - addsTo:
+ pack: codeql/go-all
+ extensible: sinkModel
+ data:
+ - ["group:xorm", "Engine", True, "Alias", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Engine", True, "And", "", "", "Argument[0]", "sql-injection", "manual"]
+ # Engine.Exec has to be modeled in QL to select only the first syntactic argument
+ - ["group:xorm", "Engine", True, "GroupBy", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Engine", True, "Having", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Engine", True, "In", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Engine", True, "Join", "", "", "Argument[0..2]", "sql-injection", "manual"]
+ - ["group:xorm", "Engine", True, "NotIn", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Engine", True, "Or", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Engine", True, "OrderBy", "", "", "Argument[0]", "sql-injection", "manual"]
+ # Engine.Query, Engine.QueryInterface and Engine.QueryString have to be modeled in QL to select only the first syntactic argument
+ - ["group:xorm", "Engine", True, "Select", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Engine", True, "SetExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Engine", True, "SQL", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Engine", True, "Sum", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:xorm", "Engine", True, "Sums", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:xorm", "Engine", True, "SumInt", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:xorm", "Engine", True, "SumsInt", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:xorm", "Engine", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Session", True, "Alias", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Session", True, "And", "", "", "Argument[0]", "sql-injection", "manual"]
+ # Session.Exec has to be modeled in QL to select only the first syntactic argument
+ - ["group:xorm", "Session", True, "GroupBy", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Session", True, "Having", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Session", True, "In", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Session", True, "Join", "", "", "Argument[0..2]", "sql-injection", "manual"]
+ - ["group:xorm", "Session", True, "NotIn", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Session", True, "Or", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Session", True, "OrderBy", "", "", "Argument[0]", "sql-injection", "manual"]
+ # Session.Query, Session.QueryInterface and Session.QueryString have to be modeled in QL to select only the first syntactic argument
+ - ["group:xorm", "Session", True, "Select", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Session", True, "SetExpr", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Session", True, "SQL", "", "", "Argument[0]", "sql-injection", "manual"]
+ - ["group:xorm", "Session", True, "Sum", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:xorm", "Session", True, "Sums", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:xorm", "Session", True, "SumInt", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:xorm", "Session", True, "SumsInt", "", "", "Argument[1]", "sql-injection", "manual"]
+ - ["group:xorm", "Session", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"]
diff --git a/go/ql/lib/semmle/go/Concepts.qll b/go/ql/lib/semmle/go/Concepts.qll
index c15d3683b40c..3f0cd0f8885e 100644
--- a/go/ql/lib/semmle/go/Concepts.qll
+++ b/go/ql/lib/semmle/go/Concepts.qll
@@ -373,6 +373,48 @@ module LoggerCall {
}
}
+private class DefaultLoggerCall extends LoggerCall::Range, DataFlow::CallNode {
+ DataFlow::ArgumentNode messageComponent;
+
+ DefaultLoggerCall() {
+ sinkNode(messageComponent, "log-injection") and
+ this = messageComponent.getCall()
+ }
+
+ override DataFlow::Node getAMessageComponent() {
+ not messageComponent instanceof DataFlow::ImplicitVarargsSlice and
+ result = messageComponent
+ or
+ messageComponent instanceof DataFlow::ImplicitVarargsSlice and
+ result = this.getAnImplicitVarargsArgument()
+ }
+}
+
+/**
+ * A call to an interface that looks like a logger. It is common to use a
+ * locally-defined interface for logging to make it easy to changing logging
+ * library.
+ */
+private class HeuristicLoggerCall extends LoggerCall::Range, DataFlow::CallNode {
+ HeuristicLoggerCall() {
+ exists(Method m, string tp, string logFunctionPrefix, string name |
+ m = this.getTarget() and
+ m.hasQualifiedName(_, tp, name) and
+ m.getReceiverBaseType().getUnderlyingType() instanceof InterfaceType
+ |
+ tp.regexpMatch(".*[lL]ogger") and
+ logFunctionPrefix =
+ [
+ "Debug", "Error", "Fatal", "Info", "Log", "Output", "Panic", "Print", "Trace", "Warn",
+ "With"
+ ] and
+ name.matches(logFunctionPrefix + "%")
+ )
+ }
+
+ override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
+}
+
/**
* A function that encodes data into a binary or textual format.
*
diff --git a/go/ql/lib/semmle/go/Scopes.qll b/go/ql/lib/semmle/go/Scopes.qll
index f9b9e3a26b9a..c3671c74b5cd 100644
--- a/go/ql/lib/semmle/go/Scopes.qll
+++ b/go/ql/lib/semmle/go/Scopes.qll
@@ -472,7 +472,7 @@ class Function extends ValueEntity, @functionobject {
/** Gets a parameter of this function. */
Parameter getAParameter() { result = this.getParameter(_) }
- /** Gets the `i`th reslt variable of this function. */
+ /** Gets the `i`th result variable of this function. */
ResultVariable getResult(int i) { result.isResultOf(this.getFuncDecl(), i) }
/** Gets a result variable of this function. */
diff --git a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll
index a59c7294e802..5ae7b6a7f0d8 100644
--- a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll
+++ b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll
@@ -38,7 +38,8 @@
* first 6 columns, and the `output` column specifies how data leaves the
* element selected by the first 6 columns. An `input` can be either "",
* "Argument[n]", or "Argument[n1..n2]":
- * - "": Selects a write to the selected element in case this is a field.
+ * - "": Selects a write to the selected element in case this is a field or
+ * package-level variable.
* - "Argument[n]": Selects an argument in a call to the selected element.
* The arguments are zero-indexed, and `receiver` specifies the receiver.
* - "Argument[n1..n2]": Similar to "Argument[n]" but selects any argument
@@ -47,7 +48,7 @@
* An `output` can be either "", "Argument[n]", "Argument[n1..n2]", "Parameter",
* "Parameter[n]", "Parameter[n1..n2]", , "ReturnValue", "ReturnValue[n]", or
* "ReturnValue[n1..n2]":
- * - "": Selects a read of a selected field.
+ * - "": Selects a read of a selected field or package-level variable.
* - "Argument[n]": Selects the post-update value of an argument in a call to the
* selected element. That is, the value of the argument after the call returns.
* The arguments are zero-indexed, and `receiver` specifies the receiver.
diff --git a/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll b/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll
index b82039a32fe3..40c68ceb900a 100644
--- a/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll
+++ b/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll
@@ -399,6 +399,13 @@ module SourceSinkInterpretationInput implements
c = "" and
pragma[only_bind_into](e) = getElementWithQualifier(frn.getField(), frn.getBase())
)
+ or
+ // A package-scope (or universe-scope) variable
+ exists(Variable v | not v instanceof Field |
+ c = "" and
+ n.(DataFlow::ReadNode).reads(v) and
+ pragma[only_bind_into](e).asEntity() = v
+ )
)
}
@@ -420,6 +427,17 @@ module SourceSinkInterpretationInput implements
fw.writesField(base, f, node.asNode()) and
pragma[only_bind_into](e) = getElementWithQualifier(f, base)
)
+ or
+ // A package-scope (or universe-scope) variable
+ exists(Node n, SourceOrSinkElement e, DataFlow::Write w, Variable v |
+ n = node.asNode() and
+ e = mid.asElement() and
+ not v instanceof Field
+ |
+ c = "" and
+ w.writes(v, n) and
+ pragma[only_bind_into](e).asEntity() = v
+ )
}
}
diff --git a/go/ql/lib/semmle/go/frameworks/Beego.qll b/go/ql/lib/semmle/go/frameworks/Beego.qll
index 9f6ee5980035..a9e296a1f973 100644
--- a/go/ql/lib/semmle/go/frameworks/Beego.qll
+++ b/go/ql/lib/semmle/go/frameworks/Beego.qll
@@ -33,13 +33,6 @@ module Beego {
result = package(v2modulePath(), "server/web/context")
}
- /** Gets the path for the logs package of beego. */
- string logsPackagePath() {
- result = package(v1modulePath(), "logs")
- or
- result = package(v2modulePath(), "core/logs")
- }
-
/** Gets the path for the utils package of beego. */
string utilsPackagePath() {
result = package(v1modulePath(), "utils")
@@ -172,36 +165,6 @@ module Beego {
override string getAContentType() { none() }
}
- private string getALogFunctionName() {
- result =
- [
- "Alert", "Critical", "Debug", "Emergency", "Error", "Info", "Informational", "Notice",
- "Trace", "Warn", "Warning"
- ]
- }
-
- private class ToplevelBeegoLoggers extends LoggerCall::Range, DataFlow::CallNode {
- ToplevelBeegoLoggers() {
- this.getTarget().hasQualifiedName([packagePath(), logsPackagePath()], getALogFunctionName())
- }
-
- override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
- }
-
- private class BeegoLoggerMethods extends LoggerCall::Range, DataFlow::MethodCallNode {
- BeegoLoggerMethods() {
- this.getTarget().hasQualifiedName(logsPackagePath(), "BeeLogger", getALogFunctionName())
- }
-
- override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
- }
-
- private class UtilLoggers extends LoggerCall::Range, DataFlow::CallNode {
- UtilLoggers() { this.getTarget().hasQualifiedName(utilsPackagePath(), "Display") }
-
- override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
- }
-
private class HtmlQuoteSanitizer extends SharedXss::Sanitizer {
HtmlQuoteSanitizer() {
exists(DataFlow::CallNode c | c.getTarget().hasQualifiedName(packagePath(), "Htmlquote") |
diff --git a/go/ql/lib/semmle/go/frameworks/BeegoOrm.qll b/go/ql/lib/semmle/go/frameworks/BeegoOrm.qll
index c1de0cf42442..925b0f19fa3f 100644
--- a/go/ql/lib/semmle/go/frameworks/BeegoOrm.qll
+++ b/go/ql/lib/semmle/go/frameworks/BeegoOrm.qll
@@ -14,57 +14,6 @@ module BeegoOrm {
/** Gets the package name `github.com/astaxie/beego/orm`. */
string packagePath() { result = package("github.com/astaxie/beego", "orm") }
- private class DbSink extends SQL::QueryString::Range {
- DbSink() {
- exists(Method m, string methodName, int argNum |
- m.hasQualifiedName(packagePath(), "DB", methodName) and
- (
- methodName = ["Exec", "Prepare", "Query", "QueryRow"] and
- argNum = 0
- or
- methodName = ["ExecContext", "PrepareContext", "QueryContext", "QueryRowContext"] and
- argNum = 1
- )
- |
- this = m.getACall().getArgument(argNum)
- )
- }
- }
-
- private class QueryBuilderSink extends SQL::QueryString::Range {
- // Note this class doesn't do any escaping, unlike the true ORM part of the package
- QueryBuilderSink() {
- exists(Method impl | impl.implements(packagePath(), "QueryBuilder", _) |
- this = impl.getACall().getASyntacticArgument()
- ) and
- this.getType().getUnderlyingType() instanceof StringType
- }
- }
-
- private class OrmerRawSink extends SQL::QueryString::Range {
- OrmerRawSink() {
- exists(Method impl | impl.implements(packagePath(), "Ormer", "Raw") |
- this = impl.getACall().getArgument(0)
- )
- }
- }
-
- private class QuerySeterFilterRawSink extends SQL::QueryString::Range {
- QuerySeterFilterRawSink() {
- exists(Method impl | impl.implements(packagePath(), "QuerySeter", "FilterRaw") |
- this = impl.getACall().getArgument(1)
- )
- }
- }
-
- private class ConditionRawSink extends SQL::QueryString::Range {
- ConditionRawSink() {
- exists(Method impl | impl.implements(packagePath(), "Condition", "Raw") |
- this = impl.getACall().getArgument(1)
- )
- }
- }
-
private class OrmerSource extends StoredXss::Source {
OrmerSource() {
exists(Method impl |
diff --git a/go/ql/lib/semmle/go/frameworks/Couchbase.qll b/go/ql/lib/semmle/go/frameworks/Couchbase.qll
index 5eaa4d20c3ad..b5bfbcb22a2d 100644
--- a/go/ql/lib/semmle/go/frameworks/Couchbase.qll
+++ b/go/ql/lib/semmle/go/frameworks/Couchbase.qll
@@ -5,57 +5,23 @@
import go
/**
+ * DEPRECATED
+ *
* Provides models of commonly used functions in the official Couchbase Go SDK library.
*/
-module Couchbase {
+deprecated module Couchbase {
/**
+ * DEPRECATED
+ *
* Gets a package path for the official Couchbase Go SDK library.
*
* Note that v1 and v2 have different APIs, but the names are disjoint so there is no need to
* distinguish between them.
*/
- string packagePath() {
+ deprecated string packagePath() {
result =
package([
"gopkg.in/couchbase/gocb", "github.com/couchbase/gocb", "github.com/couchbaselabs/gocb"
], "")
}
-
- /**
- * A query used in an API function acting on a `Bucket` or `Cluster` struct of v1 of
- * the official Couchbase Go library, gocb.
- */
- private class CouchbaseV1Query extends NoSql::Query::Range {
- CouchbaseV1Query() {
- // func (b *Bucket) ExecuteAnalyticsQuery(q *AnalyticsQuery, params interface{}) (AnalyticsResults, error)
- // func (b *Bucket) ExecuteN1qlQuery(q *N1qlQuery, params interface{}) (QueryResults, error)
- // func (c *Cluster) ExecuteAnalyticsQuery(q *AnalyticsQuery, params interface{}) (AnalyticsResults, error)
- // func (c *Cluster) ExecuteN1qlQuery(q *N1qlQuery, params interface{}) (QueryResults, error)
- exists(Method meth, string structName, string methodName |
- structName in ["Bucket", "Cluster"] and
- methodName in ["ExecuteN1qlQuery", "ExecuteAnalyticsQuery"] and
- meth.hasQualifiedName(packagePath(), structName, methodName) and
- this = meth.getACall().getArgument(0)
- )
- }
- }
-
- /**
- * A query used in an API function acting on a `Bucket` or `Cluster` struct of v1 of
- * the official Couchbase Go library, gocb.
- */
- private class CouchbaseV2Query extends NoSql::Query::Range {
- CouchbaseV2Query() {
- // func (c *Cluster) AnalyticsQuery(statement string, opts *AnalyticsOptions) (*AnalyticsResult, error)
- // func (c *Cluster) Query(statement string, opts *QueryOptions) (*QueryResult, error)
- // func (s *Scope) AnalyticsQuery(statement string, opts *AnalyticsOptions) (*AnalyticsResult, error)
- // func (s *Scope) Query(statement string, opts *QueryOptions) (*QueryResult, error)
- exists(Method meth, string structName, string methodName |
- structName in ["Cluster", "Scope"] and
- methodName in ["AnalyticsQuery", "Query"] and
- meth.hasQualifiedName(packagePath(), structName, methodName) and
- this = meth.getACall().getArgument(0)
- )
- }
- }
}
diff --git a/go/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll b/go/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll
index 4d10c8af312d..b1bf4571216a 100644
--- a/go/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll
+++ b/go/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll
@@ -100,10 +100,4 @@ module ElazarlGoproxy {
override int getFormatStringIndex() { result = 0 }
}
-
- private class ProxyLog extends LoggerCall::Range, DataFlow::MethodCallNode {
- ProxyLog() { this.getTarget() instanceof ProxyLogFunction }
-
- override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
- }
}
diff --git a/go/ql/lib/semmle/go/frameworks/Glog.qll b/go/ql/lib/semmle/go/frameworks/Glog.qll
index f9f5c9e3f11f..146b8a4f814b 100644
--- a/go/ql/lib/semmle/go/frameworks/Glog.qll
+++ b/go/ql/lib/semmle/go/frameworks/Glog.qll
@@ -40,14 +40,4 @@ module Glog {
override int getFormatStringIndex() { result = super.getFirstPrintedArg() }
}
-
- private class GlogCall extends LoggerCall::Range, DataFlow::CallNode {
- GlogFunction callee;
-
- GlogCall() { this = callee.getACall() }
-
- override DataFlow::Node getAMessageComponent() {
- result = this.getSyntacticArgument(any(int i | i >= callee.getFirstPrintedArg()))
- }
- }
}
diff --git a/go/ql/lib/semmle/go/frameworks/Logrus.qll b/go/ql/lib/semmle/go/frameworks/Logrus.qll
index f7de9a75dae8..83278a4cd9e2 100644
--- a/go/ql/lib/semmle/go/frameworks/Logrus.qll
+++ b/go/ql/lib/semmle/go/frameworks/Logrus.qll
@@ -28,12 +28,6 @@ module Logrus {
}
}
- private class LogCall extends LoggerCall::Range, DataFlow::CallNode {
- LogCall() { this = any(LogFunction f).getACall() }
-
- override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
- }
-
private class StringFormatters extends StringOps::Formatting::Range instanceof LogFunction {
int argOffset;
diff --git a/go/ql/lib/semmle/go/frameworks/NoSQL.qll b/go/ql/lib/semmle/go/frameworks/NoSQL.qll
index c2469fc02ac1..36932149628e 100644
--- a/go/ql/lib/semmle/go/frameworks/NoSQL.qll
+++ b/go/ql/lib/semmle/go/frameworks/NoSQL.qll
@@ -31,84 +31,6 @@ module NoSql {
)
}
}
-
- /**
- * Holds if method `name` of struct `Collection` from package
- * [go.mongodb.org/mongo-driver/mongo](https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo)
- * interprets parameter `n` as a query.
- */
- private predicate mongoDbCollectionMethod(string name, int n) {
- // func (coll *Collection) CountDocuments(ctx context.Context, filter interface{},
- // opts ...*options.CountOptions) (int64, error)
- name = "CountDocuments" and n = 1
- or
- // func (coll *Collection) DeleteMany(ctx context.Context, filter interface{},
- // opts ...*options.DeleteOptions) (*DeleteResult, error)
- name = "DeleteMany" and n = 1
- or
- // func (coll *Collection) DeleteOne(ctx context.Context, filter interface{},
- // opts ...*options.DeleteOptions) (*DeleteResult, error)
- name = "DeleteOne" and n = 1
- or
- // func (coll *Collection) Distinct(ctx context.Context, fieldName string, filter interface{},
- // ...) ([]interface{}, error)
- name = "Distinct" and n = 2
- or
- // func (coll *Collection) Find(ctx context.Context, filter interface{},
- // opts ...*options.FindOptions) (*Cursor, error)
- name = "Find" and n = 1
- or
- // func (coll *Collection) FindOne(ctx context.Context, filter interface{},
- // opts ...*options.FindOneOptions) *SingleResult
- name = "FindOne" and n = 1
- or
- // func (coll *Collection) FindOneAndDelete(ctx context.Context, filter interface{}, ...)
- // *SingleResult
- name = "FindOneAndDelete" and n = 1
- or
- // func (coll *Collection) FindOneAndReplace(ctx context.Context, filter interface{},
- // replacement interface{}, ...) *SingleResult
- name = "FindOneAndReplace" and n = 1
- or
- // func (coll *Collection) FindOneAndUpdate(ctx context.Context, filter interface{},
- // update interface{}, ...) *SingleResult
- name = "FindOneAndUpdate" and n = 1
- or
- // func (coll *Collection) ReplaceOne(ctx context.Context, filter interface{},
- // replacement interface{}, ...) (*UpdateResult, error)
- name = "ReplaceOne" and n = 1
- or
- // func (coll *Collection) UpdateMany(ctx context.Context, filter interface{},
- // update interface{}, ...) (*UpdateResult, error)
- name = "UpdateMany" and n = 1
- or
- // func (coll *Collection) UpdateOne(ctx context.Context, filter interface{},
- // update interface{}, ...) (*UpdateResult, error)
- name = "UpdateOne" and n = 1
- or
- // func (coll *Collection) Watch(ctx context.Context, pipeline interface{}, ...)
- // (*ChangeStream, error)
- name = "Watch" and n = 1
- or
- // func (coll *Collection) Aggregate(ctx context.Context, pipeline interface{},
- // opts ...*options.AggregateOptions) (*Cursor, error)
- name = "Aggregate" and n = 1
- }
-
- /**
- * A query used in an API function acting on a `Collection` struct of package
- * [go.mongodb.org/mongo-driver/mongo](https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo).
- */
- private class MongoDbCollectionQuery extends Range {
- MongoDbCollectionQuery() {
- exists(Method meth, string methodName, int n |
- mongoDbCollectionMethod(methodName, n) and
- meth.hasQualifiedName(package("go.mongodb.org/mongo-driver", "mongo"), "Collection",
- methodName) and
- this = meth.getACall().getArgument(n)
- )
- }
- }
}
/**
diff --git a/go/ql/lib/semmle/go/frameworks/SQL.qll b/go/ql/lib/semmle/go/frameworks/SQL.qll
index 1a6e22807812..a0e80fde1c9f 100644
--- a/go/ql/lib/semmle/go/frameworks/SQL.qll
+++ b/go/ql/lib/semmle/go/frameworks/SQL.qll
@@ -67,41 +67,34 @@ module SQL {
*/
abstract class Range extends DataFlow::Node { }
+ private class DefaultQueryString extends Range {
+ DefaultQueryString() {
+ exists(DataFlow::ArgumentNode arg | sinkNode(arg, "sql-injection") |
+ not arg instanceof DataFlow::ImplicitVarargsSlice and
+ this = arg
+ or
+ arg instanceof DataFlow::ImplicitVarargsSlice and
+ this = arg.getCall().getAnImplicitVarargsArgument()
+ )
+ }
+ }
+
/**
* An argument to an API of the squirrel library that is directly interpreted as SQL without
* taking syntactic structure into account.
*/
private class SquirrelQueryString extends Range {
SquirrelQueryString() {
- exists(Function fn |
- exists(string sq |
- sq =
- package([
- "github.com/Masterminds/squirrel", "gopkg.in/Masterminds/squirrel",
- "github.com/lann/squirrel"
- ], "")
- |
- fn.hasQualifiedName(sq, ["Delete", "Expr", "Insert", "Select", "Update"])
- or
- exists(Method m, string builder | m = fn |
- builder = ["DeleteBuilder", "InsertBuilder", "SelectBuilder", "UpdateBuilder"] and
- m.hasQualifiedName(sq, builder,
- ["Columns", "From", "Options", "OrderBy", "Prefix", "Suffix", "Where"])
- or
- builder = "InsertBuilder" and
- m.hasQualifiedName(sq, builder, ["Replace", "Into"])
- or
- builder = "SelectBuilder" and
- m.hasQualifiedName(sq, builder,
- ["CrossJoin", "GroupBy", "InnerJoin", "LeftJoin", "RightJoin"])
- or
- builder = "UpdateBuilder" and
- m.hasQualifiedName(sq, builder, ["Set", "Table"])
- )
- ) and
- this = fn.getACall().getArgument(0)
+ exists(string sq, Method m, string builder |
+ FlowExtensions::packageGrouping("squirrel", sq) and
+ builder = ["DeleteBuilder", "SelectBuilder", "UpdateBuilder"]
|
- this.getType().getUnderlyingType() instanceof StringType or
+ m.hasQualifiedName(sq, builder, "Where") and
+ this = m.getACall().getArgument(0)
+ ) and
+ (
+ this.getType().getUnderlyingType() instanceof StringType
+ or
this.getType().getUnderlyingType().(SliceType).getElementType() instanceof StringType
)
}
@@ -113,14 +106,6 @@ module SQL {
/** A string that might identify package `go-pg/pg/orm` or a specific version of it. */
private string gopgorm() { result = package("github.com/go-pg/pg", "orm") }
- /** A string that might identify package `github.com/rqlite/gorqlite` or `github.com/raindog308/gorqlite` or a specific version of it. */
- private string gorqlite() {
- result = package(["github.com/rqlite/gorqlite", "github.com/raindog308/gorqlite"], "")
- }
-
- /** A string that might identify package `github.com/gogf/gf/database/gdb` or a specific version of it. */
- private string gogf() { result = package("github.com/gogf/gf", "database/gdb") }
-
/**
* A string argument to an API of `go-pg/pg` that is directly interpreted as SQL without
* taking syntactic structure into account.
@@ -185,94 +170,6 @@ module SQL {
)
}
}
-
- /**
- * A string argument to an API of `github.com/rqlite/gorqlite`, or a specific version of it, that is directly interpreted as SQL without
- * taking syntactic structure into account.
- */
- private class GorqliteQueryString extends Range {
- GorqliteQueryString() {
- // func (conn *Connection) Query(sqlStatements []string) (results []QueryResult, err error)
- // func (conn *Connection) QueryOne(sqlStatement string) (qr QueryResult, err error)
- // func (conn *Connection) Queue(sqlStatements []string) (seq int64, err error)
- // func (conn *Connection) QueueOne(sqlStatement string) (seq int64, err error)
- // func (conn *Connection) Write(sqlStatements []string) (results []WriteResult, err error)
- // func (conn *Connection) WriteOne(sqlStatement string) (wr WriteResult, err error)
- exists(Method m, string name | m.hasQualifiedName(gorqlite(), "Connection", name) |
- name = ["Query", "QueryOne", "Queue", "QueueOne", "Write", "WriteOne"] and
- this = m.getACall().getArgument(0)
- )
- }
- }
-
- /**
- * A string argument to an API of `github.com/gogf/gf/database/gdb`, or a specific version of it, that is directly interpreted as SQL without
- * taking syntactic structure into account.
- */
- private class GogfQueryString extends Range {
- GogfQueryString() {
- exists(Method m, string name | m.implements(gogf(), ["DB", "Core", "TX"], name) |
- // func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err error)
- // func (c *Core) GetAll(sql string, args ...interface{}) (Result, error)
- // func (c *Core) GetArray(sql string, args ...interface{}) ([]Value, error)
- // func (c *Core) GetCount(sql string, args ...interface{}) (int, error)
- // func (c *Core) GetOne(sql string, args ...interface{}) (Record, error)
- // func (c *Core) GetValue(sql string, args ...interface{}) (Value, error)
- // func (c *Core) Prepare(sql string, execOnMaster ...bool) (*Stmt, error)
- // func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error)
- // func (c *Core) Raw(rawSql string, args ...interface{}) *Model
- name =
- [
- "Query", "Exec", "Prepare", "GetAll", "GetOne", "GetValue", "GetArray", "GetCount",
- "Raw"
- ] and
- this = m.getACall().getArgument(0)
- or
- // func (c *Core) GetScan(pointer interface{}, sql string, args ...interface{}) error
- // func (c *Core) GetStruct(pointer interface{}, sql string, args ...interface{}) error
- // func (c *Core) GetStructs(pointer interface{}, sql string, args ...interface{}) error
- name = ["GetScan", "GetStruct", "GetStructs"] and
- this = m.getACall().getArgument(1)
- or
- // func (c *Core) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error)
- // func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error)
- // func (c *Core) DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error)
- // func (c *Core) DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error)
- // func (c *Core) DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (rows *sql.Rows, err error)
- name = ["DoGetAll", "DoQuery", "DoExec", "DoCommit", "DoPrepare"] and
- this = m.getACall().getArgument(2)
- )
- }
- }
- }
-
- /** A model for sinks of GORM. */
- private class GormSink extends SQL::QueryString::Range {
- GormSink() {
- exists(Method meth, string package, string name |
- meth.hasQualifiedName(package, "DB", name) and
- this = meth.getACall().getSyntacticArgument(0) and
- package = Gorm::packagePath() and
- name in [
- "Where", "Raw", "Order", "Not", "Or", "Select", "Table", "Group", "Having", "Joins",
- "Exec", "Distinct", "Pluck"
- ]
- )
- }
- }
-
- /** A model for sinks of github.com/jmoiron/sqlx. */
- private class SqlxSink extends SQL::QueryString::Range {
- SqlxSink() {
- exists(Method meth, string name, int n |
- meth.hasQualifiedName(package("github.com/jmoiron/sqlx", ""), ["DB", "Tx"], name) and
- this = meth.getACall().getArgument(n)
- |
- name = ["Select", "Get"] and n = 1
- or
- name = ["MustExec", "Queryx", "NamedExec", "NamedQuery"] and n = 0
- )
- }
}
}
@@ -291,71 +188,25 @@ module Gorm {
*/
module Xorm {
/** Gets the package name for Xorm. */
- string packagePath() { result = package(["xorm.io/xorm", "github.com/go-xorm/xorm"], "") }
+ string packagePath() { FlowExtensions::packageGrouping("xorm", result) }
/** A model for sinks of XORM. */
private class XormSink extends SQL::QueryString::Range {
XormSink() {
- exists(Method meth, string type, string name, int n |
+ exists(Method meth, string type, string name |
meth.hasQualifiedName(Xorm::packagePath(), type, name) and
- this = meth.getACall().getSyntacticArgument(n) and
- type = ["Engine", "Session"]
+ type = ["Engine", "Session"] and
+ name = ["Exec", "Query", "QueryInterface", "QueryString"]
|
- name =
- [
- "Query", "Exec", "QueryString", "QueryInterface", "SQL", "Where", "And", "Or", "Alias",
- "NotIn", "In", "Select", "SetExpr", "OrderBy", "Having", "GroupBy"
- ] and
- n = 0
- or
- name = ["SumInt", "Sum", "Sums", "SumsInt"] and n = 1
- or
- name = "Join" and n = [0, 1, 2]
+ this = meth.getACall().getSyntacticArgument(0)
)
}
}
}
/**
+ * DEPRECATED
+ *
* Provides classes for working with the [Bun](https://bun.uptrace.dev/) package.
*/
-module Bun {
- /** Gets the package name for Bun package. */
- private string packagePath() { result = package("github.com/uptrace/bun", "") }
-
- /** A model for sinks of Bun. */
- private class BunSink extends SQL::QueryString::Range {
- BunSink() {
- exists(Function f, string m, int arg | this = f.getACall().getArgument(arg) |
- f.hasQualifiedName(packagePath(), m) and
- m = "NewRawQuery" and
- arg = 1
- )
- or
- exists(Method f, string tp, string m, int arg | this = f.getACall().getArgument(arg) |
- f.hasQualifiedName(packagePath(), tp, m) and
- (
- tp = ["DB", "Conn"] and
- m = ["ExecContext", "PrepareContext", "QueryContext", "QueryRowContext"] and
- arg = 1
- or
- tp = ["DB", "Conn"] and
- m = ["Exec", "NewRaw", "Prepare", "Query", "QueryRow", "Raw"] and
- arg = 0
- or
- tp.matches("%Query") and
- m =
- [
- "ColumnExpr", "DistinctOn", "For", "GroupExpr", "Having", "ModelTableExpr",
- "OrderExpr", "TableExpr", "Where", "WhereOr"
- ] and
- arg = 0
- or
- tp = "RawQuery" and
- m = "NewRaw" and
- arg = 0
- )
- )
- }
- }
-}
+deprecated module Bun { }
diff --git a/go/ql/lib/semmle/go/frameworks/Spew.qll b/go/ql/lib/semmle/go/frameworks/Spew.qll
index b12bd0fed815..f49a4aa4d89d 100644
--- a/go/ql/lib/semmle/go/frameworks/Spew.qll
+++ b/go/ql/lib/semmle/go/frameworks/Spew.qll
@@ -33,16 +33,6 @@ module Spew {
override int getFormatStringIndex() { result = super.getFirstPrintedArg() }
}
- private class SpewCall extends LoggerCall::Range, DataFlow::CallNode {
- SpewFunction target;
-
- SpewCall() { this = target.getACall() }
-
- override DataFlow::Node getAMessageComponent() {
- result = this.getSyntacticArgument(any(int i | i >= target.getFirstPrintedArg()))
- }
- }
-
// These are expressed using TaintTracking::FunctionModel because varargs functions don't work with Models-as-Data sumamries yet.
/** The `Sprint` function or one of its variants. */
class Sprinter extends TaintTracking::FunctionModel {
diff --git a/go/ql/lib/semmle/go/frameworks/Zap.qll b/go/ql/lib/semmle/go/frameworks/Zap.qll
index 359f9aba4107..0928d2b0595c 100644
--- a/go/ql/lib/semmle/go/frameworks/Zap.qll
+++ b/go/ql/lib/semmle/go/frameworks/Zap.qll
@@ -34,18 +34,6 @@ module Zap {
override int getFormatStringIndex() { result = 0 }
}
- /**
- * A call to a logger function in Zap.
- *
- * Functions which add data to be included the next time a direct logging
- * function is called are included.
- */
- private class ZapCall extends LoggerCall::Range, DataFlow::MethodCallNode {
- ZapCall() { this = any(ZapFunction f).getACall() }
-
- override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
- }
-
// These are expressed using TaintTracking::FunctionModel because varargs functions don't work with Models-as-Data sumamries yet.
/** The function `Fields` that creates an `Option` that can be added to the logger out of `Field`s. */
class FieldsFunction extends TaintTracking::FunctionModel {
diff --git a/go/ql/lib/semmle/go/frameworks/stdlib/DatabaseSql.qll b/go/ql/lib/semmle/go/frameworks/stdlib/DatabaseSql.qll
index 845225af5bd2..f41326887961 100644
--- a/go/ql/lib/semmle/go/frameworks/stdlib/DatabaseSql.qll
+++ b/go/ql/lib/semmle/go/frameworks/stdlib/DatabaseSql.qll
@@ -26,7 +26,7 @@ module DatabaseSql {
override DataFlow::Node getAResult() { result = this.getResult(0) }
override SQL::QueryString getAQueryString() {
- result = this.getAnArgument()
+ result = this.getASyntacticArgument()
or
// attempt to resolve a `QueryString` for `Stmt`s using local data flow.
t = "Stmt" and
@@ -34,24 +34,6 @@ module DatabaseSql {
}
}
- /** A query string used in an API function of the `database/sql` package. */
- private class QueryString extends SQL::QueryString::Range {
- QueryString() {
- exists(Method meth, string base, string t, string m, int n |
- t = ["DB", "Tx", "Conn"] and
- meth.hasQualifiedName("database/sql", t, m) and
- this = meth.getACall().getArgument(n)
- |
- base = ["Exec", "Prepare", "Query", "QueryRow"] and
- (
- m = base and n = 0
- or
- m = base + "Context" and n = 1
- )
- )
- }
- }
-
/** A query in the standard `database/sql/driver` package. */
private class DriverQuery extends SQL::Query::Range, DataFlow::MethodCallNode {
DriverQuery() {
@@ -78,36 +60,13 @@ module DatabaseSql {
override DataFlow::Node getAResult() { result = this.getResult(0) }
override SQL::QueryString getAQueryString() {
- result = this.getAnArgument()
+ result = this.getASyntacticArgument()
or
this.getTarget().hasQualifiedName("database/sql/driver", "Stmt") and
result = this.getReceiver().getAPredecessor*().(DataFlow::MethodCallNode).getAnArgument()
}
}
- /** A query string used in an API function of the standard `database/sql/driver` package. */
- private class DriverQueryString extends SQL::QueryString::Range {
- DriverQueryString() {
- exists(Method meth, int n |
- (
- meth.hasQualifiedName("database/sql/driver", "Execer", "Exec") and n = 0
- or
- meth.hasQualifiedName("database/sql/driver", "ExecerContext", "ExecContext") and n = 1
- or
- meth.hasQualifiedName("database/sql/driver", "Conn", "Prepare") and n = 0
- or
- meth.hasQualifiedName("database/sql/driver", "ConnPrepareContext", "PrepareContext") and
- n = 1
- or
- meth.hasQualifiedName("database/sql/driver", "Queryer", "Query") and n = 0
- or
- meth.hasQualifiedName("database/sql/driver", "QueryerContext", "QueryContext") and n = 1
- ) and
- this = meth.getACall().getArgument(n)
- )
- }
- }
-
// These are expressed using TaintTracking::FunctionModel because varargs functions don't work with Models-as-Data sumamries yet.
private class SqlMethodModels extends TaintTracking::FunctionModel, Method {
FunctionInput inp;
diff --git a/go/ql/lib/semmle/go/frameworks/stdlib/Fmt.qll b/go/ql/lib/semmle/go/frameworks/stdlib/Fmt.qll
index 54794ea21c9e..6adbd542e9b6 100644
--- a/go/ql/lib/semmle/go/frameworks/stdlib/Fmt.qll
+++ b/go/ql/lib/semmle/go/frameworks/stdlib/Fmt.qll
@@ -41,13 +41,6 @@ module Fmt {
Printer() { this.hasQualifiedName("fmt", ["Print", "Printf", "Println"]) }
}
- /** A call to `Print` or similar. */
- private class PrintCall extends LoggerCall::Range, DataFlow::CallNode {
- PrintCall() { this.getTarget() instanceof Printer }
-
- override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
- }
-
/** The `Fprint` function or one of its variants. */
private class Fprinter extends TaintTracking::FunctionModel {
Fprinter() {
diff --git a/go/ql/lib/semmle/go/frameworks/stdlib/Log.qll b/go/ql/lib/semmle/go/frameworks/stdlib/Log.qll
index 5b402fca1b7e..ca74160bf0df 100644
--- a/go/ql/lib/semmle/go/frameworks/stdlib/Log.qll
+++ b/go/ql/lib/semmle/go/frameworks/stdlib/Log.qll
@@ -32,16 +32,6 @@ module Log {
override int getFormatStringIndex() { result = 0 }
}
- private class LogCall extends LoggerCall::Range, DataFlow::CallNode {
- LogFunction target;
-
- LogCall() { this = target.getACall() }
-
- override DataFlow::Node getAMessageComponent() {
- result = this.getSyntacticArgument(any(int i | i >= target.getFirstPrintedArg()))
- }
- }
-
/** A fatal log function, which calls `os.Exit`. */
private class FatalLogFunction extends Function {
FatalLogFunction() { this.hasQualifiedName("log", ["Fatal", "Fatalf", "Fatalln"]) }
diff --git a/go/ql/lib/semmle/go/frameworks/stdlib/Os.qll b/go/ql/lib/semmle/go/frameworks/stdlib/Os.qll
index fb153451c597..72ea4cc6c573 100644
--- a/go/ql/lib/semmle/go/frameworks/stdlib/Os.qll
+++ b/go/ql/lib/semmle/go/frameworks/stdlib/Os.qll
@@ -43,12 +43,4 @@ module Os {
input = inp and output = outp
}
}
-
- private class Stdin extends SourceNode {
- Stdin() {
- exists(Variable osStdin | osStdin.hasQualifiedName("os", "Stdin") | this = osStdin.getARead())
- }
-
- override string getThreatModel() { result = "stdin" }
- }
}
diff --git a/go/ql/src/change-notes/2024-11-26-model-slices-package.md b/go/ql/src/change-notes/2024-11-26-model-slices-package.md
new file mode 100644
index 000000000000..5a3141c8075a
--- /dev/null
+++ b/go/ql/src/change-notes/2024-11-26-model-slices-package.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Added value flow models for functions in the `slices` package which do not involve the `iter` package.
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/completetest.ext.yml b/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/completetest.ext.yml
index 79bf9128ef5c..d89a9e04e16a 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/completetest.ext.yml
+++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/completetest.ext.yml
@@ -35,10 +35,12 @@ extensions:
pack: codeql/go-all
extensible: sourceModel
data:
+ - ["github.com/nonexistent/test", "", False, "SourceVariable", "", "", "", "qltest", "manual"]
- ["github.com/nonexistent/test", "A", False, "Src1", "", "", "ReturnValue", "qltest", "manual"]
- addsTo:
pack: codeql/go-all
extensible: sinkModel
data:
+ - ["github.com/nonexistent/test", "", False, "SinkVariable", "", "", "", "qltest", "manual"]
- ["github.com/nonexistent/test", "B", False, "Sink1", "", "", "Argument[0]", "qltest", "manual"]
- ["github.com/nonexistent/test", "B", False, "SinkManyArgs", "", "", "Argument[0..2]", "qltest", "manual"]
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/sinks.expected b/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/sinks.expected
index fc9adff8942d..755c3f822791 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/sinks.expected
+++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/sinks.expected
@@ -43,3 +43,4 @@ invalidModelRow
| test.go:199:17:199:20 | arg1 | qltest |
| test.go:199:23:199:26 | arg2 | qltest |
| test.go:199:29:199:32 | arg3 | qltest |
+| test.go:202:22:202:25 | temp | qltest |
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/sinks.ext.yml b/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/sinks.ext.yml
index 426e094c00c3..ec19b822a8c1 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/sinks.ext.yml
+++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/sinks.ext.yml
@@ -3,6 +3,7 @@ extensions:
pack: codeql/go-all
extensible: sinkModel
data:
+ - ["github.com/nonexistent/test", "", False, "SinkVariable", "", "", "", "qltest", "manual"]
- ["github.com/nonexistent/test", "B", False, "Sink1", "", "", "Argument[0]", "qltest", "manual"]
- ["github.com/nonexistent/test", "B", False, "SinkMethod", "", "", "Argument[receiver]", "qltest", "manual"]
- ["github.com/nonexistent/test", "B", False, "SinkManyArgs", "", "", "Argument[0..2]", "qltest", "manual"]
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/srcs.expected b/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/srcs.expected
index d63fedba3fdb..bd1525a984b9 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/srcs.expected
+++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/srcs.expected
@@ -21,3 +21,4 @@ invalidModelRow
| test.go:183:17:183:24 | call to Src1 | qltest |
| test.go:187:24:187:31 | call to Src1 | qltest |
| test.go:191:24:191:31 | call to Src1 | qltest |
+| test.go:201:10:201:28 | selection of SourceVariable | qltest |
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/srcs.ext.yml b/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/srcs.ext.yml
index 5493650132c1..cda2183894ca 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/srcs.ext.yml
+++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/srcs.ext.yml
@@ -3,9 +3,10 @@ extensions:
pack: codeql/go-all
extensible: sourceModel
data:
+ - ["github.com/nonexistent/test", "", False, "SourceVariable", "", "", "", "qltest", "manual"]
- ["github.com/nonexistent/test", "A", False, "Src1", "", "", "ReturnValue", "qltest", "manual"]
- ["github.com/nonexistent/test", "A", False, "Src2", "", "", "ReturnValue", "qltest", "manual"]
- ["github.com/nonexistent/test", "A", True, "Src2", "", "", "ReturnValue", "qltest-w-subtypes", "manual"]
- ["github.com/nonexistent/test", "A", False, "SrcArg", "", "", "Argument[0]", "qltest-arg", "manual"]
- ["github.com/nonexistent/test", "A", False, "Src3", "", "", "ReturnValue[0]", "qltest", "manual"]
- - ["github.com/nonexistent/test", "A", True, "Src3", "", "", "ReturnValue[1]", "qltest-w-subtypes", "manual"]
\ No newline at end of file
+ - ["github.com/nonexistent/test", "A", True, "Src3", "", "", "ReturnValue[1]", "qltest-w-subtypes", "manual"]
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/test.go b/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/test.go
index 33e980dac990..29ed066cd50d 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/test.go
+++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/test.go
@@ -197,6 +197,9 @@ func simpleflow() {
arg3 := src
arg4 := src
b.SinkManyArgs(arg1, arg2, arg3, arg4) // $ hasTaintFlow="arg1" hasTaintFlow="arg2" hasTaintFlow="arg3"
+
+ temp := test.SourceVariable
+ test.SinkVariable = temp // $ hasTaintFlow="temp"
}
type mapstringstringtype map[string]string
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/vendor/github.com/nonexistent/test/stub.go b/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/vendor/github.com/nonexistent/test/stub.go
index 05a5f741d764..72681cf72382 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/vendor/github.com/nonexistent/test/stub.go
+++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalTaintFlow/vendor/github.com/nonexistent/test/stub.go
@@ -72,3 +72,6 @@ func (c C) Get() string { return "" }
func (c *C) SetThroughPointer(f string) {}
func (c *C) GetThroughPointer() string { return "" }
+
+var SourceVariable string
+var SinkVariable string
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/completetest.ext.yml b/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/completetest.ext.yml
index 8fbc26ff6cda..924e19a8a73b 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/completetest.ext.yml
+++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/completetest.ext.yml
@@ -35,10 +35,12 @@ extensions:
pack: codeql/go-all
extensible: sourceModel
data:
+ - ["github.com/nonexistent/test", "", False, "SourceVariable", "", "", "", "qltest", "manual"]
- ["github.com/nonexistent/test", "A", False, "Src1", "", "", "ReturnValue", "qltest", "manual"]
- addsTo:
pack: codeql/go-all
extensible: sinkModel
data:
+ - ["github.com/nonexistent/test", "", False, "SinkVariable", "", "", "", "qltest", "manual"]
- ["github.com/nonexistent/test", "B", False, "Sink1", "", "", "Argument[0]", "qltest", "manual"]
- ["github.com/nonexistent/test", "B", False, "SinkManyArgs", "", "", "Argument[0..2]", "qltest", "manual"]
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/sinks.expected b/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/sinks.expected
index 0fe3a614e11f..c9940e181c8c 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/sinks.expected
+++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/sinks.expected
@@ -49,3 +49,4 @@ invalidModelRow
| test.go:205:10:205:26 | call to min | qltest |
| test.go:206:10:206:26 | call to min | qltest |
| test.go:207:10:207:26 | call to min | qltest |
+| test.go:210:22:210:25 | temp | qltest |
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/sinks.ext.yml b/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/sinks.ext.yml
index 426e094c00c3..ec19b822a8c1 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/sinks.ext.yml
+++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/sinks.ext.yml
@@ -3,6 +3,7 @@ extensions:
pack: codeql/go-all
extensible: sinkModel
data:
+ - ["github.com/nonexistent/test", "", False, "SinkVariable", "", "", "", "qltest", "manual"]
- ["github.com/nonexistent/test", "B", False, "Sink1", "", "", "Argument[0]", "qltest", "manual"]
- ["github.com/nonexistent/test", "B", False, "SinkMethod", "", "", "Argument[receiver]", "qltest", "manual"]
- ["github.com/nonexistent/test", "B", False, "SinkManyArgs", "", "", "Argument[0..2]", "qltest", "manual"]
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/srcs.expected b/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/srcs.expected
index d63fedba3fdb..6fcfcc2a3bcc 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/srcs.expected
+++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/srcs.expected
@@ -21,3 +21,4 @@ invalidModelRow
| test.go:183:17:183:24 | call to Src1 | qltest |
| test.go:187:24:187:31 | call to Src1 | qltest |
| test.go:191:24:191:31 | call to Src1 | qltest |
+| test.go:209:10:209:28 | selection of SourceVariable | qltest |
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/srcs.ext.yml b/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/srcs.ext.yml
index 5493650132c1..cda2183894ca 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/srcs.ext.yml
+++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/srcs.ext.yml
@@ -3,9 +3,10 @@ extensions:
pack: codeql/go-all
extensible: sourceModel
data:
+ - ["github.com/nonexistent/test", "", False, "SourceVariable", "", "", "", "qltest", "manual"]
- ["github.com/nonexistent/test", "A", False, "Src1", "", "", "ReturnValue", "qltest", "manual"]
- ["github.com/nonexistent/test", "A", False, "Src2", "", "", "ReturnValue", "qltest", "manual"]
- ["github.com/nonexistent/test", "A", True, "Src2", "", "", "ReturnValue", "qltest-w-subtypes", "manual"]
- ["github.com/nonexistent/test", "A", False, "SrcArg", "", "", "Argument[0]", "qltest-arg", "manual"]
- ["github.com/nonexistent/test", "A", False, "Src3", "", "", "ReturnValue[0]", "qltest", "manual"]
- - ["github.com/nonexistent/test", "A", True, "Src3", "", "", "ReturnValue[1]", "qltest-w-subtypes", "manual"]
\ No newline at end of file
+ - ["github.com/nonexistent/test", "A", True, "Src3", "", "", "ReturnValue[1]", "qltest-w-subtypes", "manual"]
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/test.go b/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/test.go
index 82419ae7d595..72c4db352480 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/test.go
+++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/test.go
@@ -205,6 +205,9 @@ func simpleflow() {
b.Sink1(min(srcInt, 0, 1)) // $ hasValueFlow="call to min"
b.Sink1(min(0, srcInt, 1)) // $ hasValueFlow="call to min"
b.Sink1(min(0, 1, srcInt)) // $ hasValueFlow="call to min"
+
+ temp := test.SourceVariable
+ test.SinkVariable = temp // $ hasValueFlow="temp"
}
type mapstringstringtype map[string]string
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/vendor/github.com/nonexistent/test/stub.go b/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/vendor/github.com/nonexistent/test/stub.go
index 05a5f741d764..72681cf72382 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/vendor/github.com/nonexistent/test/stub.go
+++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/vendor/github.com/nonexistent/test/stub.go
@@ -72,3 +72,6 @@ func (c C) Get() string { return "" }
func (c *C) SetThroughPointer(f string) {}
func (c *C) GetThroughPointer() string { return "" }
+
+var SourceVariable string
+var SinkVariable string
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/SqlInjection.expected b/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/SqlInjection.expected
index 6cc1e09486b7..1198f8d41a9b 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/SqlInjection.expected
+++ b/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/SqlInjection.expected
@@ -32,40 +32,61 @@
| test.go:59:31:59:39 | untrusted | test.go:57:15:57:41 | call to UserAgent | test.go:59:31:59:39 | untrusted | This query depends on a $@. | test.go:57:15:57:41 | call to UserAgent | user-provided value |
| test.go:65:19:65:27 | untrusted | test.go:63:15:63:41 | call to UserAgent | test.go:65:19:65:27 | untrusted | This query depends on a $@. | test.go:63:15:63:41 | call to UserAgent | user-provided value |
edges
-| test.go:11:15:11:41 | call to UserAgent | test.go:13:11:13:19 | untrusted | provenance | Src:MaD:1 |
-| test.go:11:15:11:41 | call to UserAgent | test.go:14:23:14:31 | untrusted | provenance | Src:MaD:1 |
-| test.go:11:15:11:41 | call to UserAgent | test.go:15:14:15:22 | untrusted | provenance | Src:MaD:1 |
-| test.go:11:15:11:41 | call to UserAgent | test.go:16:26:16:34 | untrusted | provenance | Src:MaD:1 |
-| test.go:11:15:11:41 | call to UserAgent | test.go:17:12:17:20 | untrusted | provenance | Src:MaD:1 |
-| test.go:11:15:11:41 | call to UserAgent | test.go:18:24:18:32 | untrusted | provenance | Src:MaD:1 |
-| test.go:11:15:11:41 | call to UserAgent | test.go:19:15:19:23 | untrusted | provenance | Src:MaD:1 |
-| test.go:11:15:11:41 | call to UserAgent | test.go:20:27:20:35 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:28:12:28:20 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:29:10:29:18 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:30:15:30:23 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:31:14:31:22 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:32:15:32:23 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:33:8:33:16 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:34:11:34:19 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:35:9:35:17 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:36:8:36:16 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:37:8:37:16 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:38:13:38:21 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:39:13:39:21 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:40:12:40:20 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:41:12:41:20 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:42:9:42:17 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:43:12:43:20 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:44:16:44:24 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:45:12:45:20 | untrusted | provenance | Src:MaD:1 |
-| test.go:25:15:25:41 | call to UserAgent | test.go:46:14:46:22 | untrusted | provenance | Src:MaD:1 |
-| test.go:26:16:26:42 | call to UserAgent | test.go:44:27:44:36 | untrusted2 | provenance | Src:MaD:1 |
-| test.go:26:16:26:42 | call to UserAgent | test.go:46:25:46:34 | untrusted2 | provenance | Src:MaD:1 |
-| test.go:50:15:50:41 | call to UserAgent | test.go:52:12:52:20 | untrusted | provenance | Src:MaD:1 |
-| test.go:57:15:57:41 | call to UserAgent | test.go:59:31:59:39 | untrusted | provenance | Src:MaD:1 |
-| test.go:63:15:63:41 | call to UserAgent | test.go:65:19:65:27 | untrusted | provenance | Src:MaD:1 |
+| test.go:11:15:11:41 | call to UserAgent | test.go:13:11:13:19 | untrusted | provenance | Src:MaD:22 Sink:MaD:2 |
+| test.go:11:15:11:41 | call to UserAgent | test.go:14:23:14:31 | untrusted | provenance | Src:MaD:22 Sink:MaD:3 |
+| test.go:11:15:11:41 | call to UserAgent | test.go:15:14:15:22 | untrusted | provenance | Src:MaD:22 Sink:MaD:4 |
+| test.go:11:15:11:41 | call to UserAgent | test.go:16:26:16:34 | untrusted | provenance | Src:MaD:22 Sink:MaD:5 |
+| test.go:11:15:11:41 | call to UserAgent | test.go:17:12:17:20 | untrusted | provenance | Src:MaD:22 Sink:MaD:6 |
+| test.go:11:15:11:41 | call to UserAgent | test.go:18:24:18:32 | untrusted | provenance | Src:MaD:22 Sink:MaD:7 |
+| test.go:11:15:11:41 | call to UserAgent | test.go:19:15:19:23 | untrusted | provenance | Src:MaD:22 Sink:MaD:8 |
+| test.go:11:15:11:41 | call to UserAgent | test.go:20:27:20:35 | untrusted | provenance | Src:MaD:22 Sink:MaD:9 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:28:12:28:20 | untrusted | provenance | Src:MaD:22 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:29:10:29:18 | untrusted | provenance | Src:MaD:22 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:30:15:30:23 | untrusted | provenance | Src:MaD:22 Sink:MaD:13 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:31:14:31:22 | untrusted | provenance | Src:MaD:22 Sink:MaD:15 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:32:15:32:23 | untrusted | provenance | Src:MaD:22 Sink:MaD:18 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:33:8:33:16 | untrusted | provenance | Src:MaD:22 Sink:MaD:16 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:34:11:34:19 | untrusted | provenance | Src:MaD:22 Sink:MaD:20 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:35:9:35:17 | untrusted | provenance | Src:MaD:22 Sink:MaD:11 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:36:8:36:16 | untrusted | provenance | Src:MaD:22 Sink:MaD:17 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:37:8:37:16 | untrusted | provenance | Src:MaD:22 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:38:13:38:21 | untrusted | provenance | Src:MaD:22 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:39:13:39:21 | untrusted | provenance | Src:MaD:22 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:40:12:40:20 | untrusted | provenance | Src:MaD:22 Sink:MaD:12 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:41:12:41:20 | untrusted | provenance | Src:MaD:22 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:42:9:42:17 | untrusted | provenance | Src:MaD:22 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:43:12:43:20 | untrusted | provenance | Src:MaD:22 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:44:16:44:24 | untrusted | provenance | Src:MaD:22 Sink:MaD:14 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:45:12:45:20 | untrusted | provenance | Src:MaD:22 |
+| test.go:25:15:25:41 | call to UserAgent | test.go:46:14:46:22 | untrusted | provenance | Src:MaD:22 Sink:MaD:19 |
+| test.go:26:16:26:42 | call to UserAgent | test.go:44:27:44:36 | untrusted2 | provenance | Src:MaD:22 |
+| test.go:26:16:26:42 | call to UserAgent | test.go:46:25:46:34 | untrusted2 | provenance | Src:MaD:22 Sink:MaD:19 |
+| test.go:50:15:50:41 | call to UserAgent | test.go:52:12:52:20 | untrusted | provenance | Src:MaD:22 Sink:MaD:10 |
+| test.go:57:15:57:41 | call to UserAgent | test.go:59:31:59:39 | untrusted | provenance | Src:MaD:22 Sink:MaD:21 |
+| test.go:63:15:63:41 | call to UserAgent | test.go:65:19:65:27 | untrusted | provenance | Src:MaD:22 Sink:MaD:1 |
models
-| 1 | Source: net/http; Request; true; UserAgent; ; ; ReturnValue; remote; manual |
+| 1 | Sink: group:beego-orm; Condition; true; Raw; ; ; Argument[1]; sql-injection; manual |
+| 2 | Sink: group:beego-orm; DB; true; Exec; ; ; Argument[0]; sql-injection; manual |
+| 3 | Sink: group:beego-orm; DB; true; ExecContext; ; ; Argument[1]; sql-injection; manual |
+| 4 | Sink: group:beego-orm; DB; true; Prepare; ; ; Argument[0]; sql-injection; manual |
+| 5 | Sink: group:beego-orm; DB; true; PrepareContext; ; ; Argument[1]; sql-injection; manual |
+| 6 | Sink: group:beego-orm; DB; true; Query; ; ; Argument[0]; sql-injection; manual |
+| 7 | Sink: group:beego-orm; DB; true; QueryContext; ; ; Argument[1]; sql-injection; manual |
+| 8 | Sink: group:beego-orm; DB; true; QueryRow; ; ; Argument[0]; sql-injection; manual |
+| 9 | Sink: group:beego-orm; DB; true; QueryRowContext; ; ; Argument[1]; sql-injection; manual |
+| 10 | Sink: group:beego-orm; Ormer; true; Raw; ; ; Argument[0]; sql-injection; manual |
+| 11 | Sink: group:beego-orm; QueryBuilder; true; And; ; ; Argument[0]; sql-injection; manual |
+| 12 | Sink: group:beego-orm; QueryBuilder; true; Having; ; ; Argument[0]; sql-injection; manual |
+| 13 | Sink: group:beego-orm; QueryBuilder; true; InnerJoin; ; ; Argument[0]; sql-injection; manual |
+| 14 | Sink: group:beego-orm; QueryBuilder; true; InsertInto; ; ; Argument[0..1]; sql-injection; manual |
+| 15 | Sink: group:beego-orm; QueryBuilder; true; LeftJoin; ; ; Argument[0]; sql-injection; manual |
+| 16 | Sink: group:beego-orm; QueryBuilder; true; On; ; ; Argument[0]; sql-injection; manual |
+| 17 | Sink: group:beego-orm; QueryBuilder; true; Or; ; ; Argument[0]; sql-injection; manual |
+| 18 | Sink: group:beego-orm; QueryBuilder; true; RightJoin; ; ; Argument[0]; sql-injection; manual |
+| 19 | Sink: group:beego-orm; QueryBuilder; true; Subquery; ; ; Argument[0..1]; sql-injection; manual |
+| 20 | Sink: group:beego-orm; QueryBuilder; true; Where; ; ; Argument[0]; sql-injection; manual |
+| 21 | Sink: group:beego-orm; QuerySeter; true; FilterRaw; ; ; Argument[1]; sql-injection; manual |
+| 22 | Source: net/http; Request; true; UserAgent; ; ; ReturnValue; remote; manual |
nodes
| test.go:11:15:11:41 | call to UserAgent | semmle.label | call to UserAgent |
| test.go:13:11:13:19 | untrusted | semmle.label | untrusted |
diff --git a/java/ql/automodel/test/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractionTests.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/QueryString.expected
similarity index 57%
rename from java/ql/automodel/test/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractionTests.expected
rename to go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/QueryString.expected
index 8ec8033d086e..db33d6d2504a 100644
--- a/java/ql/automodel/test/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractionTests.expected
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/QueryString.expected
@@ -1,2 +1,3 @@
testFailures
+invalidModelRow
failures
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/QueryString.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/QueryString.ql
new file mode 100644
index 000000000000..eeb43a82fadd
--- /dev/null
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/QueryString.ql
@@ -0,0 +1,60 @@
+import go
+import semmle.go.dataflow.ExternalFlow
+import ModelValidation
+import TestUtilities.InlineExpectationsTest
+
+module SqlTest implements TestSig {
+ string getARelevantTag() { result = "query" }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "query" and
+ exists(SQL::Query q, SQL::QueryString qs | qs = q.getAQueryString() |
+ q.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
+ location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
+ element = q.toString() and
+ value = qs.toString()
+ )
+ }
+}
+
+module QueryString implements TestSig {
+ string getARelevantTag() { result = "querystring" }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "querystring" and
+ element = "" and
+ exists(SQL::QueryString qs | not exists(SQL::Query q | qs = q.getAQueryString()) |
+ qs.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
+ location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
+ value = qs.toString()
+ )
+ }
+}
+
+module Config implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLit }
+
+ predicate isSink(DataFlow::Node n) {
+ n = any(DataFlow::CallNode cn | cn.getTarget().getName() = "sink").getAnArgument()
+ }
+}
+
+module Flow = TaintTracking::Global;
+
+module TaintFlow implements TestSig {
+ string getARelevantTag() { result = "flowfrom" }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "flowfrom" and
+ element = "" and
+ exists(DataFlow::Node fromNode, DataFlow::Node toNode |
+ toNode
+ .hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
+ location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
+ Flow::flow(fromNode, toNode) and
+ value = fromNode.asExpr().(StringLit).getValue()
+ )
+ }
+}
+
+import MakeTest>
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.expected
deleted file mode 100644
index ca70ed07c33d..000000000000
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.expected
+++ /dev/null
@@ -1,25 +0,0 @@
-| gorm.go:20:12:20:20 | untrusted | github.com/jinzhu/gorm | DB | Where |
-| gorm.go:21:10:21:18 | untrusted | github.com/jinzhu/gorm | DB | Raw |
-| gorm.go:22:10:22:18 | untrusted | github.com/jinzhu/gorm | DB | Not |
-| gorm.go:23:12:23:20 | untrusted | github.com/jinzhu/gorm | DB | Order |
-| gorm.go:24:9:24:17 | untrusted | github.com/jinzhu/gorm | DB | Or |
-| gorm.go:25:13:25:21 | untrusted | github.com/jinzhu/gorm | DB | Select |
-| gorm.go:26:12:26:20 | untrusted | github.com/jinzhu/gorm | DB | Table |
-| gorm.go:27:12:27:20 | untrusted | github.com/jinzhu/gorm | DB | Group |
-| gorm.go:28:13:28:21 | untrusted | github.com/jinzhu/gorm | DB | Having |
-| gorm.go:29:12:29:20 | untrusted | github.com/jinzhu/gorm | DB | Joins |
-| gorm.go:30:11:30:19 | untrusted | github.com/jinzhu/gorm | DB | Exec |
-| gorm.go:31:12:31:20 | untrusted | github.com/jinzhu/gorm | DB | Pluck |
-| gorm.go:34:12:34:20 | untrusted | gorm.io/gorm | DB | Where |
-| gorm.go:35:10:35:18 | untrusted | gorm.io/gorm | DB | Raw |
-| gorm.go:36:10:36:18 | untrusted | gorm.io/gorm | DB | Not |
-| gorm.go:37:12:37:20 | untrusted | gorm.io/gorm | DB | Order |
-| gorm.go:38:9:38:17 | untrusted | gorm.io/gorm | DB | Or |
-| gorm.go:39:13:39:21 | untrusted | gorm.io/gorm | DB | Select |
-| gorm.go:40:12:40:20 | untrusted | gorm.io/gorm | DB | Table |
-| gorm.go:41:12:41:20 | untrusted | gorm.io/gorm | DB | Group |
-| gorm.go:42:13:42:21 | untrusted | gorm.io/gorm | DB | Having |
-| gorm.go:43:12:43:20 | untrusted | gorm.io/gorm | DB | Joins |
-| gorm.go:44:11:44:19 | untrusted | gorm.io/gorm | DB | Exec |
-| gorm.go:45:15:45:23 | untrusted | gorm.io/gorm | DB | Distinct |
-| gorm.go:46:12:46:20 | untrusted | gorm.io/gorm | DB | Pluck |
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.go
index bee8edbf7af8..2d736e2407c9 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.go
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.go
@@ -13,36 +13,35 @@ func getUntrustedString() string {
}
func main() {
-
untrusted := getUntrustedString()
db1 := gorm1.DB{}
- db1.Where(untrusted)
- db1.Raw(untrusted)
- db1.Not(untrusted)
- db1.Order(untrusted)
- db1.Or(untrusted)
- db1.Select(untrusted)
- db1.Table(untrusted)
- db1.Group(untrusted)
- db1.Having(untrusted)
- db1.Joins(untrusted)
- db1.Exec(untrusted)
- db1.Pluck(untrusted, nil)
+ db1.Where(untrusted) // $ querystring=untrusted
+ db1.Raw(untrusted) // $ querystring=untrusted
+ db1.Not(untrusted) // $ querystring=untrusted
+ db1.Order(untrusted) // $ querystring=untrusted
+ db1.Or(untrusted) // $ querystring=untrusted
+ db1.Select(untrusted) // $ querystring=untrusted
+ db1.Table(untrusted) // $ querystring=untrusted
+ db1.Group(untrusted) // $ querystring=untrusted
+ db1.Having(untrusted) // $ querystring=untrusted
+ db1.Joins(untrusted) // $ querystring=untrusted
+ db1.Exec(untrusted) // $ querystring=untrusted
+ db1.Pluck(untrusted, nil) // $ querystring=untrusted
db2 := gorm2.DB{}
- db2.Where(untrusted)
- db2.Raw(untrusted)
- db2.Not(untrusted)
- db2.Order(untrusted)
- db2.Or(untrusted)
- db2.Select(untrusted)
- db2.Table(untrusted)
- db2.Group(untrusted)
- db2.Having(untrusted)
- db2.Joins(untrusted)
- db2.Exec(untrusted)
- db2.Distinct(untrusted)
- db2.Pluck(untrusted, nil)
+ db2.Where(untrusted) // $ querystring=untrusted
+ db2.Raw(untrusted) // $ querystring=untrusted
+ db2.Not(untrusted) // $ querystring=untrusted
+ db2.Order(untrusted) // $ querystring=untrusted
+ db2.Or(untrusted) // $ querystring=untrusted
+ db2.Select(untrusted) // $ querystring=untrusted
+ db2.Table(untrusted) // $ querystring=untrusted
+ db2.Group(untrusted) // $ querystring=untrusted
+ db2.Having(untrusted) // $ querystring=untrusted
+ db2.Joins(untrusted) // $ querystring=untrusted
+ db2.Exec(untrusted) // $ querystring=untrusted
+ db2.Distinct(untrusted) // $ querystring=untrusted
+ db2.Pluck(untrusted, nil) // $ querystring=untrusted
}
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.ql
deleted file mode 100644
index e08b506deaf1..000000000000
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.ql
+++ /dev/null
@@ -1,5 +0,0 @@
-import go
-
-from SQL::QueryString qs, Method meth, string a, string b, string c
-where meth.hasQualifiedName(a, b, c) and qs = meth.getACall().getSyntacticArgument(0)
-select qs, a, b, c
diff --git a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractionTests.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/QueryString.expected
similarity index 57%
rename from java/ql/automodel/test/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractionTests.expected
rename to go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/QueryString.expected
index 8ec8033d086e..db33d6d2504a 100644
--- a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractionTests.expected
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/QueryString.expected
@@ -1,2 +1,3 @@
testFailures
+invalidModelRow
failures
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/QueryString.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/QueryString.ql
new file mode 100644
index 000000000000..eeb43a82fadd
--- /dev/null
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/QueryString.ql
@@ -0,0 +1,60 @@
+import go
+import semmle.go.dataflow.ExternalFlow
+import ModelValidation
+import TestUtilities.InlineExpectationsTest
+
+module SqlTest implements TestSig {
+ string getARelevantTag() { result = "query" }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "query" and
+ exists(SQL::Query q, SQL::QueryString qs | qs = q.getAQueryString() |
+ q.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
+ location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
+ element = q.toString() and
+ value = qs.toString()
+ )
+ }
+}
+
+module QueryString implements TestSig {
+ string getARelevantTag() { result = "querystring" }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "querystring" and
+ element = "" and
+ exists(SQL::QueryString qs | not exists(SQL::Query q | qs = q.getAQueryString()) |
+ qs.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
+ location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
+ value = qs.toString()
+ )
+ }
+}
+
+module Config implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLit }
+
+ predicate isSink(DataFlow::Node n) {
+ n = any(DataFlow::CallNode cn | cn.getTarget().getName() = "sink").getAnArgument()
+ }
+}
+
+module Flow = TaintTracking::Global;
+
+module TaintFlow implements TestSig {
+ string getARelevantTag() { result = "flowfrom" }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "flowfrom" and
+ element = "" and
+ exists(DataFlow::Node fromNode, DataFlow::Node toNode |
+ toNode
+ .hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
+ location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
+ Flow::flow(fromNode, toNode) and
+ value = fromNode.asExpr().(StringLit).getValue()
+ )
+ }
+}
+
+import MakeTest>
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.expected
deleted file mode 100644
index 0540a78fb343..000000000000
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.expected
+++ /dev/null
@@ -1,12 +0,0 @@
-| sqlx.go:15:17:15:25 | untrusted |
-| sqlx.go:16:14:16:22 | untrusted |
-| sqlx.go:17:14:17:22 | untrusted |
-| sqlx.go:18:12:18:20 | untrusted |
-| sqlx.go:19:15:19:23 | untrusted |
-| sqlx.go:20:16:20:24 | untrusted |
-| sqlx.go:23:17:23:25 | untrusted |
-| sqlx.go:24:14:24:22 | untrusted |
-| sqlx.go:25:14:25:22 | untrusted |
-| sqlx.go:26:12:26:20 | untrusted |
-| sqlx.go:27:15:27:23 | untrusted |
-| sqlx.go:28:16:28:24 | untrusted |
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.go
index edc29c4d4eef..e4e8d43395bd 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.go
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.go
@@ -12,19 +12,19 @@ func main() {
db := sqlx.DB{}
untrusted := getUntrustedString()
- db.Select(nil, untrusted)
- db.Get(nil, untrusted)
- db.MustExec(untrusted)
- db.Queryx(untrusted)
- db.NamedExec(untrusted, nil)
- db.NamedQuery(untrusted, nil)
+ db.Select(nil, untrusted) // $ querystring=untrusted
+ db.Get(nil, untrusted) // $ querystring=untrusted
+ db.MustExec(untrusted) // $ querystring=untrusted
+ db.Queryx(untrusted) // $ querystring=untrusted
+ db.NamedExec(untrusted, nil) // $ querystring=untrusted
+ db.NamedQuery(untrusted, nil) // $ querystring=untrusted
tx := sqlx.Tx{}
- tx.Select(nil, untrusted)
- tx.Get(nil, untrusted)
- tx.MustExec(untrusted)
- tx.Queryx(untrusted)
- tx.NamedExec(untrusted, nil)
- tx.NamedQuery(untrusted, nil)
+ tx.Select(nil, untrusted) // $ querystring=untrusted
+ tx.Get(nil, untrusted) // $ querystring=untrusted
+ tx.MustExec(untrusted) // $ querystring=untrusted
+ tx.Queryx(untrusted) // $ querystring=untrusted
+ tx.NamedExec(untrusted, nil) // $ querystring=untrusted
+ tx.NamedQuery(untrusted, nil) // $ querystring=untrusted
}
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.ql
deleted file mode 100644
index 7b56fd974419..000000000000
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.ql
+++ /dev/null
@@ -1,4 +0,0 @@
-import go
-
-from SQL::QueryString qs
-select qs
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/QueryString.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/QueryString.expected
new file mode 100644
index 000000000000..105b7026d0c4
--- /dev/null
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/QueryString.expected
@@ -0,0 +1,3 @@
+failures
+invalidModelRow
+testFailures
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/QueryString.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/QueryString.ql
new file mode 100644
index 000000000000..eeb43a82fadd
--- /dev/null
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/QueryString.ql
@@ -0,0 +1,60 @@
+import go
+import semmle.go.dataflow.ExternalFlow
+import ModelValidation
+import TestUtilities.InlineExpectationsTest
+
+module SqlTest implements TestSig {
+ string getARelevantTag() { result = "query" }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "query" and
+ exists(SQL::Query q, SQL::QueryString qs | qs = q.getAQueryString() |
+ q.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
+ location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
+ element = q.toString() and
+ value = qs.toString()
+ )
+ }
+}
+
+module QueryString implements TestSig {
+ string getARelevantTag() { result = "querystring" }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "querystring" and
+ element = "" and
+ exists(SQL::QueryString qs | not exists(SQL::Query q | qs = q.getAQueryString()) |
+ qs.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
+ location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
+ value = qs.toString()
+ )
+ }
+}
+
+module Config implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLit }
+
+ predicate isSink(DataFlow::Node n) {
+ n = any(DataFlow::CallNode cn | cn.getTarget().getName() = "sink").getAnArgument()
+ }
+}
+
+module Flow = TaintTracking::Global;
+
+module TaintFlow implements TestSig {
+ string getARelevantTag() { result = "flowfrom" }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "flowfrom" and
+ element = "" and
+ exists(DataFlow::Node fromNode, DataFlow::Node toNode |
+ toNode
+ .hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
+ location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
+ Flow::flow(fromNode, toNode) and
+ value = fromNode.asExpr().(StringLit).getValue()
+ )
+ }
+}
+
+import MakeTest>
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/bun.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/bun.go
index 8ce4e5b0826b..44a2e2c2fce4 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/bun.go
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/bun.go
@@ -22,28 +22,28 @@ func main() {
panic(err)
}
db := bun.NewDB(sqlite, sqlitedialect.New())
- bun.NewRawQuery(db, untrusted)
-
- db.ExecContext(ctx, untrusted)
- db.PrepareContext(ctx, untrusted)
- db.QueryContext(ctx, untrusted)
- db.QueryRowContext(ctx, untrusted)
-
- db.Exec(untrusted)
- db.NewRaw(untrusted)
- db.Prepare(untrusted)
- db.Query(untrusted)
- db.QueryRow(untrusted)
- db.Raw(untrusted)
-
- db.NewSelect().ColumnExpr(untrusted)
- db.NewSelect().DistinctOn(untrusted)
- db.NewSelect().For(untrusted)
- db.NewSelect().GroupExpr(untrusted)
- db.NewSelect().Having(untrusted)
- db.NewSelect().ModelTableExpr(untrusted)
- db.NewSelect().OrderExpr(untrusted)
- db.NewSelect().TableExpr(untrusted)
- db.NewSelect().Where(untrusted)
- db.NewSelect().WhereOr(untrusted)
+ bun.NewRawQuery(db, untrusted) // $ querystring=untrusted
+
+ db.ExecContext(ctx, untrusted) // $ querystring=untrusted
+ db.PrepareContext(ctx, untrusted) // $ querystring=untrusted
+ db.QueryContext(ctx, untrusted) // $ querystring=untrusted
+ db.QueryRowContext(ctx, untrusted) // $ querystring=untrusted
+
+ db.Exec(untrusted) // $ querystring=untrusted
+ db.NewRaw(untrusted) // $ querystring=untrusted
+ db.Prepare(untrusted) // $ querystring=untrusted
+ db.Query(untrusted) // $ querystring=untrusted
+ db.QueryRow(untrusted) // $ querystring=untrusted
+ db.Raw(untrusted) // $ querystring=untrusted
+
+ db.NewSelect().ColumnExpr(untrusted) // $ querystring=untrusted
+ db.NewSelect().DistinctOn(untrusted) // $ querystring=untrusted
+ db.NewSelect().For(untrusted) // $ querystring=untrusted
+ db.NewSelect().GroupExpr(untrusted) // $ querystring=untrusted
+ db.NewSelect().Having(untrusted) // $ querystring=untrusted
+ db.NewSelect().ModelTableExpr(untrusted) // $ querystring=untrusted
+ db.NewSelect().OrderExpr(untrusted) // $ querystring=untrusted
+ db.NewSelect().TableExpr(untrusted) // $ querystring=untrusted
+ db.NewSelect().Where(untrusted) // $ querystring=untrusted
+ db.NewSelect().WhereOr(untrusted) // $ querystring=untrusted
}
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/QueryString.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/QueryString.expected
new file mode 100644
index 000000000000..db33d6d2504a
--- /dev/null
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/QueryString.expected
@@ -0,0 +1,3 @@
+testFailures
+invalidModelRow
+failures
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/QueryString.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/QueryString.ql
new file mode 100644
index 000000000000..eeb43a82fadd
--- /dev/null
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/QueryString.ql
@@ -0,0 +1,60 @@
+import go
+import semmle.go.dataflow.ExternalFlow
+import ModelValidation
+import TestUtilities.InlineExpectationsTest
+
+module SqlTest implements TestSig {
+ string getARelevantTag() { result = "query" }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "query" and
+ exists(SQL::Query q, SQL::QueryString qs | qs = q.getAQueryString() |
+ q.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
+ location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
+ element = q.toString() and
+ value = qs.toString()
+ )
+ }
+}
+
+module QueryString implements TestSig {
+ string getARelevantTag() { result = "querystring" }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "querystring" and
+ element = "" and
+ exists(SQL::QueryString qs | not exists(SQL::Query q | qs = q.getAQueryString()) |
+ qs.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
+ location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
+ value = qs.toString()
+ )
+ }
+}
+
+module Config implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLit }
+
+ predicate isSink(DataFlow::Node n) {
+ n = any(DataFlow::CallNode cn | cn.getTarget().getName() = "sink").getAnArgument()
+ }
+}
+
+module Flow = TaintTracking::Global;
+
+module TaintFlow implements TestSig {
+ string getARelevantTag() { result = "flowfrom" }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "flowfrom" and
+ element = "" and
+ exists(DataFlow::Node fromNode, DataFlow::Node toNode |
+ toNode
+ .hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
+ location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
+ Flow::flow(fromNode, toNode) and
+ value = fromNode.asExpr().(StringLit).getValue()
+ )
+ }
+}
+
+import MakeTest>
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.expected
deleted file mode 100644
index f4e3e4f15b0a..000000000000
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.expected
+++ /dev/null
@@ -1,47 +0,0 @@
-| gogf.go:12:9:12:11 | sql |
-| gogf.go:13:11:13:13 | sql |
-| gogf.go:14:13:14:15 | sql |
-| gogf.go:15:13:15:15 | sql |
-| gogf.go:16:11:16:13 | sql |
-| gogf.go:17:13:17:15 | sql |
-| gogf.go:18:12:18:14 | sql |
-| gogf.go:19:10:19:12 | sql |
-| gogf.go:20:8:20:10 | sql |
-| gogf.go:21:17:21:19 | sql |
-| gogf.go:22:19:22:21 | sql |
-| gogf.go:23:20:23:22 | sql |
-| gogf.go:24:23:24:25 | sql |
-| gogf.go:25:21:25:23 | sql |
-| gogf.go:26:23:26:25 | sql |
-| gogf.go:27:22:27:24 | sql |
-| gogf.go:28:24:28:26 | sql |
-| gogf.go:32:9:32:11 | sql |
-| gogf.go:33:11:33:13 | sql |
-| gogf.go:34:13:34:15 | sql |
-| gogf.go:35:13:35:15 | sql |
-| gogf.go:36:11:36:13 | sql |
-| gogf.go:37:13:37:15 | sql |
-| gogf.go:38:12:38:14 | sql |
-| gogf.go:39:10:39:12 | sql |
-| gogf.go:40:8:40:10 | sql |
-| gogf.go:41:17:41:19 | sql |
-| gogf.go:42:23:42:25 | sql |
-| gogf.go:43:21:43:23 | sql |
-| gogf.go:44:23:44:25 | sql |
-| gogf.go:45:22:45:24 | sql |
-| gogf.go:46:24:46:26 | sql |
-| gogf.go:51:9:51:11 | sql |
-| gogf.go:52:11:52:13 | sql |
-| gogf.go:53:13:53:15 | sql |
-| gogf.go:54:13:54:15 | sql |
-| gogf.go:55:11:55:13 | sql |
-| gogf.go:56:13:56:15 | sql |
-| gogf.go:57:12:57:14 | sql |
-| gogf.go:58:10:58:12 | sql |
-| gogf.go:59:8:59:10 | sql |
-| gogf.go:60:17:60:19 | sql |
-| gogf.go:61:23:61:25 | sql |
-| gogf.go:62:21:62:23 | sql |
-| gogf.go:63:23:63:25 | sql |
-| gogf.go:64:22:64:24 | sql |
-| gogf.go:65:24:65:26 | sql |
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.go
index d0eceaf862cf..e2db8016cf0d 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.go
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.go
@@ -4,11 +4,13 @@ package main
//go:generate depstubber -vendor github.com/gogf/gf/database/gdb DB,Core,TX ""
import (
+ "context"
+
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/frame/g"
)
-func gogfCoreTest(sql string, c *gdb.Core) {
+func gogfCoreTest(sql string, c *gdb.Core, ctx context.Context) {
c.Exec(sql, nil) // $ querystring=sql
c.GetAll(sql, nil) // $ querystring=sql
c.GetArray(sql, nil) // $ querystring=sql
@@ -21,14 +23,14 @@ func gogfCoreTest(sql string, c *gdb.Core) {
c.GetScan(nil, sql, nil) // $ querystring=sql
c.GetStruct(nil, sql, nil) // $ querystring=sql
c.GetStructs(nil, sql, nil) // $ querystring=sql
- c.DoCommit(nil, nil, sql, nil) // $ querystring=sql
- c.DoExec(nil, nil, sql, nil) // $ querystring=sql
- c.DoGetAll(nil, nil, sql, nil) // $ querystring=sql
- c.DoQuery(nil, nil, sql, nil) // $ querystring=sql
- c.DoPrepare(nil, nil, sql) // $ querystring=sql
+ c.DoCommit(ctx, nil, sql, nil) // $ querystring=sql
+ c.DoExec(ctx, nil, sql, nil) // $ querystring=sql
+ c.DoGetAll(ctx, nil, sql, nil) // $ querystring=sql
+ c.DoQuery(ctx, nil, sql, nil) // $ querystring=sql
+ c.DoPrepare(ctx, nil, sql) // $ querystring=sql
}
-func gogfDbtest(sql string, c gdb.DB) {
+func gogfDbtest(sql string, c gdb.DB, ctx context.Context) {
c.Exec(sql, nil) // $ querystring=sql
c.GetAll(sql, nil) // $ querystring=sql
c.GetArray(sql, nil) // $ querystring=sql
@@ -39,14 +41,14 @@ func gogfDbtest(sql string, c gdb.DB) {
c.Query(sql, nil) // $ querystring=sql
c.Raw(sql, nil) // $ querystring=sql
c.GetScan(nil, sql, nil) // $ querystring=sql
- c.DoCommit(nil, nil, sql, nil) // $ querystring=sql
- c.DoExec(nil, nil, sql, nil) // $ querystring=sql
- c.DoGetAll(nil, nil, sql, nil) // $ querystring=sql
- c.DoQuery(nil, nil, sql, nil) // $ querystring=sql
- c.DoPrepare(nil, nil, sql) // $ querystring=sql
+ c.DoCommit(ctx, nil, sql, nil) // $ querystring=sql
+ c.DoExec(ctx, nil, sql, nil) // $ querystring=sql
+ c.DoGetAll(ctx, nil, sql, nil) // $ querystring=sql
+ c.DoQuery(ctx, nil, sql, nil) // $ querystring=sql
+ c.DoPrepare(ctx, nil, sql) // $ querystring=sql
}
-func gogfGTest(sql string) {
+func gogfGTest(sql string, ctx context.Context) {
c := g.DB("ad")
c.Exec(sql, nil) // $ querystring=sql
c.GetAll(sql, nil) // $ querystring=sql
@@ -58,11 +60,11 @@ func gogfGTest(sql string) {
c.Query(sql, nil) // $ querystring=sql
c.Raw(sql, nil) // $ querystring=sql
c.GetScan(nil, sql, nil) // $ querystring=sql
- c.DoCommit(nil, nil, sql, nil) // $ querystring=sql
- c.DoExec(nil, nil, sql, nil) // $ querystring=sql
- c.DoGetAll(nil, nil, sql, nil) // $ querystring=sql
- c.DoQuery(nil, nil, sql, nil) // $ querystring=sql
- c.DoPrepare(nil, nil, sql) // $ querystring=sql
+ c.DoCommit(ctx, nil, sql, nil) // $ querystring=sql
+ c.DoExec(ctx, nil, sql, nil) // $ querystring=sql
+ c.DoGetAll(ctx, nil, sql, nil) // $ querystring=sql
+ c.DoQuery(ctx, nil, sql, nil) // $ querystring=sql
+ c.DoPrepare(ctx, nil, sql) // $ querystring=sql
}
func main() {
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.ql
deleted file mode 100644
index 7b56fd974419..000000000000
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.ql
+++ /dev/null
@@ -1,4 +0,0 @@
-import go
-
-from SQL::QueryString qs
-select qs
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/QueryString.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/QueryString.expected
new file mode 100644
index 000000000000..db33d6d2504a
--- /dev/null
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/QueryString.expected
@@ -0,0 +1,3 @@
+testFailures
+invalidModelRow
+failures
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/QueryString.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/QueryString.ql
new file mode 100644
index 000000000000..eeb43a82fadd
--- /dev/null
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/QueryString.ql
@@ -0,0 +1,60 @@
+import go
+import semmle.go.dataflow.ExternalFlow
+import ModelValidation
+import TestUtilities.InlineExpectationsTest
+
+module SqlTest implements TestSig {
+ string getARelevantTag() { result = "query" }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "query" and
+ exists(SQL::Query q, SQL::QueryString qs | qs = q.getAQueryString() |
+ q.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
+ location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
+ element = q.toString() and
+ value = qs.toString()
+ )
+ }
+}
+
+module QueryString implements TestSig {
+ string getARelevantTag() { result = "querystring" }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "querystring" and
+ element = "" and
+ exists(SQL::QueryString qs | not exists(SQL::Query q | qs = q.getAQueryString()) |
+ qs.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
+ location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
+ value = qs.toString()
+ )
+ }
+}
+
+module Config implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLit }
+
+ predicate isSink(DataFlow::Node n) {
+ n = any(DataFlow::CallNode cn | cn.getTarget().getName() = "sink").getAnArgument()
+ }
+}
+
+module Flow = TaintTracking::Global;
+
+module TaintFlow implements TestSig {
+ string getARelevantTag() { result = "flowfrom" }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "flowfrom" and
+ element = "" and
+ exists(DataFlow::Node fromNode, DataFlow::Node toNode |
+ toNode
+ .hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
+ location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
+ Flow::flow(fromNode, toNode) and
+ value = fromNode.asExpr().(StringLit).getValue()
+ )
+ }
+}
+
+import MakeTest>
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/go.mod b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/go.mod
index 1f243775658a..826ed0eb1c05 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/go.mod
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/go.mod
@@ -2,4 +2,4 @@ module main
go 1.18
-require github.com/rqlite/gorqlite v0.0.0-20220528150909-c4e99ae96be6
+require github.com/rqlite/gorqlite v0.0.0-20240808172217-12ae7d03ef19
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.expected
deleted file mode 100644
index cbd8166ea5e1..000000000000
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.expected
+++ /dev/null
@@ -1,6 +0,0 @@
-| gorqlite.go:11:13:11:16 | sqls |
-| gorqlite.go:12:13:12:16 | sqls |
-| gorqlite.go:13:13:13:16 | sqls |
-| gorqlite.go:14:16:14:18 | sql |
-| gorqlite.go:15:16:15:18 | sql |
-| gorqlite.go:16:16:16:18 | sql |
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.go
index 9b60c6684e67..ebd6e5fd9f3b 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.go
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.go
@@ -1,20 +1,49 @@
package main
-//go:generate depstubber -vendor github.com/rqlite/gorqlite Connection Open
+//go:generate depstubber -vendor github.com/rqlite/gorqlite Connection,ParameterizedStatement Open
import (
+ "context"
+
"github.com/rqlite/gorqlite"
)
-func gorqlitetest(sql string, sqls []string) {
+func gorqlitetest(sql string, sqls []string, param_sql gorqlite.ParameterizedStatement, param_sqls []gorqlite.ParameterizedStatement, ctx context.Context) {
conn, _ := gorqlite.Open("dbUrl")
- conn.Query(sqls) // $ querystring=sqls
- conn.Queue(sqls) // $ querystring=sqls
- conn.Write(sqls) // $ querystring=sqls
+
+ conn.Query(sqls) // $ querystring=sqls
+ conn.Queue(sqls) // $ querystring=sqls
+ conn.Write(sqls) // $ querystring=sqls
+
conn.QueryOne(sql) // $ querystring=sql
conn.QueueOne(sql) // $ querystring=sql
conn.WriteOne(sql) // $ querystring=sql
+
+ conn.QueryParameterized(param_sqls) // $ querystring=param_sqls
+ conn.QueueParameterized(param_sqls) // $ querystring=param_sqls
+ conn.WriteParameterized(param_sqls) // $ querystring=param_sqls
+
+ conn.QueryOneParameterized(param_sql) // $ querystring=param_sql
+ conn.QueueOneParameterized(param_sql) // $ querystring=param_sql
+ conn.WriteOneParameterized(param_sql) // $ querystring=param_sql
+
+ conn.QueryContext(ctx, sqls) // $ querystring=sqls
+ conn.QueueContext(ctx, sqls) // $ querystring=sqls
+ conn.WriteContext(ctx, sqls) // $ querystring=sqls
+
+ conn.QueryOneContext(ctx, sql) // $ querystring=sql
+ conn.QueueOneContext(ctx, sql) // $ querystring=sql
+ conn.WriteOneContext(ctx, sql) // $ querystring=sql
+
+ conn.QueryParameterizedContext(ctx, param_sqls) // $ querystring=param_sqls
+ conn.QueueParameterizedContext(ctx, param_sqls) // $ querystring=param_sqls
+ conn.WriteParameterizedContext(ctx, param_sqls) // $ querystring=param_sqls
+
+ conn.QueryOneParameterizedContext(ctx, param_sql) // $ querystring=param_sql
+ conn.QueueOneParameterizedContext(ctx, param_sql) // $ querystring=param_sql
+ conn.WriteOneParameterizedContext(ctx, param_sql) // $ querystring=param_sql
}
+
func main() {
return
}
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.ql
deleted file mode 100644
index 7b56fd974419..000000000000
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.ql
+++ /dev/null
@@ -1,4 +0,0 @@
-import go
-
-from SQL::QueryString qs
-select qs
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/vendor/github.com/rqlite/gorqlite/stub.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/vendor/github.com/rqlite/gorqlite/stub.go
index f6f4ca18ec12..0572097582e0 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/vendor/github.com/rqlite/gorqlite/stub.go
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/vendor/github.com/rqlite/gorqlite/stub.go
@@ -2,11 +2,15 @@
// This is a simple stub for github.com/rqlite/gorqlite, strictly for use in testing.
// See the LICENSE file for information about the licensing of the original library.
-// Source: github.com/rqlite/gorqlite (exports: Connection; functions: Open)
+// Source: github.com/rqlite/gorqlite (exports: Connection,ParameterizedStatement; functions: Open)
// Package gorqlite is a stub of github.com/rqlite/gorqlite, generated by depstubber.
package gorqlite
+import (
+ context "context"
+)
+
type Connection struct {
ID string
}
@@ -29,19 +33,83 @@ func (_ *Connection) Query(_ []string) ([]QueryResult, error) {
return nil, nil
}
+func (_ *Connection) QueryContext(_ context.Context, _ []string) ([]QueryResult, error) {
+ return nil, nil
+}
+
func (_ *Connection) QueryOne(_ string) (QueryResult, error) {
return QueryResult{}, nil
}
+func (_ *Connection) QueryOneContext(_ context.Context, _ string) (QueryResult, error) {
+ return QueryResult{}, nil
+}
+
+func (_ *Connection) QueryOneParameterized(_ ParameterizedStatement) (QueryResult, error) {
+ return QueryResult{}, nil
+}
+
+func (_ *Connection) QueryOneParameterizedContext(_ context.Context, _ ParameterizedStatement) (QueryResult, error) {
+ return QueryResult{}, nil
+}
+
+func (_ *Connection) QueryParameterized(_ []ParameterizedStatement) ([]QueryResult, error) {
+ return nil, nil
+}
+
+func (_ *Connection) QueryParameterizedContext(_ context.Context, _ []ParameterizedStatement) ([]QueryResult, error) {
+ return nil, nil
+}
+
func (_ *Connection) Queue(_ []string) (int64, error) {
return 0, nil
}
+func (_ *Connection) QueueContext(_ context.Context, _ []string) (int64, error) {
+ return 0, nil
+}
+
func (_ *Connection) QueueOne(_ string) (int64, error) {
return 0, nil
}
-func (_ *Connection) SetConsistencyLevel(_ string) error {
+func (_ *Connection) QueueOneContext(_ context.Context, _ string) (int64, error) {
+ return 0, nil
+}
+
+func (_ *Connection) QueueOneParameterized(_ ParameterizedStatement) (int64, error) {
+ return 0, nil
+}
+
+func (_ *Connection) QueueOneParameterizedContext(_ context.Context, _ ParameterizedStatement) (int64, error) {
+ return 0, nil
+}
+
+func (_ *Connection) QueueParameterized(_ []ParameterizedStatement) (int64, error) {
+ return 0, nil
+}
+
+func (_ *Connection) QueueParameterizedContext(_ context.Context, _ []ParameterizedStatement) (int64, error) {
+ return 0, nil
+}
+
+func (_ *Connection) Request(_ []string) ([]RequestResult, error) {
+ return nil, nil
+}
+
+func (_ *Connection) RequestContext(_ context.Context, _ []string) ([]RequestResult, error) {
+ return nil, nil
+}
+
+func (_ *Connection) RequestParameterized(_ []ParameterizedStatement) ([]RequestResult, error) {
+ return nil, nil
+}
+
+func (_ *Connection) RequestParameterizedContext(_ context.Context, _ []ParameterizedStatement) ([]RequestResult, error) {
+ return nil, nil
+}
+
+func (_ *Connection) SetConsistencyLevel(_ interface{}) error {
return nil
}
@@ -53,12 +121,41 @@ func (_ *Connection) Write(_ []string) ([]WriteResult, error) {
return nil, nil
}
+func (_ *Connection) WriteContext(_ context.Context, _ []string) ([]WriteResult, error) {
+ return nil, nil
+}
+
func (_ *Connection) WriteOne(_ string) (WriteResult, error) {
return WriteResult{}, nil
}
-func Open(_ string) (Connection, error) {
- return Connection{}, nil
+func (_ *Connection) WriteOneContext(_ context.Context, _ string) (WriteResult, error) {
+ return WriteResult{}, nil
+}
+
+func (_ *Connection) WriteOneParameterized(_ ParameterizedStatement) (WriteResult, error) {
+ return WriteResult{}, nil
+}
+
+func (_ *Connection) WriteOneParameterizedContext(_ context.Context, _ ParameterizedStatement) (WriteResult, error) {
+ return WriteResult{}, nil
+}
+
+func (_ *Connection) WriteParameterized(_ []ParameterizedStatement) ([]WriteResult, error) {
+ return nil, nil
+}
+
+func (_ *Connection) WriteParameterizedContext(_ context.Context, _ []ParameterizedStatement) ([]WriteResult, error) {
+ return nil, nil
+}
+
+func Open(_ string) (*Connection, error) {
+ return nil, nil
+}
+
+type ParameterizedStatement struct {
+ Query string
+ Arguments []interface{}
}
type QueryResult struct {
@@ -90,10 +187,20 @@ func (_ *QueryResult) Scan(_ ...interface{}) error {
return nil
}
+func (_ *QueryResult) Slice() ([]interface{}, error) {
+ return nil, nil
+}
+
func (_ *QueryResult) Types() []string {
return nil
}
+type RequestResult struct {
+ Err error
+ Query *QueryResult
+ Write *WriteResult
+}
+
type WriteResult struct {
Err error
Timing float64
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/vendor/modules.txt b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/vendor/modules.txt
index f5e5b9989ede..dafb7c6d1a98 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/vendor/modules.txt
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/vendor/modules.txt
@@ -1,3 +1,3 @@
-# github.com/rqlite/gorqlite v0.0.0-20220528150909-c4e99ae96be6
+# github.com/rqlite/gorqlite v0.0.0-20240808172217-12ae7d03ef19
## explicit
github.com/rqlite/gorqlite
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/squirrel.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/squirrel.go
index 15b687c7ad16..d0350643bb06 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/squirrel.go
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/squirrel.go
@@ -1,6 +1,6 @@
package main
-//go:generate depstubber -vendor github.com/Masterminds/squirrel DeleteBuilder,InsertBuilder,SelectBuilder,UpdateBuilder Delete,Expr,Insert,Select,Update
+//go:generate depstubber -vendor github.com/Masterminds/squirrel DeleteBuilder,Eq,InsertBuilder,SelectBuilder,UpdateBuilder Delete,Expr,Insert,Select,Update
import (
"github.com/Masterminds/squirrel"
@@ -10,38 +10,44 @@ func squirrelTest(querypart string) {
squirrel.Expr(querypart) // $ querystring=querypart
deleteBuilder := squirrel.Delete(querypart) // $ querystring=querypart
deleteBuilder.From(querypart) // $ querystring=querypart
- deleteBuilder.OrderBy(querypart) // $ querystring=[]type{args}
+ deleteBuilder.OrderBy(querypart) // $ querystring=querypart
deleteBuilder.Prefix(querypart) // $ querystring=querypart
deleteBuilder.Suffix(querypart) // $ querystring=querypart
deleteBuilder.Where(querypart) // $ querystring=querypart
insertBuilder := squirrel.Insert(querypart) // $ querystring=querypart
- insertBuilder.Columns(querypart) // $ querystring=[]type{args}
- insertBuilder.Options(querypart) // $ querystring=[]type{args}
+ insertBuilder.Columns(querypart) // $ querystring=querypart
+ insertBuilder.Options(querypart) // $ querystring=querypart
insertBuilder.Prefix(querypart) // $ querystring=querypart
insertBuilder.Suffix(querypart) // $ querystring=querypart
insertBuilder.Into(querypart) // $ querystring=querypart
- selectBuilder := squirrel.Select(querypart) // $ querystring=[]type{args}
- selectBuilder.Columns(querypart) // $ querystring=[]type{args}
+ selectBuilder := squirrel.Select(querypart) // $ querystring=querypart
+ selectBuilder.Columns(querypart) // $ querystring=querypart
selectBuilder.From(querypart) // $ querystring=querypart
- selectBuilder.Options(querypart) // $ querystring=[]type{args}
- selectBuilder.OrderBy(querypart) // $ querystring=[]type{args}
+ selectBuilder.Options(querypart) // $ querystring=querypart
+ selectBuilder.OrderBy(querypart) // $ querystring=querypart
selectBuilder.Prefix(querypart) // $ querystring=querypart
selectBuilder.Suffix(querypart) // $ querystring=querypart
selectBuilder.Where(querypart) // $ querystring=querypart
selectBuilder.CrossJoin(querypart) // $ querystring=querypart
- selectBuilder.GroupBy(querypart) // $ querystring=[]type{args}
+ selectBuilder.GroupBy(querypart) // $ querystring=querypart
selectBuilder.InnerJoin(querypart) // $ querystring=querypart
selectBuilder.LeftJoin(querypart) // $ querystring=querypart
selectBuilder.RightJoin(querypart) // $ querystring=querypart
updateBuilder := squirrel.Update(querypart) // $ querystring=querypart
updateBuilder.From(querypart) // $ querystring=querypart
- updateBuilder.OrderBy(querypart) // $ querystring=[]type{args}
+ updateBuilder.OrderBy(querypart) // $ querystring=querypart
updateBuilder.Prefix(querypart) // $ querystring=querypart
updateBuilder.Suffix(querypart) // $ querystring=querypart
updateBuilder.Where(querypart) // $ querystring=querypart
updateBuilder.Set(querypart, "") // $ querystring=querypart
updateBuilder.Table(querypart) // $ querystring=querypart
+
+ // safe
+ wrapped := squirrel.Eq{"id": querypart}
+ deleteBuilder.Where(wrapped)
+ selectBuilder.Where(wrapped)
+ updateBuilder.Where(wrapped)
}
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/Masterminds/squirrel/stub.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/Masterminds/squirrel/stub.go
index 96a30e49d4ec..1a9e5ca0e9b9 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/Masterminds/squirrel/stub.go
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/Masterminds/squirrel/stub.go
@@ -2,7 +2,7 @@
// This is a simple stub for github.com/Masterminds/squirrel, strictly for use in testing.
// See the LICENSE file for information about the licensing of the original library.
-// Source: github.com/Masterminds/squirrel (exports: DeleteBuilder,InsertBuilder,SelectBuilder,UpdateBuilder; functions: Delete,Expr,Insert,Select,Update)
+// Source: github.com/Masterminds/squirrel (exports: DeleteBuilder,Eq,InsertBuilder,SelectBuilder,UpdateBuilder; functions: Delete,Expr,Insert,Select,Update)
// Package squirrel is a stub of github.com/Masterminds/squirrel, generated by depstubber.
package squirrel
@@ -99,6 +99,12 @@ func (_ DeleteBuilder) Where(_ interface{}, _ ...interface{}) DeleteBuilder {
return DeleteBuilder{}
}
+type Eq map[string]interface{}
+
+func (_ Eq) ToSql() (string, []interface{}, error) {
+ return "", nil, nil
+}
+
func Expr(_ string, _ ...interface{}) Sqlizer {
return nil
}
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/xorm.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/xorm.go
index 6b4dbb116eec..f63b33a0e09b 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/xorm.go
+++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/xorm.go
@@ -10,68 +10,73 @@ import (
func xormtest() {
query := "UntrustedString"
+ arg := "arg"
engine1 := xorm1.Engine{}
- engine1.Query(query) // $ querystring=query
- engine1.QueryString(query) // $ querystring=query
- engine1.QueryInterface(query) // $ querystring=query
- engine1.SQL(query) // $ querystring=query
- engine1.Where(query) // $ querystring=query
- engine1.Alias(query) // $ querystring=query
- engine1.NotIn(query) // $ querystring=query
- engine1.In(query) // $ querystring=query
- engine1.Select(query) // $ querystring=query
- engine1.SetExpr(query, nil) // $ querystring=query
- engine1.OrderBy(query) // $ querystring=query
- engine1.Having(query) // $ querystring=query
- engine1.GroupBy(query) // $ querystring=query
+ engine1.Query(query, arg) // $ querystring=query
+ engine1.Exec(query, arg) // $ querystring=query
+ engine1.QueryString(query, arg) // $ querystring=query
+ engine1.QueryInterface(query, arg) // $ querystring=query
+ engine1.SQL(query) // $ querystring=query
+ engine1.Where(query) // $ querystring=query
+ engine1.Alias(query) // $ querystring=query
+ engine1.NotIn(query) // $ querystring=query
+ engine1.In(query) // $ querystring=query
+ engine1.Select(query) // $ querystring=query
+ engine1.SetExpr(query, nil) // $ querystring=query
+ engine1.OrderBy(query) // $ querystring=query
+ engine1.Having(query) // $ querystring=query
+ engine1.GroupBy(query) // $ querystring=query
engine2 := xorm2.Engine{}
- engine2.Query(query) // $ querystring=query
- engine2.QueryString(query) // $ querystring=query
- engine2.QueryInterface(query) // $ querystring=query
- engine2.SQL(query) // $ querystring=query
- engine2.Where(query) // $ querystring=query
- engine2.Alias(query) // $ querystring=query
- engine2.NotIn(query) // $ querystring=query
- engine2.In(query) // $ querystring=query
- engine2.Select(query) // $ querystring=query
- engine2.SetExpr(query, nil) // $ querystring=query
- engine2.OrderBy(query) // $ querystring=query
- engine2.Having(query) // $ querystring=query
- engine2.GroupBy(query) // $ querystring=query
+ engine2.Query(query, arg) // $ querystring=query
+ engine2.Exec(query, arg) // $ querystring=query
+ engine2.QueryString(query, arg) // $ querystring=query
+ engine2.QueryInterface(query, arg) // $ querystring=query
+ engine2.SQL(query) // $ querystring=query
+ engine2.Where(query) // $ querystring=query
+ engine2.Alias(query) // $ querystring=query
+ engine2.NotIn(query) // $ querystring=query
+ engine2.In(query) // $ querystring=query
+ engine2.Select(query) // $ querystring=query
+ engine2.SetExpr(query, nil) // $ querystring=query
+ engine2.OrderBy(query) // $ querystring=query
+ engine2.Having(query) // $ querystring=query
+ engine2.GroupBy(query) // $ querystring=query
session1 := xorm1.Session{}
- session1.Query(query) // $ querystring=query
- session1.QueryString(query) // $ querystring=query
- session1.QueryInterface(query) // $ querystring=query
- session1.SQL(query) // $ querystring=query
- session1.Where(query) // $ querystring=query
- session1.Alias(query) // $ querystring=query
- session1.NotIn(query) // $ querystring=query
- session1.In(query) // $ querystring=query
- session1.Select(query) // $ querystring=query
- session1.SetExpr(query, nil) // $ querystring=query
- session1.OrderBy(query) // $ querystring=query
- session1.Having(query) // $ querystring=query
- session1.GroupBy(query) // $ querystring=query
- session1.And(query) // $ querystring=query
- session1.Or(query) // $ querystring=query
+ session1.Query(query, arg) // $ querystring=query
+ session1.Exec(query, arg) // $ querystring=query
+ session1.QueryString(query, arg) // $ querystring=query
+ session1.QueryInterface(query, arg) // $ querystring=query
+ session1.SQL(query) // $ querystring=query
+ session1.Where(query) // $ querystring=query
+ session1.Alias(query) // $ querystring=query
+ session1.NotIn(query) // $ querystring=query
+ session1.In(query) // $ querystring=query
+ session1.Select(query) // $ querystring=query
+ session1.SetExpr(query, nil) // $ querystring=query
+ session1.OrderBy(query) // $ querystring=query
+ session1.Having(query) // $ querystring=query
+ session1.GroupBy(query) // $ querystring=query
+ session1.And(query) // $ querystring=query
+ session1.Or(query) // $ querystring=query
session2 := xorm2.Session{}
- session2.Query(query) // $ querystring=query
- session2.QueryString(query) // $ querystring=query
- session2.QueryInterface(query) // $ querystring=query
- session2.SQL(query) // $ querystring=query
- session2.Where(query) // $ querystring=query
- session2.Alias(query) // $ querystring=query
- session2.NotIn(query) // $ querystring=query
- session2.In(query) // $ querystring=query
- session2.Select(query) // $ querystring=query
- session2.SetExpr(query, nil) // $ querystring=query
- session2.OrderBy(query) // $ querystring=query
- session2.Having(query) // $ querystring=query
- session2.GroupBy(query) // $ querystring=query
- session2.And(query) // $ querystring=query
- session2.Or(query) // $ querystring=query
+ session2.Query(query, arg) // $ querystring=query
+ session2.Exec(query, arg) // $ querystring=query
+ session2.QueryString(query, arg) // $ querystring=query
+ session2.QueryInterface(query, arg) // $ querystring=query
+ session2.SQL(query) // $ querystring=query
+ session2.Where(query) // $ querystring=query
+ session2.Alias(query) // $ querystring=query
+ session2.NotIn(query) // $ querystring=query
+ session2.In(query) // $ querystring=query
+ session2.Select(query) // $ querystring=query
+ session2.SetExpr(query, nil) // $ querystring=query
+ session2.OrderBy(query) // $ querystring=query
+ session2.Having(query) // $ querystring=query
+ session2.GroupBy(query) // $ querystring=query
+ session2.And(query) // $ querystring=query
+ session2.Or(query) // $ querystring=query
}
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/Slices.go b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/Slices.go
new file mode 100644
index 000000000000..6ecc98c0a2d3
--- /dev/null
+++ b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/Slices.go
@@ -0,0 +1,193 @@
+package main
+
+import (
+ "cmp"
+ "slices"
+ "strings"
+)
+
+func TaintStepTest_SlicesClip(fromStringSlice []string) []string {
+ toStringSlice := slices.Clip(fromStringSlice)
+ return toStringSlice
+}
+
+func TaintStepTest_SlicesClone(fromStringSlice []string) []string {
+ toStringSlice := slices.Clone(fromStringSlice)
+ return toStringSlice
+}
+
+func TaintStepTest_SlicesCompact(fromStringSlice []string) []string {
+ toStringSlice := slices.Compact(fromStringSlice)
+ return toStringSlice
+}
+
+func TaintStepTest_SlicesCompactFunc(fromStringSlice []string) []string {
+ toStringSlice := slices.CompactFunc(fromStringSlice, strings.EqualFold)
+ return toStringSlice
+}
+
+func TaintStepTest_SlicesConcat0(fromStringSlice []string) []string {
+ toStringSlice := slices.Concat(fromStringSlice, []string{"a", "b", "c"})
+ return toStringSlice
+}
+
+func TaintStepTest_SlicesConcat1(fromStringSlice []string) []string {
+ toStringSlice := slices.Concat([]string{"a", "b", "c"}, fromStringSlice)
+ return toStringSlice
+}
+
+func TaintStepTest_SlicesDelete(fromStringSlice []string) []string {
+ toStringSlice := slices.Delete(fromStringSlice, 0, 1)
+ return toStringSlice
+}
+
+func TaintStepTest_SlicesDeleteFunc(fromStringSlice []string) []string {
+ deleteEmptyString := func(str string) bool {
+ return str == ""
+ }
+ toStringSlice := slices.DeleteFunc(fromStringSlice, deleteEmptyString)
+ return toStringSlice
+}
+
+func TaintStepTest_SlicesGrow(fromStringSlice []string) []string {
+ toStringSlice := slices.Grow(fromStringSlice, 1)
+ return toStringSlice
+}
+
+func TaintStepTest_SlicesInsert0(fromStringSlice []string) []string {
+ toStringSlice := slices.Insert(fromStringSlice, 1, "a", "b")
+ return toStringSlice
+}
+
+func TaintStepTest_SlicesInsert2(fromString string) []string {
+ toStringSlice := slices.Insert([]string{}, 0, fromString, "b")
+ return toStringSlice
+}
+
+func TaintStepTest_SlicesMax(fromStringSlice []string) string {
+ toString := slices.Max(fromStringSlice)
+ return toString
+}
+
+func TaintStepTest_SlicesMaxFunc(fromStringSlice []string) string {
+ toString := slices.MaxFunc(fromStringSlice, cmp.Compare)
+ return toString
+}
+
+func TaintStepTest_SlicesMin(fromStringSlice []string) string {
+ toString := slices.Min(fromStringSlice)
+ return toString
+}
+
+func TaintStepTest_SlicesMinFunc(fromStringSlice []string) string {
+ toString := slices.MinFunc(fromStringSlice, cmp.Compare)
+ return toString
+}
+
+func TaintStepTest_SlicesRepeat(fromStringSlice []string) []string {
+ toStringSlice := slices.Repeat(fromStringSlice, 2)
+ return toStringSlice
+}
+
+func TaintStepTest_SlicesReplace0(fromStringSlice []string) []string {
+ toStringSlice := slices.Replace(fromStringSlice, 1, 2, "a")
+ return toStringSlice
+}
+
+func TaintStepTest_SlicesReplace3(fromString string) []string {
+ toStringSlice := slices.Replace([]string{}, 1, 3, fromString, "b")
+ return toStringSlice
+}
+
+func RunAllTaints_Slices() {
+ {
+ source := []string{newSource(0).(string)}
+ out := TaintStepTest_SlicesClip(source)
+ sink(0, out[0])
+ }
+ {
+ source := []string{newSource(1).(string)}
+ out := TaintStepTest_SlicesClone(source)
+ sink(1, out[0])
+ }
+ {
+ source := []string{newSource(2).(string)}
+ out := TaintStepTest_SlicesCompact(source)
+ sink(2, out[0])
+ }
+ {
+ source := []string{newSource(3).(string)}
+ out := TaintStepTest_SlicesCompactFunc(source)
+ sink(3, out[0])
+ }
+ {
+ source := []string{newSource(4).(string)}
+ out := TaintStepTest_SlicesConcat0(source)
+ sink(4, out[0])
+ }
+ {
+ source := []string{newSource(5).(string)}
+ out := TaintStepTest_SlicesConcat1(source)
+ sink(5, out[0])
+ }
+ {
+ source := []string{newSource(6).(string)}
+ out := TaintStepTest_SlicesDelete(source)
+ sink(6, out[0])
+ }
+ {
+ source := []string{newSource(7).(string)}
+ out := TaintStepTest_SlicesDeleteFunc(source)
+ sink(7, out[0])
+ }
+ {
+ source := []string{newSource(8).(string)}
+ out := TaintStepTest_SlicesGrow(source)
+ sink(8, out[0])
+ }
+ {
+ source := []string{newSource(9).(string)}
+ out := TaintStepTest_SlicesInsert0(source)
+ sink(9, out[0])
+ }
+ {
+ source := newSource(10).(string)
+ out := TaintStepTest_SlicesInsert2(source)
+ sink(10, out[0])
+ }
+ {
+ source := []string{newSource(11).(string)}
+ out := TaintStepTest_SlicesMax(source)
+ sink(11, out)
+ }
+ {
+ source := []string{newSource(12).(string)}
+ out := TaintStepTest_SlicesMaxFunc(source)
+ sink(12, out)
+ }
+ {
+ source := []string{newSource(13).(string)}
+ out := TaintStepTest_SlicesMin(source)
+ sink(13, out)
+ }
+ {
+ source := []string{newSource(14).(string)}
+ out := TaintStepTest_SlicesMinFunc(source)
+ sink(14, out)
+ }
+ {
+ source := []string{newSource(15).(string)}
+ out := TaintStepTest_SlicesRepeat(source)
+ sink(15, out[0])
+ }
+ {
+ source := []string{newSource(16).(string)}
+ out := TaintStepTest_SlicesReplace0(source)
+ sink(16, out[0])
+ }
+ {
+ source := newSource(17).(string)
+ out := TaintStepTest_SlicesReplace3(source)
+ sink(17, out[0])
+ }
+}
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/go.mod b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/go.mod
index 1481ec9caf63..4168c0a398b9 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/go.mod
+++ b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/go.mod
@@ -1,6 +1,6 @@
module example.com/m
-go 1.20
+go 1.23
require (
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/vendor/modules.txt b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/vendor/modules.txt
index fe5007e8ae12..b11be0ff1d24 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/vendor/modules.txt
+++ b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/vendor/modules.txt
@@ -1,3 +1,3 @@
# golang.org/x/net v0.0.0-20201010224723-4f7140c49acb
-## explicit
-golang.org/x/net
+## explicit; go 1.11
+golang.org/x/net/context
diff --git a/go/ql/test/library-tests/semmle/go/frameworks/XNetHtml/SqlInjection.expected b/go/ql/test/library-tests/semmle/go/frameworks/XNetHtml/SqlInjection.expected
index c28b1058e7c7..8b2f05c297f8 100644
--- a/go/ql/test/library-tests/semmle/go/frameworks/XNetHtml/SqlInjection.expected
+++ b/go/ql/test/library-tests/semmle/go/frameworks/XNetHtml/SqlInjection.expected
@@ -1,11 +1,12 @@
#select
| test.go:57:11:57:41 | call to EscapeString | test.go:56:2:56:42 | ... := ...[0] | test.go:57:11:57:41 | call to EscapeString | This query depends on a $@. | test.go:56:2:56:42 | ... := ...[0] | user-provided value |
edges
-| test.go:56:2:56:42 | ... := ...[0] | test.go:57:29:57:40 | selection of Value | provenance | Src:MaD:1 |
-| test.go:57:29:57:40 | selection of Value | test.go:57:11:57:41 | call to EscapeString | provenance | MaD:2 |
+| test.go:56:2:56:42 | ... := ...[0] | test.go:57:29:57:40 | selection of Value | provenance | Src:MaD:2 |
+| test.go:57:29:57:40 | selection of Value | test.go:57:11:57:41 | call to EscapeString | provenance | MaD:3 Sink:MaD:1 |
models
-| 1 | Source: net/http; Request; true; Cookie; ; ; ReturnValue[0]; remote; manual |
-| 2 | Summary: golang.org/x/net/html; ; false; EscapeString; ; ; Argument[0]; ReturnValue; taint; manual |
+| 1 | Sink: database/sql; DB; true; Query; ; ; Argument[0]; sql-injection; manual |
+| 2 | Source: net/http; Request; true; Cookie; ; ; ReturnValue[0]; remote; manual |
+| 3 | Summary: golang.org/x/net/html; ; false; EscapeString; ; ; Argument[0]; ReturnValue; taint; manual |
nodes
| test.go:56:2:56:42 | ... := ...[0] | semmle.label | ... := ...[0] |
| test.go:57:11:57:41 | call to EscapeString | semmle.label | call to EscapeString |
diff --git a/go/ql/test/query-tests/Security/CWE-089/SqlInjection.expected b/go/ql/test/query-tests/Security/CWE-089/SqlInjection.expected
index 79d8809e19f9..1ce8c3d1dcf6 100644
--- a/go/ql/test/query-tests/Security/CWE-089/SqlInjection.expected
+++ b/go/ql/test/query-tests/Security/CWE-089/SqlInjection.expected
@@ -25,53 +25,53 @@
| mongoDB.go:80:22:80:27 | filter | mongoDB.go:40:20:40:30 | call to Referer | mongoDB.go:80:22:80:27 | filter | This query depends on a $@. | mongoDB.go:40:20:40:30 | call to Referer | user-provided value |
| mongoDB.go:81:18:81:25 | pipeline | mongoDB.go:40:20:40:30 | call to Referer | mongoDB.go:81:18:81:25 | pipeline | This query depends on a $@. | mongoDB.go:40:20:40:30 | call to Referer | user-provided value |
edges
-| SqlInjection.go:10:7:11:30 | []type{args} [array] | SqlInjection.go:10:7:11:30 | call to Sprintf | provenance | MaD:7 |
-| SqlInjection.go:10:7:11:30 | call to Sprintf | SqlInjection.go:12:11:12:11 | q | provenance | |
-| SqlInjection.go:11:3:11:9 | selection of URL | SqlInjection.go:11:3:11:17 | call to Query | provenance | Src:MaD:5 MaD:10 |
+| SqlInjection.go:10:7:11:30 | []type{args} [array] | SqlInjection.go:10:7:11:30 | call to Sprintf | provenance | MaD:23 |
+| SqlInjection.go:10:7:11:30 | call to Sprintf | SqlInjection.go:12:11:12:11 | q | provenance | Sink:MaD:1 |
+| SqlInjection.go:11:3:11:9 | selection of URL | SqlInjection.go:11:3:11:17 | call to Query | provenance | Src:MaD:21 MaD:26 |
| SqlInjection.go:11:3:11:17 | call to Query | SqlInjection.go:11:3:11:29 | index expression | provenance | |
| SqlInjection.go:11:3:11:29 | index expression | SqlInjection.go:10:7:11:30 | []type{args} [array] | provenance | |
| SqlInjection.go:11:3:11:29 | index expression | SqlInjection.go:10:7:11:30 | call to Sprintf | provenance | FunctionModel |
| issue48.go:17:2:17:33 | ... := ...[0] | issue48.go:18:17:18:17 | b | provenance | |
-| issue48.go:17:25:17:32 | selection of Body | issue48.go:17:2:17:33 | ... := ...[0] | provenance | Src:MaD:1 MaD:8 |
-| issue48.go:18:17:18:17 | b | issue48.go:18:20:18:39 | &... | provenance | MaD:6 |
+| issue48.go:17:25:17:32 | selection of Body | issue48.go:17:2:17:33 | ... := ...[0] | provenance | Src:MaD:17 MaD:24 |
+| issue48.go:18:17:18:17 | b | issue48.go:18:20:18:39 | &... | provenance | MaD:22 |
| issue48.go:18:20:18:39 | &... | issue48.go:21:3:21:33 | index expression | provenance | |
-| issue48.go:20:8:21:34 | []type{args} [array] | issue48.go:20:8:21:34 | call to Sprintf | provenance | MaD:7 |
-| issue48.go:20:8:21:34 | call to Sprintf | issue48.go:22:11:22:12 | q3 | provenance | |
+| issue48.go:20:8:21:34 | []type{args} [array] | issue48.go:20:8:21:34 | call to Sprintf | provenance | MaD:23 |
+| issue48.go:20:8:21:34 | call to Sprintf | issue48.go:22:11:22:12 | q3 | provenance | Sink:MaD:1 |
| issue48.go:21:3:21:33 | index expression | issue48.go:20:8:21:34 | []type{args} [array] | provenance | |
| issue48.go:21:3:21:33 | index expression | issue48.go:20:8:21:34 | call to Sprintf | provenance | FunctionModel |
| issue48.go:27:2:27:34 | ... := ...[0] | issue48.go:28:17:28:18 | b2 | provenance | |
-| issue48.go:27:26:27:33 | selection of Body | issue48.go:27:2:27:34 | ... := ...[0] | provenance | Src:MaD:1 MaD:8 |
-| issue48.go:28:17:28:18 | b2 | issue48.go:28:21:28:41 | &... | provenance | MaD:6 |
+| issue48.go:27:26:27:33 | selection of Body | issue48.go:27:2:27:34 | ... := ...[0] | provenance | Src:MaD:17 MaD:24 |
+| issue48.go:28:17:28:18 | b2 | issue48.go:28:21:28:41 | &... | provenance | MaD:22 |
| issue48.go:28:21:28:41 | &... | issue48.go:31:3:31:31 | selection of Category | provenance | |
-| issue48.go:30:8:31:32 | []type{args} [array] | issue48.go:30:8:31:32 | call to Sprintf | provenance | MaD:7 |
-| issue48.go:30:8:31:32 | call to Sprintf | issue48.go:32:11:32:12 | q4 | provenance | |
+| issue48.go:30:8:31:32 | []type{args} [array] | issue48.go:30:8:31:32 | call to Sprintf | provenance | MaD:23 |
+| issue48.go:30:8:31:32 | call to Sprintf | issue48.go:32:11:32:12 | q4 | provenance | Sink:MaD:1 |
| issue48.go:31:3:31:31 | selection of Category | issue48.go:30:8:31:32 | []type{args} [array] | provenance | |
| issue48.go:31:3:31:31 | selection of Category | issue48.go:30:8:31:32 | call to Sprintf | provenance | FunctionModel |
-| issue48.go:37:17:37:50 | type conversion | issue48.go:37:53:37:73 | &... | provenance | MaD:6 |
-| issue48.go:37:24:37:30 | selection of URL | issue48.go:37:24:37:38 | call to Query | provenance | Src:MaD:5 MaD:10 |
+| issue48.go:37:17:37:50 | type conversion | issue48.go:37:53:37:73 | &... | provenance | MaD:22 |
+| issue48.go:37:24:37:30 | selection of URL | issue48.go:37:24:37:38 | call to Query | provenance | Src:MaD:21 MaD:26 |
| issue48.go:37:24:37:38 | call to Query | issue48.go:37:17:37:50 | type conversion | provenance | |
| issue48.go:37:53:37:73 | &... | issue48.go:40:3:40:31 | selection of Category | provenance | |
-| issue48.go:39:8:40:32 | []type{args} [array] | issue48.go:39:8:40:32 | call to Sprintf | provenance | MaD:7 |
-| issue48.go:39:8:40:32 | call to Sprintf | issue48.go:41:11:41:12 | q5 | provenance | |
+| issue48.go:39:8:40:32 | []type{args} [array] | issue48.go:39:8:40:32 | call to Sprintf | provenance | MaD:23 |
+| issue48.go:39:8:40:32 | call to Sprintf | issue48.go:41:11:41:12 | q5 | provenance | Sink:MaD:1 |
| issue48.go:40:3:40:31 | selection of Category | issue48.go:39:8:40:32 | []type{args} [array] | provenance | |
| issue48.go:40:3:40:31 | selection of Category | issue48.go:39:8:40:32 | call to Sprintf | provenance | FunctionModel |
-| main.go:11:11:11:16 | selection of Form | main.go:11:11:11:28 | index expression | provenance | Src:MaD:2 |
-| main.go:15:11:15:84 | []type{args} [array] | main.go:15:11:15:84 | call to Sprintf | provenance | MaD:7 |
-| main.go:15:63:15:67 | selection of URL | main.go:15:63:15:75 | call to Query | provenance | Src:MaD:5 MaD:10 |
+| main.go:11:11:11:16 | selection of Form | main.go:11:11:11:28 | index expression | provenance | Src:MaD:18 Sink:MaD:1 |
+| main.go:15:11:15:84 | []type{args} [array] | main.go:15:11:15:84 | call to Sprintf | provenance | MaD:23 Sink:MaD:2 |
+| main.go:15:63:15:67 | selection of URL | main.go:15:63:15:75 | call to Query | provenance | Src:MaD:21 MaD:26 |
| main.go:15:63:15:75 | call to Query | main.go:15:63:15:83 | index expression | provenance | |
| main.go:15:63:15:83 | index expression | main.go:15:11:15:84 | []type{args} [array] | provenance | |
-| main.go:15:63:15:83 | index expression | main.go:15:11:15:84 | call to Sprintf | provenance | FunctionModel |
-| main.go:16:11:16:85 | []type{args} [array] | main.go:16:11:16:85 | call to Sprintf | provenance | MaD:7 |
-| main.go:16:63:16:70 | selection of Header | main.go:16:63:16:84 | call to Get | provenance | Src:MaD:3 MaD:9 |
+| main.go:15:63:15:83 | index expression | main.go:15:11:15:84 | call to Sprintf | provenance | FunctionModel Sink:MaD:2 |
+| main.go:16:11:16:85 | []type{args} [array] | main.go:16:11:16:85 | call to Sprintf | provenance | MaD:23 Sink:MaD:2 |
+| main.go:16:63:16:70 | selection of Header | main.go:16:63:16:84 | call to Get | provenance | Src:MaD:19 MaD:25 |
| main.go:16:63:16:84 | call to Get | main.go:16:11:16:85 | []type{args} [array] | provenance | |
-| main.go:16:63:16:84 | call to Get | main.go:16:11:16:85 | call to Sprintf | provenance | FunctionModel |
+| main.go:16:63:16:84 | call to Get | main.go:16:11:16:85 | call to Sprintf | provenance | FunctionModel Sink:MaD:2 |
| main.go:28:17:31:2 | &... [pointer, Category] | main.go:34:3:34:13 | RequestData [pointer, Category] | provenance | |
| main.go:28:18:31:2 | struct literal [Category] | main.go:28:17:31:2 | &... [pointer, Category] | provenance | |
-| main.go:30:13:30:19 | selection of URL | main.go:30:13:30:27 | call to Query | provenance | Src:MaD:5 MaD:10 |
+| main.go:30:13:30:19 | selection of URL | main.go:30:13:30:27 | call to Query | provenance | Src:MaD:21 MaD:26 |
| main.go:30:13:30:27 | call to Query | main.go:30:13:30:39 | index expression | provenance | |
| main.go:30:13:30:39 | index expression | main.go:28:18:31:2 | struct literal [Category] | provenance | |
-| main.go:33:7:34:23 | []type{args} [array] | main.go:33:7:34:23 | call to Sprintf | provenance | MaD:7 |
-| main.go:33:7:34:23 | call to Sprintf | main.go:35:11:35:11 | q | provenance | |
+| main.go:33:7:34:23 | []type{args} [array] | main.go:33:7:34:23 | call to Sprintf | provenance | MaD:23 |
+| main.go:33:7:34:23 | call to Sprintf | main.go:35:11:35:11 | q | provenance | Sink:MaD:1 |
| main.go:34:3:34:13 | RequestData [pointer, Category] | main.go:34:3:34:13 | implicit dereference [Category] | provenance | |
| main.go:34:3:34:13 | implicit dereference [Category] | main.go:34:3:34:22 | selection of Category | provenance | |
| main.go:34:3:34:22 | selection of Category | main.go:33:7:34:23 | []type{args} [array] | provenance | |
@@ -80,11 +80,11 @@ edges
| main.go:39:2:39:12 | definition of RequestData [pointer, Category] | main.go:43:3:43:13 | RequestData [pointer, Category] | provenance | |
| main.go:40:2:40:12 | RequestData [pointer, Category] | main.go:40:2:40:12 | implicit dereference [Category] | provenance | |
| main.go:40:2:40:12 | implicit dereference [Category] | main.go:39:2:39:12 | definition of RequestData [pointer, Category] | provenance | |
-| main.go:40:25:40:31 | selection of URL | main.go:40:25:40:39 | call to Query | provenance | Src:MaD:5 MaD:10 |
+| main.go:40:25:40:31 | selection of URL | main.go:40:25:40:39 | call to Query | provenance | Src:MaD:21 MaD:26 |
| main.go:40:25:40:39 | call to Query | main.go:40:25:40:51 | index expression | provenance | |
| main.go:40:25:40:51 | index expression | main.go:40:2:40:12 | implicit dereference [Category] | provenance | |
-| main.go:42:7:43:23 | []type{args} [array] | main.go:42:7:43:23 | call to Sprintf | provenance | MaD:7 |
-| main.go:42:7:43:23 | call to Sprintf | main.go:44:11:44:11 | q | provenance | |
+| main.go:42:7:43:23 | []type{args} [array] | main.go:42:7:43:23 | call to Sprintf | provenance | MaD:23 |
+| main.go:42:7:43:23 | call to Sprintf | main.go:44:11:44:11 | q | provenance | Sink:MaD:1 |
| main.go:43:3:43:13 | RequestData [pointer, Category] | main.go:43:3:43:13 | implicit dereference [Category] | provenance | |
| main.go:43:3:43:13 | implicit dereference [Category] | main.go:43:3:43:22 | selection of Category | provenance | |
| main.go:43:3:43:22 | selection of Category | main.go:42:7:43:23 | []type{args} [array] | provenance | |
@@ -93,11 +93,11 @@ edges
| main.go:48:2:48:12 | definition of RequestData [pointer, Category] | main.go:52:3:52:13 | RequestData [pointer, Category] | provenance | |
| main.go:49:3:49:14 | star expression [Category] | main.go:48:2:48:12 | definition of RequestData [pointer, Category] | provenance | |
| main.go:49:4:49:14 | RequestData [pointer, Category] | main.go:49:3:49:14 | star expression [Category] | provenance | |
-| main.go:49:28:49:34 | selection of URL | main.go:49:28:49:42 | call to Query | provenance | Src:MaD:5 MaD:10 |
+| main.go:49:28:49:34 | selection of URL | main.go:49:28:49:42 | call to Query | provenance | Src:MaD:21 MaD:26 |
| main.go:49:28:49:42 | call to Query | main.go:49:28:49:54 | index expression | provenance | |
| main.go:49:28:49:54 | index expression | main.go:49:3:49:14 | star expression [Category] | provenance | |
-| main.go:51:7:52:23 | []type{args} [array] | main.go:51:7:52:23 | call to Sprintf | provenance | MaD:7 |
-| main.go:51:7:52:23 | call to Sprintf | main.go:53:11:53:11 | q | provenance | |
+| main.go:51:7:52:23 | []type{args} [array] | main.go:51:7:52:23 | call to Sprintf | provenance | MaD:23 |
+| main.go:51:7:52:23 | call to Sprintf | main.go:53:11:53:11 | q | provenance | Sink:MaD:1 |
| main.go:52:3:52:13 | RequestData [pointer, Category] | main.go:52:3:52:13 | implicit dereference [Category] | provenance | |
| main.go:52:3:52:13 | implicit dereference [Category] | main.go:52:3:52:22 | selection of Category | provenance | |
| main.go:52:3:52:22 | selection of Category | main.go:51:7:52:23 | []type{args} [array] | provenance | |
@@ -106,44 +106,60 @@ edges
| main.go:57:2:57:12 | definition of RequestData [pointer, Category] | main.go:61:5:61:15 | RequestData [pointer, Category] | provenance | |
| main.go:58:3:58:14 | star expression [Category] | main.go:57:2:57:12 | definition of RequestData [pointer, Category] | provenance | |
| main.go:58:4:58:14 | RequestData [pointer, Category] | main.go:58:3:58:14 | star expression [Category] | provenance | |
-| main.go:58:28:58:34 | selection of URL | main.go:58:28:58:42 | call to Query | provenance | Src:MaD:5 MaD:10 |
+| main.go:58:28:58:34 | selection of URL | main.go:58:28:58:42 | call to Query | provenance | Src:MaD:21 MaD:26 |
| main.go:58:28:58:42 | call to Query | main.go:58:28:58:54 | index expression | provenance | |
| main.go:58:28:58:54 | index expression | main.go:58:3:58:14 | star expression [Category] | provenance | |
-| main.go:60:7:61:26 | []type{args} [array] | main.go:60:7:61:26 | call to Sprintf | provenance | MaD:7 |
-| main.go:60:7:61:26 | call to Sprintf | main.go:62:11:62:11 | q | provenance | |
+| main.go:60:7:61:26 | []type{args} [array] | main.go:60:7:61:26 | call to Sprintf | provenance | MaD:23 |
+| main.go:60:7:61:26 | call to Sprintf | main.go:62:11:62:11 | q | provenance | Sink:MaD:1 |
| main.go:61:3:61:25 | selection of Category | main.go:60:7:61:26 | []type{args} [array] | provenance | |
| main.go:61:3:61:25 | selection of Category | main.go:60:7:61:26 | call to Sprintf | provenance | FunctionModel |
| main.go:61:4:61:15 | star expression [Category] | main.go:61:3:61:25 | selection of Category | provenance | |
| main.go:61:5:61:15 | RequestData [pointer, Category] | main.go:61:4:61:15 | star expression [Category] | provenance | |
-| mongoDB.go:40:20:40:30 | call to Referer | mongoDB.go:42:28:42:41 | untrustedInput | provenance | Src:MaD:4 |
+| mongoDB.go:40:20:40:30 | call to Referer | mongoDB.go:42:28:42:41 | untrustedInput | provenance | Src:MaD:20 |
| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:50:34:50:39 | filter | provenance | |
-| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:61:27:61:32 | filter | provenance | |
-| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:63:23:63:28 | filter | provenance | |
-| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:64:22:64:27 | filter | provenance | |
-| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:66:32:66:37 | filter | provenance | |
-| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:69:17:69:22 | filter | provenance | |
-| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:70:20:70:25 | filter | provenance | |
-| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:71:29:71:34 | filter | provenance | |
-| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:72:30:72:35 | filter | provenance | |
-| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:73:29:73:34 | filter | provenance | |
-| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:78:23:78:28 | filter | provenance | |
-| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:79:23:79:28 | filter | provenance | |
-| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:80:22:80:27 | filter | provenance | |
+| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:61:27:61:32 | filter | provenance | Sink:MaD:4 |
+| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:63:23:63:28 | filter | provenance | Sink:MaD:5 |
+| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:64:22:64:27 | filter | provenance | Sink:MaD:6 |
+| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:66:32:66:37 | filter | provenance | Sink:MaD:7 |
+| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:69:17:69:22 | filter | provenance | Sink:MaD:8 |
+| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:70:20:70:25 | filter | provenance | Sink:MaD:9 |
+| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:71:29:71:34 | filter | provenance | Sink:MaD:10 |
+| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:72:30:72:35 | filter | provenance | Sink:MaD:11 |
+| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:73:29:73:34 | filter | provenance | Sink:MaD:12 |
+| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:78:23:78:28 | filter | provenance | Sink:MaD:13 |
+| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:79:23:79:28 | filter | provenance | Sink:MaD:14 |
+| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:80:22:80:27 | filter | provenance | Sink:MaD:15 |
| mongoDB.go:42:28:42:41 | untrustedInput | mongoDB.go:42:19:42:42 | struct literal | provenance | Config |
-| mongoDB.go:50:23:50:40 | struct literal | mongoDB.go:57:22:57:29 | pipeline | provenance | |
-| mongoDB.go:50:23:50:40 | struct literal | mongoDB.go:81:18:81:25 | pipeline | provenance | |
+| mongoDB.go:50:23:50:40 | struct literal | mongoDB.go:57:22:57:29 | pipeline | provenance | Sink:MaD:3 |
+| mongoDB.go:50:23:50:40 | struct literal | mongoDB.go:81:18:81:25 | pipeline | provenance | Sink:MaD:16 |
| mongoDB.go:50:34:50:39 | filter | mongoDB.go:50:23:50:40 | struct literal | provenance | Config |
models
-| 1 | Source: net/http; Request; true; Body; ; ; ; remote; manual |
-| 2 | Source: net/http; Request; true; Form; ; ; ; remote; manual |
-| 3 | Source: net/http; Request; true; Header; ; ; ; remote; manual |
-| 4 | Source: net/http; Request; true; Referer; ; ; ReturnValue; remote; manual |
-| 5 | Source: net/http; Request; true; URL; ; ; ; remote; manual |
-| 6 | Summary: encoding/json; ; false; Unmarshal; ; ; Argument[0]; Argument[1]; taint; manual |
-| 7 | Summary: fmt; ; false; Sprintf; ; ; Argument[1].ArrayElement; ReturnValue; taint; manual |
-| 8 | Summary: io/ioutil; ; false; ReadAll; ; ; Argument[0]; ReturnValue[0]; taint; manual |
-| 9 | Summary: net/http; Header; true; Get; ; ; Argument[receiver]; ReturnValue; taint; manual |
-| 10 | Summary: net/url; URL; true; Query; ; ; Argument[receiver]; ReturnValue; taint; manual |
+| 1 | Sink: database/sql; DB; true; Query; ; ; Argument[0]; sql-injection; manual |
+| 2 | Sink: database/sql; Tx; true; Query; ; ; Argument[0]; sql-injection; manual |
+| 3 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; Aggregate; ; ; Argument[1]; nosql-injection; manual |
+| 4 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; CountDocuments; ; ; Argument[1]; nosql-injection; manual |
+| 5 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; DeleteMany; ; ; Argument[1]; nosql-injection; manual |
+| 6 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; DeleteOne; ; ; Argument[1]; nosql-injection; manual |
+| 7 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; Distinct; ; ; Argument[2]; nosql-injection; manual |
+| 8 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; Find; ; ; Argument[1]; nosql-injection; manual |
+| 9 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; FindOne; ; ; Argument[1]; nosql-injection; manual |
+| 10 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; FindOneAndDelete; ; ; Argument[1]; nosql-injection; manual |
+| 11 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; FindOneAndReplace; ; ; Argument[1]; nosql-injection; manual |
+| 12 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; FindOneAndUpdate; ; ; Argument[1]; nosql-injection; manual |
+| 13 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; ReplaceOne; ; ; Argument[1]; nosql-injection; manual |
+| 14 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; UpdateMany; ; ; Argument[1]; nosql-injection; manual |
+| 15 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; UpdateOne; ; ; Argument[1]; nosql-injection; manual |
+| 16 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; Watch; ; ; Argument[1]; nosql-injection; manual |
+| 17 | Source: net/http; Request; true; Body; ; ; ; remote; manual |
+| 18 | Source: net/http; Request; true; Form; ; ; ; remote; manual |
+| 19 | Source: net/http; Request; true; Header; ; ; ; remote; manual |
+| 20 | Source: net/http; Request; true; Referer; ; ; ReturnValue; remote; manual |
+| 21 | Source: net/http; Request; true; URL; ; ; ; remote; manual |
+| 22 | Summary: encoding/json; ; false; Unmarshal; ; ; Argument[0]; Argument[1]; taint; manual |
+| 23 | Summary: fmt; ; false; Sprintf; ; ; Argument[1].ArrayElement; ReturnValue; taint; manual |
+| 24 | Summary: io/ioutil; ; false; ReadAll; ; ; Argument[0]; ReturnValue[0]; taint; manual |
+| 25 | Summary: net/http; Header; true; Get; ; ; Argument[receiver]; ReturnValue; taint; manual |
+| 26 | Summary: net/url; URL; true; Query; ; ; Argument[receiver]; ReturnValue; taint; manual |
nodes
| SqlInjection.go:10:7:11:30 | []type{args} [array] | semmle.label | []type{args} [array] |
| SqlInjection.go:10:7:11:30 | call to Sprintf | semmle.label | call to Sprintf |
diff --git a/go/ql/test/query-tests/Security/CWE-117/LogInjection.go b/go/ql/test/query-tests/Security/CWE-117/LogInjection.go
index 4f02cc9aef83..6fb628c4cc38 100644
--- a/go/ql/test/query-tests/Security/CWE-117/LogInjection.go
+++ b/go/ql/test/query-tests/Security/CWE-117/LogInjection.go
@@ -7,7 +7,7 @@ package main
//go:generate depstubber -vendor github.com/davecgh/go-spew/spew "" Dump,Errorf,Print,Printf,Println,Fdump,Fprint,Fprintf,Fprintln
//go:generate depstubber -vendor github.com/elazarl/goproxy ProxyCtx ""
//go:generate depstubber -vendor github.com/golang/glog Level,Verbose Info,InfoDepth,Infof,Infoln,Error,ErrorDepth,Errorf,Errorln,Fatal,FatalDepth,Fatalf,Fatalln,Exit,ExitDepth,Exitf,Exitln,V
-//go:generate depstubber -vendor github.com/sirupsen/logrus Fields,Entry,Logger,Level Debug,Debugf,Debugln,Error,Errorf,Errorln,Fatal,Fatalf,Fatalln,Info,Infof,Infoln,Panic,Panicf,Panicln,Print,Printf,Println,Trace,Tracef,Traceln,Warn,Warnf,Warnln,Warning,Warningf,Warningln,WithFields,WithField
+//go:generate depstubber -vendor github.com/sirupsen/logrus FieldLogger,Fields,Entry,Logger,Level Debug,Debugf,Debugln,Error,Errorf,Errorln,Fatal,Fatalf,Fatalln,Info,Infof,Infoln,New,NewEntry,Panic,Panicf,Panicln,Print,Printf,Println,Trace,Tracef,Traceln,Warn,Warnf,Warnln,Warning,Warningf,Warningln,WithError,WithFields,WithField
//go:generate depstubber -vendor go.uber.org/zap Logger,SugaredLogger NewProduction
import (
@@ -30,6 +30,7 @@ import (
func handler(req *http.Request, ctx *goproxy.ProxyCtx) {
username := req.URL.Query()["username"][0]
+ slice := []any{"username", username}
testFlag := req.URL.Query()["testFlag"][0]
{
@@ -170,129 +171,180 @@ func handler(req *http.Request, ctx *goproxy.ProxyCtx) {
}
// sirupsen/logrus
{
- logrus.Debug(username) // $ hasTaintFlow="username"
- logrus.Debugf(username, "") // $ hasTaintFlow="username"
- logrus.Debugf("", username) // $ hasTaintFlow="username"
- logrus.Debugln(username) // $ hasTaintFlow="username"
- logrus.Error(username) // $ hasTaintFlow="username"
- logrus.Errorf(username, "") // $ hasTaintFlow="username"
- logrus.Errorf("", username) // $ hasTaintFlow="username"
- logrus.Errorln(username) // $ hasTaintFlow="username"
- logrus.Fatal(username) // $ hasTaintFlow="username"
- logrus.Fatalf(username, "") // $ hasTaintFlow="username"
- logrus.Fatalf("", username) // $ hasTaintFlow="username"
- logrus.Fatalln(username) // $ hasTaintFlow="username"
- logrus.Info(username) // $ hasTaintFlow="username"
- logrus.Infof(username, "") // $ hasTaintFlow="username"
- logrus.Infof("", username) // $ hasTaintFlow="username"
- logrus.Infoln(username) // $ hasTaintFlow="username"
- logrus.Panic(username) // $ hasTaintFlow="username"
- logrus.Panicf(username, "") // $ hasTaintFlow="username"
- logrus.Panicf("", username) // $ hasTaintFlow="username"
- logrus.Panicln(username) // $ hasTaintFlow="username"
- logrus.Print(username) // $ hasTaintFlow="username"
- logrus.Printf(username, "") // $ hasTaintFlow="username"
- logrus.Printf("", username) // $ hasTaintFlow="username"
- logrus.Println(username) // $ hasTaintFlow="username"
- logrus.Trace(username) // $ hasTaintFlow="username"
- logrus.Tracef(username, "") // $ hasTaintFlow="username"
- logrus.Tracef("", username) // $ hasTaintFlow="username"
- logrus.Traceln(username) // $ hasTaintFlow="username"
- logrus.Warn(username) // $ hasTaintFlow="username"
- logrus.Warnf(username, "") // $ hasTaintFlow="username"
- logrus.Warnf("", username) // $ hasTaintFlow="username"
- logrus.Warnln(username) // $ hasTaintFlow="username"
- logrus.Warning(username) // $ hasTaintFlow="username"
- logrus.Warningf(username, "") // $ hasTaintFlow="username"
- logrus.Warningf("", username) // $ hasTaintFlow="username"
- logrus.Warningln(username) // $ hasTaintFlow="username"
-
+ err := fmt.Errorf("error: %s", username)
fields := make(logrus.Fields)
fields["username"] = username
- entry := logrus.WithFields(fields) // $ hasTaintFlow="fields"
- entry = logrus.WithField("username", username) // $ hasTaintFlow="username"
- entry.Debug(username) // $ hasTaintFlow="username"
- entry.Debugf(username, "") // $ hasTaintFlow="username"
- entry.Debugf("", username) // $ hasTaintFlow="username"
- entry.Debugln(username) // $ hasTaintFlow="username"
- entry.Error(username) // $ hasTaintFlow="username"
- entry.Errorf(username, "") // $ hasTaintFlow="username"
- entry.Errorf("", username) // $ hasTaintFlow="username"
- entry.Errorln(username) // $ hasTaintFlow="username"
- entry.Fatal(username) // $ hasTaintFlow="username"
- entry.Fatalf(username, "") // $ hasTaintFlow="username"
- entry.Fatalf("", username) // $ hasTaintFlow="username"
- entry.Fatalln(username) // $ hasTaintFlow="username"
- entry.Info(username) // $ hasTaintFlow="username"
- entry.Infof(username, "") // $ hasTaintFlow="username"
- entry.Infof("", username) // $ hasTaintFlow="username"
- entry.Infoln(username) // $ hasTaintFlow="username"
- entry.Log(0, username) // $ hasTaintFlow="username"
- entry.Logf(0, username, "") // $ hasTaintFlow="username"
- entry.Logf(0, "", username) // $ hasTaintFlow="username"
- entry.Logln(0, username) // $ hasTaintFlow="username"
- entry.Panic(username) // $ hasTaintFlow="username"
- entry.Panicf(username, "") // $ hasTaintFlow="username"
- entry.Panicf("", username) // $ hasTaintFlow="username"
- entry.Panicln(username) // $ hasTaintFlow="username"
- entry.Print(username) // $ hasTaintFlow="username"
- entry.Printf(username, "") // $ hasTaintFlow="username"
- entry.Printf("", username) // $ hasTaintFlow="username"
- entry.Println(username) // $ hasTaintFlow="username"
- entry.Trace(username) // $ hasTaintFlow="username"
- entry.Tracef(username, "") // $ hasTaintFlow="username"
- entry.Tracef("", username) // $ hasTaintFlow="username"
- entry.Traceln(username) // $ hasTaintFlow="username"
- entry.Warn(username) // $ hasTaintFlow="username"
- entry.Warnf(username, "") // $ hasTaintFlow="username"
- entry.Warnf("", username) // $ hasTaintFlow="username"
- entry.Warnln(username) // $ hasTaintFlow="username"
- entry.Warning(username) // $ hasTaintFlow="username"
- entry.Warningf(username, "") // $ hasTaintFlow="username"
- entry.Warningf("", username) // $ hasTaintFlow="username"
- entry.Warningln(username) // $ hasTaintFlow="username"
-
- logger := entry.Logger
- logger.Debug(username) // $ hasTaintFlow="username"
- logger.Debugf(username, "") // $ hasTaintFlow="username"
- logger.Debugf("", username) // $ hasTaintFlow="username"
- logger.Debugln(username) // $ hasTaintFlow="username"
- logger.Error(username) // $ hasTaintFlow="username"
- logger.Errorf(username, "") // $ hasTaintFlow="username"
- logger.Errorf("", username) // $ hasTaintFlow="username"
- logger.Errorln(username) // $ hasTaintFlow="username"
- logger.Fatal(username) // $ hasTaintFlow="username"
- logger.Fatalf(username, "") // $ hasTaintFlow="username"
- logger.Fatalf("", username) // $ hasTaintFlow="username"
- logger.Fatalln(username) // $ hasTaintFlow="username"
- logger.Info(username) // $ hasTaintFlow="username"
- logger.Infof(username, "") // $ hasTaintFlow="username"
- logger.Infof("", username) // $ hasTaintFlow="username"
- logger.Infoln(username) // $ hasTaintFlow="username"
- logger.Log(0, username) // $ hasTaintFlow="username"
- logger.Logf(0, username, "") // $ hasTaintFlow="username"
- logger.Logf(0, "", username) // $ hasTaintFlow="username"
- logger.Logln(0, username) // $ hasTaintFlow="username"
- logger.Panic(username) // $ hasTaintFlow="username"
- logger.Panicf(username, "") // $ hasTaintFlow="username"
- logger.Panicf("", username) // $ hasTaintFlow="username"
- logger.Panicln(username) // $ hasTaintFlow="username"
- logger.Print(username) // $ hasTaintFlow="username"
- logger.Printf(username, "") // $ hasTaintFlow="username"
- logger.Printf("", username) // $ hasTaintFlow="username"
- logger.Println(username) // $ hasTaintFlow="username"
- logger.Trace(username) // $ hasTaintFlow="username"
- logger.Tracef(username, "") // $ hasTaintFlow="username"
- logger.Tracef("", username) // $ hasTaintFlow="username"
- logger.Traceln(username) // $ hasTaintFlow="username"
- logger.Warn(username) // $ hasTaintFlow="username"
- logger.Warnf(username, "") // $ hasTaintFlow="username"
- logger.Warnf("", username) // $ hasTaintFlow="username"
- logger.Warnln(username) // $ hasTaintFlow="username"
- logger.Warning(username) // $ hasTaintFlow="username"
- logger.Warningf(username, "") // $ hasTaintFlow="username"
- logger.Warningf("", username) // $ hasTaintFlow="username"
- logger.Warningln(username) // $ hasTaintFlow="username"
+ logger := logrus.New()
+ entry := logrus.NewEntry(logger)
+
+ logrus.Debug(username) // $ hasTaintFlow="username"
+ logrus.Debugf(username, "") // $ hasTaintFlow="username"
+ logrus.Debugf("", username) // $ hasTaintFlow="username"
+ logrus.Debugln(username) // $ hasTaintFlow="username"
+ logrus.Error(username) // $ hasTaintFlow="username"
+ logrus.Errorf(username, "") // $ hasTaintFlow="username"
+ logrus.Errorf("", username) // $ hasTaintFlow="username"
+ logrus.Errorln(username) // $ hasTaintFlow="username"
+ logrus.Fatal(username) // $ hasTaintFlow="username"
+ logrus.Fatalf(username, "") // $ hasTaintFlow="username"
+ logrus.Fatalf("", username) // $ hasTaintFlow="username"
+ logrus.Fatalln(username) // $ hasTaintFlow="username"
+ logrus.Info(username) // $ hasTaintFlow="username"
+ logrus.Infof(username, "") // $ hasTaintFlow="username"
+ logrus.Infof("", username) // $ hasTaintFlow="username"
+ logrus.Infoln(username) // $ hasTaintFlow="username"
+ logrus.Panic(username) // $ hasTaintFlow="username"
+ logrus.Panicf(username, "") // $ hasTaintFlow="username"
+ logrus.Panicf("", username) // $ hasTaintFlow="username"
+ logrus.Panicln(username) // $ hasTaintFlow="username"
+ logrus.Print(username) // $ hasTaintFlow="username"
+ logrus.Printf(username, "") // $ hasTaintFlow="username"
+ logrus.Printf("", username) // $ hasTaintFlow="username"
+ logrus.Println(username) // $ hasTaintFlow="username"
+ logrus.Trace(username) // $ hasTaintFlow="username"
+ logrus.Tracef(username, "") // $ hasTaintFlow="username"
+ logrus.Tracef("", username) // $ hasTaintFlow="username"
+ logrus.Traceln(username) // $ hasTaintFlow="username"
+ logrus.Warn(username) // $ hasTaintFlow="username"
+ logrus.Warnf(username, "") // $ hasTaintFlow="username"
+ logrus.Warnf("", username) // $ hasTaintFlow="username"
+ logrus.Warnln(username) // $ hasTaintFlow="username"
+ logrus.Warning(username) // $ hasTaintFlow="username"
+ logrus.Warningf(username, "") // $ hasTaintFlow="username"
+ logrus.Warningf("", username) // $ hasTaintFlow="username"
+ logrus.Warningln(username) // $ hasTaintFlow="username"
+ logrus.WithError(err) // $ hasTaintFlow="err"
+ logrus.WithField(username, "") // $ hasTaintFlow="username"
+ logrus.WithField("", username) // $ hasTaintFlow="username"
+ logrus.WithFields(fields) // $ hasTaintFlow="fields"
+
+ entry.Debug(username) // $ hasTaintFlow="username"
+ entry.Debugf(username, "") // $ hasTaintFlow="username"
+ entry.Debugf("", username) // $ hasTaintFlow="username"
+ entry.Debugln(username) // $ hasTaintFlow="username"
+ entry.Error(username) // $ hasTaintFlow="username"
+ entry.Errorf(username, "") // $ hasTaintFlow="username"
+ entry.Errorf("", username) // $ hasTaintFlow="username"
+ entry.Errorln(username) // $ hasTaintFlow="username"
+ entry.Fatal(username) // $ hasTaintFlow="username"
+ entry.Fatalf(username, "") // $ hasTaintFlow="username"
+ entry.Fatalf("", username) // $ hasTaintFlow="username"
+ entry.Fatalln(username) // $ hasTaintFlow="username"
+ entry.Info(username) // $ hasTaintFlow="username"
+ entry.Infof(username, "") // $ hasTaintFlow="username"
+ entry.Infof("", username) // $ hasTaintFlow="username"
+ entry.Infoln(username) // $ hasTaintFlow="username"
+ entry.Log(0, username) // $ hasTaintFlow="username"
+ entry.Logf(0, username, "") // $ hasTaintFlow="username"
+ entry.Logf(0, "", username) // $ hasTaintFlow="username"
+ entry.Logln(0, username) // $ hasTaintFlow="username"
+ entry.Panic(username) // $ hasTaintFlow="username"
+ entry.Panicf(username, "") // $ hasTaintFlow="username"
+ entry.Panicf("", username) // $ hasTaintFlow="username"
+ entry.Panicln(username) // $ hasTaintFlow="username"
+ entry.Print(username) // $ hasTaintFlow="username"
+ entry.Printf(username, "") // $ hasTaintFlow="username"
+ entry.Printf("", username) // $ hasTaintFlow="username"
+ entry.Println(username) // $ hasTaintFlow="username"
+ entry.Trace(username) // $ hasTaintFlow="username"
+ entry.Tracef(username, "") // $ hasTaintFlow="username"
+ entry.Tracef("", username) // $ hasTaintFlow="username"
+ entry.Traceln(username) // $ hasTaintFlow="username"
+ entry.Warn(username) // $ hasTaintFlow="username"
+ entry.Warnf(username, "") // $ hasTaintFlow="username"
+ entry.Warnf("", username) // $ hasTaintFlow="username"
+ entry.Warnln(username) // $ hasTaintFlow="username"
+ entry.Warning(username) // $ hasTaintFlow="username"
+ entry.Warningf(username, "") // $ hasTaintFlow="username"
+ entry.Warningf("", username) // $ hasTaintFlow="username"
+ entry.Warningln(username) // $ hasTaintFlow="username"
+ entry.WithError(err) // $ hasTaintFlow="err"
+ entry.WithField(username, "") // $ hasTaintFlow="username"
+ entry.WithField("", username) // $ hasTaintFlow="username"
+ entry.WithFields(fields) // $ hasTaintFlow="fields"
+
+ logger.Debug(username) // $ hasTaintFlow="username"
+ logger.Debugf(username, "") // $ hasTaintFlow="username"
+ logger.Debugf("", username) // $ hasTaintFlow="username"
+ logger.Debugln(username) // $ hasTaintFlow="username"
+ logger.Error(username) // $ hasTaintFlow="username"
+ logger.Errorf(username, "") // $ hasTaintFlow="username"
+ logger.Errorf("", username) // $ hasTaintFlow="username"
+ logger.Errorln(username) // $ hasTaintFlow="username"
+ logger.Fatal(username) // $ hasTaintFlow="username"
+ logger.Fatalf(username, "") // $ hasTaintFlow="username"
+ logger.Fatalf("", username) // $ hasTaintFlow="username"
+ logger.Fatalln(username) // $ hasTaintFlow="username"
+ logger.Info(username) // $ hasTaintFlow="username"
+ logger.Infof(username, "") // $ hasTaintFlow="username"
+ logger.Infof("", username) // $ hasTaintFlow="username"
+ logger.Infoln(username) // $ hasTaintFlow="username"
+ logger.Log(0, username) // $ hasTaintFlow="username"
+ logger.Logf(0, username, "") // $ hasTaintFlow="username"
+ logger.Logf(0, "", username) // $ hasTaintFlow="username"
+ logger.Logln(0, username) // $ hasTaintFlow="username"
+ logger.Panic(username) // $ hasTaintFlow="username"
+ logger.Panicf(username, "") // $ hasTaintFlow="username"
+ logger.Panicf("", username) // $ hasTaintFlow="username"
+ logger.Panicln(username) // $ hasTaintFlow="username"
+ logger.Print(username) // $ hasTaintFlow="username"
+ logger.Printf(username, "") // $ hasTaintFlow="username"
+ logger.Printf("", username) // $ hasTaintFlow="username"
+ logger.Println(username) // $ hasTaintFlow="username"
+ logger.Trace(username) // $ hasTaintFlow="username"
+ logger.Tracef(username, "") // $ hasTaintFlow="username"
+ logger.Tracef("", username) // $ hasTaintFlow="username"
+ logger.Traceln(username) // $ hasTaintFlow="username"
+ logger.Warn(username) // $ hasTaintFlow="username"
+ logger.Warnf(username, "") // $ hasTaintFlow="username"
+ logger.Warnf("", username) // $ hasTaintFlow="username"
+ logger.Warnln(username) // $ hasTaintFlow="username"
+ logger.Warning(username) // $ hasTaintFlow="username"
+ logger.Warningf(username, "") // $ hasTaintFlow="username"
+ logger.Warningf("", username) // $ hasTaintFlow="username"
+ logger.Warningln(username) // $ hasTaintFlow="username"
+ logger.WithError(err) // $ hasTaintFlow="err"
+ logger.WithField(username, "") // $ hasTaintFlow="username"
+ logger.WithField("", username) // $ hasTaintFlow="username"
+ logger.WithFields(fields) // $ hasTaintFlow="fields"
+
+ var fieldlogger logrus.FieldLogger = entry
+ fieldlogger.Debug(username) // $ hasTaintFlow="username"
+ fieldlogger.Debugf(username, "") // $ hasTaintFlow="username"
+ fieldlogger.Debugf("", username) // $ hasTaintFlow="username"
+ fieldlogger.Debugln(username) // $ hasTaintFlow="username"
+ fieldlogger.Error(username) // $ hasTaintFlow="username"
+ fieldlogger.Errorf(username, "") // $ hasTaintFlow="username"
+ fieldlogger.Errorf("", username) // $ hasTaintFlow="username"
+ fieldlogger.Errorln(username) // $ hasTaintFlow="username"
+ fieldlogger.Fatal(username) // $ hasTaintFlow="username"
+ fieldlogger.Fatalf(username, "") // $ hasTaintFlow="username"
+ fieldlogger.Fatalf("", username) // $ hasTaintFlow="username"
+ fieldlogger.Fatalln(username) // $ hasTaintFlow="username"
+ fieldlogger.Info(username) // $ hasTaintFlow="username"
+ fieldlogger.Infof(username, "") // $ hasTaintFlow="username"
+ fieldlogger.Infof("", username) // $ hasTaintFlow="username"
+ fieldlogger.Infoln(username) // $ hasTaintFlow="username"
+ fieldlogger.Panic(username) // $ hasTaintFlow="username"
+ fieldlogger.Panicf(username, "") // $ hasTaintFlow="username"
+ fieldlogger.Panicf("", username) // $ hasTaintFlow="username"
+ fieldlogger.Panicln(username) // $ hasTaintFlow="username"
+ fieldlogger.Print(username) // $ hasTaintFlow="username"
+ fieldlogger.Printf(username, "") // $ hasTaintFlow="username"
+ fieldlogger.Printf("", username) // $ hasTaintFlow="username"
+ fieldlogger.Println(username) // $ hasTaintFlow="username"
+ fieldlogger.Warn(username) // $ hasTaintFlow="username"
+ fieldlogger.Warnf(username, "") // $ hasTaintFlow="username"
+ fieldlogger.Warnf("", username) // $ hasTaintFlow="username"
+ fieldlogger.Warnln(username) // $ hasTaintFlow="username"
+ fieldlogger.Warning(username) // $ hasTaintFlow="username"
+ fieldlogger.Warningf(username, "") // $ hasTaintFlow="username"
+ fieldlogger.Warningf("", username) // $ hasTaintFlow="username"
+ fieldlogger.Warningln(username) // $ hasTaintFlow="username"
+ fieldlogger.WithError(err) // $ hasTaintFlow="err"
+ fieldlogger.WithField(username, "") // $ hasTaintFlow="username"
+ fieldlogger.WithField("", username) // $ hasTaintFlow="username"
+ fieldlogger.WithFields(fields) // $ hasTaintFlow="fields"
}
// davecgh/go-spew/spew
{
@@ -361,8 +413,34 @@ func handler(req *http.Request, ctx *goproxy.ProxyCtx) {
sLogger.Named(username) // $ hasTaintFlow="username"
sLogger.With(username) // $ hasTaintFlow="username"
}
+ // heuristic logger interface
+ {
+ logger.Printf(username) // $ hasTaintFlow="username"
+ logger.Printf("%s", username) // $ hasTaintFlow="username"
+ simpleLogger.Tracew(username) // $ hasTaintFlow="username"
+ simpleLogger.Tracew("%s", username) // $ hasTaintFlow="username"
+ simpleLogger.Debugw("%s %s", slice...) // $ hasTaintFlow="slice"
+ }
+
}
+type Logger interface {
+ Printf(string, ...interface{})
+}
+
+type SimpleLogger interface {
+ Debugw(msg string, keysAndValues ...any)
+ Infow(msg string, keysAndValues ...any)
+ Warnw(msg string, keysAndValues ...any)
+ Errorw(msg string, keysAndValues ...any)
+ Tracew(msg string, keysAndValues ...any)
+}
+
+var (
+ logger Logger
+ simpleLogger SimpleLogger
+)
+
// GOOD: The user-provided value is escaped before being written to the log.
func handlerGood(req *http.Request) {
username := req.URL.Query()["username"][0]
@@ -598,5 +676,4 @@ func handlerGood4(req *http.Request, ctx *goproxy.ProxyCtx) {
}
sLogger.Warnf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
}
-
}
diff --git a/go/ql/test/query-tests/Security/CWE-117/go.mod b/go/ql/test/query-tests/Security/CWE-117/go.mod
index 57b2077a4ed1..906d90f31b66 100644
--- a/go/ql/test/query-tests/Security/CWE-117/go.mod
+++ b/go/ql/test/query-tests/Security/CWE-117/go.mod
@@ -1,14 +1,33 @@
module main
-go 1.14
+go 1.23
require (
github.com/astaxie/beego v1.12.3
+ github.com/davecgh/go-spew v1.1.1
github.com/elazarl/goproxy v0.0.0-20211114080932-d06c3be7c11b
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
- github.com/kr/text v0.2.0 // indirect
github.com/sirupsen/logrus v1.8.1
- github.com/stretchr/testify v1.6.0 // indirect
- golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect
+ go.uber.org/zap v1.27.0
k8s.io/klog v1.0.0
)
+
+require (
+ github.com/beorn7/perks v1.0.1 // indirect
+ github.com/cespare/xxhash/v2 v2.1.1 // indirect
+ github.com/golang/protobuf v1.4.2 // indirect
+ github.com/hashicorp/golang-lru v0.5.4 // indirect
+ github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
+ github.com/prometheus/client_golang v1.7.0 // indirect
+ github.com/prometheus/client_model v0.2.0 // indirect
+ github.com/prometheus/common v0.10.0 // indirect
+ github.com/prometheus/procfs v0.1.3 // indirect
+ github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect
+ go.uber.org/multierr v1.10.0 // indirect
+ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 // indirect
+ golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect
+ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect
+ golang.org/x/text v0.3.0 // indirect
+ google.golang.org/protobuf v1.23.0 // indirect
+ gopkg.in/yaml.v2 v2.2.8 // indirect
+)
diff --git a/go/ql/test/query-tests/Security/CWE-117/vendor/github.com/sirupsen/logrus/stub.go b/go/ql/test/query-tests/Security/CWE-117/vendor/github.com/sirupsen/logrus/stub.go
index e8f7bccc4465..497d85559bb2 100644
--- a/go/ql/test/query-tests/Security/CWE-117/vendor/github.com/sirupsen/logrus/stub.go
+++ b/go/ql/test/query-tests/Security/CWE-117/vendor/github.com/sirupsen/logrus/stub.go
@@ -2,7 +2,7 @@
// This is a simple stub for github.com/sirupsen/logrus, strictly for use in testing.
// See the LICENSE file for information about the licensing of the original library.
-// Source: github.com/sirupsen/logrus (exports: Fields,Entry,Logger,Level; functions: Debug,Debugf,Debugln,Error,Errorf,Errorln,Fatal,Fatalf,Fatalln,Info,Infof,Infoln,Panic,Panicf,Panicln,Print,Printf,Println,Trace,Tracef,Traceln,Warn,Warnf,Warnln,Warning,Warningf,Warningln,WithFields,WithField)
+// Source: github.com/sirupsen/logrus (exports: FieldLogger,Fields,Entry,Logger,Level; functions: Debug,Debugf,Debugln,Error,Errorf,Errorln,Fatal,Fatalf,Fatalln,Info,Infof,Infoln,New,NewEntry,Panic,Panicf,Panicln,Print,Printf,Println,Trace,Tracef,Traceln,Warn,Warnf,Warnln,Warning,Warningf,Warningln,WithError,WithFields,WithField)
// Package logrus is a stub of github.com/sirupsen/logrus, generated by depstubber.
package logrus
@@ -148,6 +148,36 @@ func Fatalf(_ string, _ ...interface{}) {}
func Fatalln(_ ...interface{}) {}
+type FieldLogger interface {
+ Debug(_ ...interface{})
+ Debugf(_ string, _ ...interface{})
+ Debugln(_ ...interface{})
+ Error(_ ...interface{})
+ Errorf(_ string, _ ...interface{})
+ Errorln(_ ...interface{})
+ Fatal(_ ...interface{})
+ Fatalf(_ string, _ ...interface{})
+ Fatalln(_ ...interface{})
+ Info(_ ...interface{})
+ Infof(_ string, _ ...interface{})
+ Infoln(_ ...interface{})
+ Panic(_ ...interface{})
+ Panicf(_ string, _ ...interface{})
+ Panicln(_ ...interface{})
+ Print(_ ...interface{})
+ Printf(_ string, _ ...interface{})
+ Println(_ ...interface{})
+ Warn(_ ...interface{})
+ Warnf(_ string, _ ...interface{})
+ Warning(_ ...interface{})
+ Warningf(_ string, _ ...interface{})
+ Warningln(_ ...interface{})
+ Warnln(_ ...interface{})
+ WithError(_ error) *Entry
+ WithField(_ string, _ interface{}) *Entry
+ WithFields(_ Fields) *Entry
+}
+
type Fields map[string]interface{}
type Formatter interface {
@@ -332,6 +362,14 @@ func (_ *Logger) WriterLevel(_ Level) *io.PipeWriter {
return nil
}
+func New() *Logger {
+ return nil
+}
+
+func NewEntry(_ *Logger) *Entry {
+ return nil
+}
+
func Panic(_ ...interface{}) {}
func Panicf(_ string, _ ...interface{}) {}
@@ -362,6 +400,10 @@ func Warningln(_ ...interface{}) {}
func Warnln(_ ...interface{}) {}
+func WithError(_ error) *Entry {
+ return nil
+}
+
func WithField(_ string, _ interface{}) *Entry {
return nil
}
diff --git a/go/ql/test/query-tests/Security/CWE-117/vendor/modules.txt b/go/ql/test/query-tests/Security/CWE-117/vendor/modules.txt
index a6227c09a936..fa7cb0a9a823 100644
--- a/go/ql/test/query-tests/Security/CWE-117/vendor/modules.txt
+++ b/go/ql/test/query-tests/Security/CWE-117/vendor/modules.txt
@@ -1,24 +1,134 @@
# github.com/astaxie/beego v1.12.3
-## explicit
+## explicit; go 1.13
github.com/astaxie/beego
+github.com/astaxie/beego/config
+github.com/astaxie/beego/context
+github.com/astaxie/beego/context/param
+github.com/astaxie/beego/grace
+github.com/astaxie/beego/logs
+github.com/astaxie/beego/session
+github.com/astaxie/beego/toolbox
+github.com/astaxie/beego/utils
+# github.com/beorn7/perks v1.0.1
+## explicit; go 1.11
+github.com/beorn7/perks/quantile
+# github.com/cespare/xxhash/v2 v2.1.1
+## explicit; go 1.11
+github.com/cespare/xxhash/v2
+# github.com/davecgh/go-spew v1.1.1
+## explicit
+github.com/davecgh/go-spew/spew
# github.com/elazarl/goproxy v0.0.0-20211114080932-d06c3be7c11b
## explicit
github.com/elazarl/goproxy
# github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
## explicit
github.com/golang/glog
-# github.com/kr/text v0.2.0
+# github.com/golang/protobuf v1.4.2
+## explicit; go 1.9
+github.com/golang/protobuf/proto
+github.com/golang/protobuf/ptypes
+github.com/golang/protobuf/ptypes/any
+github.com/golang/protobuf/ptypes/duration
+github.com/golang/protobuf/ptypes/timestamp
+# github.com/hashicorp/golang-lru v0.5.4
+## explicit; go 1.12
+github.com/hashicorp/golang-lru
+github.com/hashicorp/golang-lru/simplelru
+# github.com/matttproud/golang_protobuf_extensions v1.0.1
## explicit
-github.com/kr/text
-# github.com/sirupsen/logrus v1.8.1
+github.com/matttproud/golang_protobuf_extensions/pbutil
+# github.com/prometheus/client_golang v1.7.0
+## explicit; go 1.11
+github.com/prometheus/client_golang/prometheus
+github.com/prometheus/client_golang/prometheus/internal
+github.com/prometheus/client_golang/prometheus/promhttp
+# github.com/prometheus/client_model v0.2.0
+## explicit; go 1.9
+github.com/prometheus/client_model/go
+# github.com/prometheus/common v0.10.0
+## explicit; go 1.11
+github.com/prometheus/common/expfmt
+github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg
+github.com/prometheus/common/model
+# github.com/prometheus/procfs v0.1.3
+## explicit; go 1.12
+github.com/prometheus/procfs
+github.com/prometheus/procfs/internal/fs
+github.com/prometheus/procfs/internal/util
+# github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644
## explicit
+github.com/shiena/ansicolor
+# github.com/sirupsen/logrus v1.8.1
+## explicit; go 1.13
github.com/sirupsen/logrus
-# github.com/stretchr/testify v1.6.0
+# go.uber.org/multierr v1.10.0
+## explicit; go 1.19
+go.uber.org/multierr
+# go.uber.org/zap v1.27.0
+## explicit; go 1.19
+go.uber.org/zap
+go.uber.org/zap/buffer
+go.uber.org/zap/internal
+go.uber.org/zap/internal/bufferpool
+go.uber.org/zap/internal/color
+go.uber.org/zap/internal/exit
+go.uber.org/zap/internal/pool
+go.uber.org/zap/internal/stacktrace
+go.uber.org/zap/zapcore
+# golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
## explicit
-github.com/stretchr/testify
+golang.org/x/crypto/acme
+golang.org/x/crypto/acme/autocert
+# golang.org/x/net v0.0.0-20190620200207-3b0461eec859
+## explicit; go 1.11
+golang.org/x/net/idna
# golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f
+## explicit; go 1.12
+golang.org/x/sys/internal/unsafeheader
+golang.org/x/sys/unix
+golang.org/x/sys/windows
+# golang.org/x/text v0.3.0
## explicit
-golang.org/x/sys
-# k8s.io/klog v1.0.0
+golang.org/x/text/secure/bidirule
+golang.org/x/text/transform
+golang.org/x/text/unicode/bidi
+golang.org/x/text/unicode/norm
+# google.golang.org/protobuf v1.23.0
+## explicit; go 1.9
+google.golang.org/protobuf/encoding/prototext
+google.golang.org/protobuf/encoding/protowire
+google.golang.org/protobuf/internal/descfmt
+google.golang.org/protobuf/internal/descopts
+google.golang.org/protobuf/internal/detrand
+google.golang.org/protobuf/internal/encoding/defval
+google.golang.org/protobuf/internal/encoding/messageset
+google.golang.org/protobuf/internal/encoding/tag
+google.golang.org/protobuf/internal/encoding/text
+google.golang.org/protobuf/internal/errors
+google.golang.org/protobuf/internal/fieldnum
+google.golang.org/protobuf/internal/fieldsort
+google.golang.org/protobuf/internal/filedesc
+google.golang.org/protobuf/internal/filetype
+google.golang.org/protobuf/internal/flags
+google.golang.org/protobuf/internal/genname
+google.golang.org/protobuf/internal/impl
+google.golang.org/protobuf/internal/mapsort
+google.golang.org/protobuf/internal/pragma
+google.golang.org/protobuf/internal/set
+google.golang.org/protobuf/internal/strs
+google.golang.org/protobuf/internal/version
+google.golang.org/protobuf/proto
+google.golang.org/protobuf/reflect/protoreflect
+google.golang.org/protobuf/reflect/protoregistry
+google.golang.org/protobuf/runtime/protoiface
+google.golang.org/protobuf/runtime/protoimpl
+google.golang.org/protobuf/types/known/anypb
+google.golang.org/protobuf/types/known/durationpb
+google.golang.org/protobuf/types/known/timestamppb
+# gopkg.in/yaml.v2 v2.2.8
## explicit
+gopkg.in/yaml.v2
+# k8s.io/klog v1.0.0
+## explicit; go 1.12
k8s.io/klog
diff --git a/java/ql/automodel/publish.sh b/java/ql/automodel/publish.sh
deleted file mode 100755
index 7304f0da1809..000000000000
--- a/java/ql/automodel/publish.sh
+++ /dev/null
@@ -1,197 +0,0 @@
-#!/bin/bash
-set -e
-
-help="Usage: ./publish [--override-release] [--dry-run]
-Publish the automodel query pack.
-
-If no arguments are provided, publish the version of the codeql repo specified by the latest official release of the codeml-automodel repo.
-If the --override-release argument is provided, your current local HEAD is used (for unofficial releases or patching).
-If the --dry-run argument is provided, the release is not published (for testing purposes)."
-
-# Echo the help message
-if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
- echo "$help"
- exit 0
-fi
-
-# Check the number of arguments are valid
-if [ $# -gt 2 ]; then
- echo "Error: Invalid arguments provided"
- echo "$help"
- exit 1
-fi
-
-OVERRIDE_RELEASE=0
-DRY_RUN=0
-for arg in "$@"
-do
- case $arg in
- --override-release)
- OVERRIDE_RELEASE=1
- shift # Remove --override-release from processing
- ;;
- --dry-run)
- DRY_RUN=1
- shift # Remove --dry-run from processing
- ;;
- *)
- echo "Error: Invalid argument provided: $arg"
- echo "$help"
- exit 1
- ;;
- esac
-done
-
-# Describe what we're about to do based on the command-line arguments
-if [ $OVERRIDE_RELEASE = 1 ]; then
- echo "Publishing the current HEAD of the automodel repo"
-else
- echo "Publishing the version of the automodel repo specified by the latest official release of the codeml-automodel repo"
-fi
-if [ $DRY_RUN = 1 ]; then
- echo "Dry run: we will step through the process but we won't publish the query pack"
-else
- echo "Not a dry run! Publishing the query pack"
-fi
-
-# If we're publishing the codeml-automodel release then we will checkout the sha specified in the release.
-# So we need to check that there are no uncommitted changes in the local branch.
-# And, if we're publishing the current HEAD, it's cleaner to ensure that there are no uncommitted changes.
-if ! git diff --quiet; then
- echo "Error: Uncommitted changes exist. Please commit or stash your changes before publishing."
- exit 1
-fi
-
-# Check the above environment variables are set
-if [ -z "${GITHUB_TOKEN}" ]; then
- echo "Error: GITHUB_TOKEN environment variable not set. Please set this to a token with package:write permissions to codeql."
- exit 1
-fi
-if [ -z "${GH_TOKEN}" ]; then
- echo "Error: GH_TOKEN environment variable not set. Please set this to a token with repo permissions to github/codeml-automodel."
- exit 1
-fi
-
-# Get the sha of the previous release, i.e. the last commit to the main branch that updated the query pack version
-PREVIOUS_RELEASE_SHA=$(git rev-list -n 1 main -- ./src/qlpack.yml)
-if [ -z "$PREVIOUS_RELEASE_SHA" ]; then
- echo "Error: Could not get the sha of the previous release of codeml-automodel query pack"
- exit 1
-else
- echo "Previous query-pack release sha: $PREVIOUS_RELEASE_SHA"
-fi
-
-CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
-CURRENT_SHA=$(git rev-parse HEAD)
-
-if [ $OVERRIDE_RELEASE = 1 ]; then
- # Check that the current HEAD is downstream from PREVIOUS_RELEASE_SHA
- if ! git merge-base --is-ancestor "$PREVIOUS_RELEASE_SHA" "$CURRENT_SHA"; then
- echo "Error: The current HEAD is not downstream from the previous release"
- exit 1
- fi
-else
- # Get the latest release of codeml-automodel
- TAG_NAME=$(gh api -H 'Accept: application/vnd.github+json' -H 'X-GitHub-Api-Version: 2022-11-28' /repos/github/codeml-automodel/releases/latest | jq -r .tag_name)
- # Check TAG_NAME is not empty
- if [ -z "$TAG_NAME" ]; then
- echo "Error: Could not get latest release of codeml-automodel"
- exit 1
- fi
- echo "Updating to latest automodel release: $TAG_NAME"
- # Before downloading, delete any existing release.zip, and ignore failure if not present
- rm release.zip || true
- gh release download $TAG_NAME -A zip -O release.zip --repo 'https://github.com/github/codeml-automodel'
- # Before unzipping, delete any existing release directory, and ignore failure if not present
- rm -rf release || true
- unzip -o release.zip -d release
- REVISION=$(jq -r '.["codeql-sha"]' release/codeml-automodel*/codeml-automodel-release.json)
- echo "The latest codeml-automodel release specifies the codeql sha $REVISION"
- # Check that REVISION is downstream from PREVIOUS_RELEASE_SHA
- if ! git merge-base --is-ancestor "$PREVIOUS_RELEASE_SHA" "$REVISION"; then
- echo "Error: The codeql version $REVISION is not downstream of the query-pack version $PREVIOUS_RELEASE_SHA"
- exit 1
- fi
- # Get the version of the codeql code specified by the codeml-automodel release
- git checkout "$REVISION"
-fi
-
-# Get the absolute path of the automodel repo
-AUTOMODEL_ROOT="$(readlink -f "$(dirname $0)")"
-# Get the absolute path of the workspace root
-WORKSPACE_ROOT="$AUTOMODEL_ROOT/../../.."
-# Specify the groups of queries to test and publish
-GRPS="automodel,-test"
-
-# Install the codeql gh extension
-gh extensions install github/gh-codeql
-
-pushd "$AUTOMODEL_ROOT"
-echo Testing automodel queries
-gh codeql test run test
-popd
-
-pushd "$WORKSPACE_ROOT"
-echo "Preparing the release"
-gh codeql pack release --groups $GRPS -v
-
-if [ $DRY_RUN = 1 ]; then
- echo "Dry run: not publishing the query pack"
- gh codeql pack publish --groups $GRPS --dry-run -v
-else
- echo "Not a dry run! Publishing the query pack"
- gh codeql pack publish --groups $GRPS -v
-fi
-
-echo "Bumping versions"
-gh codeql pack post-release --groups $GRPS -v
-popd
-
-# The above commands update
-# ./src/CHANGELOG.md
-# ./src/codeql-pack.release.yml
-# ./src/qlpack.yml
-# and add a new file
-# ./src/change-notes/released/.md
-
-# Get the filename of the most recently created file in ./src/change-notes/released/*.md
-# This will be the file for the new release
-NEW_CHANGE_NOTES_FILE=$(ls -t ./src/change-notes/released/*.md | head -n 1)
-
-# Make a copy of the modified files
-mv ./src/CHANGELOG.md ./src/CHANGELOG.md.dry-run
-mv ./src/codeql-pack.release.yml ./src/codeql-pack.release.yml.dry-run
-mv ./src/qlpack.yml ./src/qlpack.yml.dry-run
-mv "$NEW_CHANGE_NOTES_FILE" ./src/change-notes/released.md.dry-run
-
-if [ $OVERRIDE_RELEASE = 1 ]; then
- # Restore the original files
- git checkout ./src/CHANGELOG.md
- git checkout ./src/codeql-pack.release.yml
- git checkout ./src/qlpack.yml
-else
- # Restore the original files
- git checkout "$CURRENT_BRANCH" --force
-fi
-
-if [ $DRY_RUN = 1 ]; then
- echo "Inspect the updated dry-run version files:"
- ls -l ./src/*.dry-run
- ls -l ./src/change-notes/*.dry-run
-else
- # Add the updated files to the current branch
- echo "Adding the version changes"
- mv -f ./src/CHANGELOG.md.dry-run ./src/CHANGELOG.md
- mv -f ./src/codeql-pack.release.yml.dry-run ./src/codeql-pack.release.yml
- mv -f ./src/qlpack.yml.dry-run ./src/qlpack.yml
- mv -f ./src/change-notes/released.md.dry-run "$NEW_CHANGE_NOTES_FILE"
- git add ./src/CHANGELOG.md
- git add ./src/codeql-pack.release.yml
- git add ./src/qlpack.yml
- git add "$NEW_CHANGE_NOTES_FILE"
- echo "Added the following updated version files to the current branch:"
- git status -s
- echo "To complete the release, please commit these files and merge to the main branch"
-fi
-
-echo "Done"
\ No newline at end of file
diff --git a/java/ql/automodel/src/AutomodelAlertSinkUtil.qll b/java/ql/automodel/src/AutomodelAlertSinkUtil.qll
deleted file mode 100644
index f20c8e57b6cc..000000000000
--- a/java/ql/automodel/src/AutomodelAlertSinkUtil.qll
+++ /dev/null
@@ -1,183 +0,0 @@
-private import java
-private import semmle.code.java.dataflow.ExternalFlow as ExternalFlow
-private import semmle.code.java.dataflow.TaintTracking
-private import semmle.code.java.security.RequestForgeryConfig
-private import semmle.code.java.security.CommandLineQuery
-private import semmle.code.java.security.SqlConcatenatedQuery
-private import semmle.code.java.security.SqlInjectionQuery
-private import semmle.code.java.security.UrlRedirectQuery
-private import semmle.code.java.security.TaintedPathQuery
-private import semmle.code.java.security.SqlInjectionQuery
-private import AutomodelJavaUtil
-
-private newtype TSinkModel =
- MkSinkModel(
- string package, string type, boolean subtypes, string name, string signature, string ext,
- string input, string kind, string provenance
- ) {
- ExternalFlow::sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance,
- _)
- }
-
-class SinkModel extends TSinkModel {
- string package;
- string type;
- boolean subtypes;
- string name;
- string signature;
- string ext;
- string input;
- string kind;
- string provenance;
-
- SinkModel() {
- this = MkSinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance)
- }
-
- /** Gets the package for this sink model. */
- string getPackage() { result = package }
-
- /** Gets the type for this sink model. */
- string getType() { result = type }
-
- /** Gets whether this sink model considers subtypes. */
- boolean getSubtypes() { result = subtypes }
-
- /** Gets the name for this sink model. */
- string getName() { result = name }
-
- /** Gets the signature for this sink model. */
- string getSignature() { result = signature }
-
- /** Gets the input for this sink model. */
- string getInput() { result = input }
-
- /** Gets the extension for this sink model. */
- string getExt() { result = ext }
-
- /** Gets the kind for this sink model. */
- string getKind() { result = kind }
-
- /** Gets the provenance for this sink model. */
- string getProvenance() { result = provenance }
-
- /** Gets the number of instances of this sink model. */
- int getInstanceCount() { result = count(PotentialSinkModelExpr p | p.getSinkModel() = this) }
-
- /** Gets a string representation of this sink model. */
- string toString() {
- result =
- "SinkModel(" + package + ", " + type + ", " + subtypes + ", " + name + ", " + signature + ", "
- + ext + ", " + input + ", " + kind + ", " + provenance + ")"
- }
-
- /** Gets a string representation of this sink model as it would appear in a Models-as-Data file. */
- string getRepr() {
- result =
- "\"" + package + "\", \"" + type + "\", " + pyBool(subtypes) + ", \"" + name + "\", \"" +
- signature + "\", \"" + ext + "\", \"" + input + "\", \"" + kind + "\", \"" + provenance +
- "\""
- }
-}
-
-/** An expression that may correspond to a sink model. */
-class PotentialSinkModelExpr extends Expr {
- /**
- * Holds if this expression has the given signature. The signature should contain enough
- * information to determine a corresponding sink model, if one exists.
- */
- pragma[nomagic]
- predicate hasSignature(
- string package, string type, boolean subtypes, string name, string signature, string input
- ) {
- exists(Call call, Callable callable, int argIdx |
- call.getCallee().getSourceDeclaration() = callable and
- (
- this = call.getArgument(argIdx)
- or
- this = call.getQualifier() and argIdx = -1
- ) and
- (if argIdx = -1 then input = "Argument[this]" else input = "Argument[" + argIdx + "]") and
- package = callable.getDeclaringType().getPackage().getName() and
- type = callable.getDeclaringType().getErasure().(RefType).getNestedName() and
- subtypes = considerSubtypes(callable) and
- name = callable.getName() and
- signature = ExternalFlow::paramsString(callable)
- )
- }
-
- /** Gets a sink model that corresponds to this expression. */
- SinkModel getSinkModel() {
- this.hasSignature(result.getPackage(), result.getType(), result.getSubtypes(), result.getName(),
- result.getSignature(), result.getInput())
- }
-}
-
-private string pyBool(boolean b) {
- b = true and result = "True"
- or
- b = false and result = "False"
-}
-
-/**
- * Gets a string representation of the existing sink model at the expression `e`, in the format in
- * which it would appear in a Models-as-Data file. Also restricts the provenance of the sink model
- * to be `ai-generated`.
- */
-string getSinkModelRepr(PotentialSinkModelExpr e) {
- result = e.getSinkModel().getRepr() and
- e.getSinkModel().getProvenance() = "ai-generated"
-}
-
-/**
- * Gets the string representation of a sink model in a format suitable for appending to an alert
- * message.
- */
-string getSinkModelQueryRepr(PotentialSinkModelExpr e) {
- result = "\nsinkModel: " + getSinkModelRepr(e)
-}
-
-/**
- * A parameterised module that takes a dataflow config, and exposes a predicate for counting the
- * number of AI-generated sink models that appear in alerts for that query.
- */
-private module SinkTallier {
- module ConfigFlow = TaintTracking::Global;
-
- predicate getSinkModelCount(int c, SinkModel s) {
- s = any(ConfigFlow::PathNode sink).getNode().asExpr().(PotentialSinkModelExpr).getSinkModel() and
- c =
- strictcount(ConfigFlow::PathNode sink |
- ConfigFlow::flowPath(_, sink) and
- s = sink.getNode().asExpr().(PotentialSinkModelExpr).getSinkModel()
- )
- }
-}
-
-predicate sinkModelTallyPerQuery(string queryName, int alertCount, SinkModel sinkModel) {
- queryName = "java/request-forgery" and
- SinkTallier::getSinkModelCount(alertCount, sinkModel)
- or
- queryName = "java/command-line-injection" and
- SinkTallier::getSinkModelCount(alertCount, sinkModel)
- or
- queryName = "java/concatenated-sql-query" and
- SinkTallier::getSinkModelCount(alertCount, sinkModel)
- or
- queryName = "java/ssrf" and
- SinkTallier::getSinkModelCount(alertCount, sinkModel)
- or
- queryName = "java/path-injection" and
- SinkTallier::getSinkModelCount(alertCount, sinkModel)
- or
- queryName = "java/unvalidated-url-redirection" and
- SinkTallier::getSinkModelCount(alertCount, sinkModel)
- or
- queryName = "java/sql-injection" and
- SinkTallier::getSinkModelCount(alertCount, sinkModel)
-}
-
-predicate sinkModelTally(int alertCount, SinkModel sinkModel) {
- sinkModelTallyPerQuery(_, _, sinkModel) and
- alertCount = sum(int c | sinkModelTallyPerQuery(_, c, sinkModel))
-}
diff --git a/java/ql/automodel/src/AutomodelAlertSinks.ql b/java/ql/automodel/src/AutomodelAlertSinks.ql
deleted file mode 100644
index e9af51b4d637..000000000000
--- a/java/ql/automodel/src/AutomodelAlertSinks.ql
+++ /dev/null
@@ -1,16 +0,0 @@
-/**
- * @name Number of alerts per sink model
- * @description Counts the number of alerts using `ai-generated` sink models.
- * @kind table
- * @id java/ml/metrics-count-alerts-per-sink-model
- * @tags internal automodel metrics
- */
-
-private import java
-private import AutomodelAlertSinkUtil
-
-from int alertCount, SinkModel s
-where sinkModelTally(alertCount, s) and s.getProvenance() = "ai-generated"
-select alertCount, s.getPackage() as package, s.getType() as type, s.getSubtypes() as subtypes,
- s.getName() as name, s.getSignature() as signature, s.getInput() as input, s.getExt() as ext,
- s.getKind() as kind, s.getProvenance() as provenance order by alertCount desc
diff --git a/java/ql/automodel/src/AutomodelAlertSinksPerQuery.ql b/java/ql/automodel/src/AutomodelAlertSinksPerQuery.ql
deleted file mode 100644
index 64a5038d1166..000000000000
--- a/java/ql/automodel/src/AutomodelAlertSinksPerQuery.ql
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * @name Number of alerts per sink model and query
- * @description Counts the number of alerts per query using `ai-generated` sink models.
- * @kind table
- * @id java/ml/metrics-count-alerts-per-sink-model-and-query
- * @tags internal automodel metrics
- */
-
-private import java
-private import AutomodelAlertSinkUtil
-
-from string queryId, int alertCount, SinkModel s
-where
- sinkModelTallyPerQuery(queryId, alertCount, s) and
- s.getProvenance() = "ai-generated"
-select queryId, alertCount, s.getPackage() as package, s.getType() as type,
- s.getSubtypes() as subtypes, s.getName() as name, s.getSignature() as signature,
- s.getInput() as input, s.getExt() as ext, s.getKind() as kind, s.getProvenance() as provenance
- order by queryId, alertCount desc
diff --git a/java/ql/automodel/src/AutomodelApplicationModeCharacteristics.qll b/java/ql/automodel/src/AutomodelApplicationModeCharacteristics.qll
deleted file mode 100644
index 750d776891f3..000000000000
--- a/java/ql/automodel/src/AutomodelApplicationModeCharacteristics.qll
+++ /dev/null
@@ -1,677 +0,0 @@
-/**
- * For internal use only.
- */
-
-private import java
-private import semmle.code.Location as Location
-private import semmle.code.java.dataflow.DataFlow
-private import semmle.code.java.dataflow.TaintTracking
-private import semmle.code.java.dataflow.ExternalFlow as ExternalFlow
-private import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
-private import semmle.code.java.security.ExternalAPIs as ExternalAPIs
-private import semmle.code.java.Expr as Expr
-private import semmle.code.java.security.QueryInjection
-private import semmle.code.java.dataflow.internal.ModelExclusions as ModelExclusions
-private import AutomodelJavaUtil as AutomodelJavaUtil
-private import semmle.code.java.security.PathSanitizer as PathSanitizer
-import AutomodelSharedCharacteristics as SharedCharacteristics
-import AutomodelEndpointTypes as AutomodelEndpointTypes
-
-newtype JavaRelatedLocationType =
- CallContext() or
- MethodDoc() or
- ClassDoc()
-
-newtype TApplicationModeEndpoint =
- TExplicitArgument(Call call, DataFlow::Node arg) {
- AutomodelJavaUtil::isFromSource(call) and
- exists(Argument argExpr |
- arg.asExpr() = argExpr and call = argExpr.getCall() and not argExpr.isVararg()
- ) and
- not AutomodelJavaUtil::isUnexploitableType(arg.getType())
- } or
- TInstanceArgument(Call call, DataFlow::Node arg) {
- AutomodelJavaUtil::isFromSource(call) and
- arg = DataFlow::getInstanceArgument(call) and
- not call instanceof ConstructorCall and
- not AutomodelJavaUtil::isUnexploitableType(arg.getType())
- } or
- TImplicitVarargsArray(Call call, DataFlow::ImplicitVarargsArray arg, int idx) {
- AutomodelJavaUtil::isFromSource(call) and
- call = arg.getCall() and
- idx = call.getCallee().getVaragsParameterIndex() and
- not AutomodelJavaUtil::isUnexploitableType(arg.getType())
- } or
- TMethodReturnValue(MethodCall call) {
- AutomodelJavaUtil::isFromSource(call) and
- not AutomodelJavaUtil::isUnexploitableType(call.getType())
- } or
- TOverriddenParameter(Parameter p, Method overriddenMethod) {
- AutomodelJavaUtil::isFromSource(p) and
- p.getCallable().(Method).overrides(overriddenMethod)
- }
-
-/**
- * An endpoint is a node that is a candidate for modeling.
- */
-abstract private class ApplicationModeEndpoint extends TApplicationModeEndpoint {
- /**
- * Gets the callable to be modeled that this endpoint represents.
- */
- abstract Callable getCallable();
-
- /**
- * Gets the input (if any) for this endpoint, eg.: `Argument[0]`.
- *
- * For endpoints that are source candidates, this will be `none()`.
- */
- abstract string getMaDInput();
-
- /**
- * Gets the output (if any) for this endpoint, eg.: `ReturnValue`.
- *
- * For endpoints that are sink candidates, this will be `none()`.
- */
- abstract string getMaDOutput();
-
- abstract Top asTop();
-
- /**
- * Converts the endpoint to a node that can be used in a data flow graph.
- */
- abstract DataFlow::Node asNode();
-
- string getExtensibleType() {
- if not exists(this.getMaDInput()) and exists(this.getMaDOutput())
- then result = "sourceModel"
- else
- if exists(this.getMaDInput()) and not exists(this.getMaDOutput())
- then result = "sinkModel"
- else none() // if both exist, it would be a summaryModel (not yet supported)
- }
-
- abstract string toString();
-}
-
-class TCallArgument = TExplicitArgument or TInstanceArgument or TImplicitVarargsArray;
-
-/**
- * An endpoint that represents an "argument" to a call in a broad sense, including
- * both explicit arguments and the instance argument.
- */
-abstract class CallArgument extends ApplicationModeEndpoint, TCallArgument {
- Call call;
- DataFlow::Node arg;
-
- override Callable getCallable() { result = call.getCallee().getSourceDeclaration() }
-
- override string getMaDOutput() { none() }
-
- override DataFlow::Node asNode() { result = arg }
-
- Call getCall() { result = call }
-
- override string toString() { result = arg.toString() }
-}
-
-/**
- * An endpoint that represents an explicit argument to a call.
- */
-class ExplicitArgument extends CallArgument, TExplicitArgument {
- ExplicitArgument() { this = TExplicitArgument(call, arg) }
-
- private int getArgIndex() { this.asTop() = call.getArgument(result) }
-
- override string getMaDInput() { result = "Argument[" + this.getArgIndex() + "]" }
-
- override Top asTop() { result = arg.asExpr() }
-}
-
-/**
- * An endpoint that represents the instance argument to a call.
- */
-class InstanceArgument extends CallArgument, TInstanceArgument {
- InstanceArgument() { this = TInstanceArgument(call, arg) }
-
- override string getMaDInput() { result = "Argument[this]" }
-
- override Top asTop() { if exists(arg.asExpr()) then result = arg.asExpr() else result = call }
-
- override string toString() { result = arg.toString() }
-}
-
-/**
- * An endpoint that represents an implicit varargs array.
- * We choose to represent the varargs array as a single endpoint, rather than as multiple endpoints.
- *
- * This avoids the problem of having to deal with redundant endpoints downstream.
- *
- * In order to be able to distinguish between varargs endpoints and regular endpoints, we export the `isVarargsArray`
- * meta data field in the extraction queries.
- */
-class ImplicitVarargsArray extends CallArgument, TImplicitVarargsArray {
- int idx;
-
- ImplicitVarargsArray() { this = TImplicitVarargsArray(call, arg, idx) }
-
- override string getMaDInput() { result = "Argument[" + idx + "]" }
-
- override Top asTop() { result = call }
-}
-
-/**
- * An endpoint that represents a method call. The `ReturnValue` of a method call
- * may be a source.
- */
-class MethodReturnValue extends ApplicationModeEndpoint, TMethodReturnValue {
- MethodCall call;
-
- MethodReturnValue() { this = TMethodReturnValue(call) }
-
- override Callable getCallable() { result = call.getCallee().getSourceDeclaration() }
-
- override string getMaDInput() { none() }
-
- override string getMaDOutput() { result = "ReturnValue" }
-
- override Top asTop() { result = call }
-
- override DataFlow::Node asNode() { result.asExpr() = call }
-
- override string toString() { result = call.toString() }
-}
-
-/**
- * An endpoint that represents a parameter of an overridden method that may be
- * a source.
- */
-class OverriddenParameter extends ApplicationModeEndpoint, TOverriddenParameter {
- Parameter p;
- Method overriddenMethod;
-
- OverriddenParameter() { this = TOverriddenParameter(p, overriddenMethod) }
-
- override Callable getCallable() {
- // NB: we're returning the overridden callable here. This means that the
- // candidate model will be about the overridden method, not the overriding
- // method. This is a more general model, that also applies to other
- // subclasses of the overridden class.
- result = overriddenMethod.getSourceDeclaration()
- }
-
- private int getArgIndex() { p.getCallable().getParameter(result) = p }
-
- override string getMaDInput() { none() }
-
- override string getMaDOutput() { result = "Parameter[" + this.getArgIndex() + "]" }
-
- override Top asTop() { result = p }
-
- override DataFlow::Node asNode() { result.(DataFlow::ParameterNode).asParameter() = p }
-
- override string toString() { result = p.toString() }
-}
-
-/**
- * A candidates implementation.
- *
- * Some important notes:
- * - This mode is using arguments as endpoints.
- * - We use the `CallContext` (the surrounding call expression) as related location.
- */
-module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig {
- // for documentation of the implementations here, see the QLDoc in the CandidateSig signature module.
- class Endpoint = ApplicationModeEndpoint;
-
- class EndpointType = AutomodelEndpointTypes::EndpointType;
-
- class SinkType = AutomodelEndpointTypes::SinkType;
-
- class SourceType = AutomodelEndpointTypes::SourceType;
-
- class RelatedLocation = Location::Top;
-
- class RelatedLocationType = JavaRelatedLocationType;
-
- // Sanitizers are currently not modeled in MaD. TODO: check if this has large negative impact.
- predicate isSanitizer(Endpoint e, EndpointType t) {
- exists(t) and
- AutomodelJavaUtil::isUnexploitableType([
- // for most endpoints, we can get the type from the node
- e.asNode().getType(),
- // but not for calls to void methods, where we need to go via the AST
- e.asTop().(Expr).getType()
- ])
- or
- t instanceof AutomodelEndpointTypes::PathInjectionSinkType and
- e.asNode() instanceof PathSanitizer::PathInjectionSanitizer
- }
-
- RelatedLocation asLocation(Endpoint e) { result = e.asTop() }
-
- predicate isKnownKind = AutomodelJavaUtil::isKnownKind/2;
-
- predicate isSink(Endpoint e, string kind, string provenance) {
- exists(
- string package, string type, boolean subtypes, string name, string signature, string ext,
- string input
- |
- sinkSpec(e, package, type, subtypes, name, signature, ext, input) and
- ExternalFlow::sinkModel(package, type, subtypes, name, [signature, ""], ext, input, kind,
- provenance, _)
- )
- or
- isCustomSink(e, kind) and provenance = "custom-sink"
- }
-
- predicate isSource(Endpoint e, string kind, string provenance) {
- exists(
- string package, string type, boolean subtypes, string name, string signature, string ext,
- string output
- |
- sourceSpec(e, package, type, subtypes, name, signature, ext, output) and
- ExternalFlow::sourceModel(package, type, subtypes, name, [signature, ""], ext, output, kind,
- provenance, _)
- )
- }
-
- predicate isNeutral(Endpoint e) {
- exists(string package, string type, string name, string signature, string endpointType |
- sinkSpec(e, package, type, _, name, signature, _, _) and
- endpointType = "sink"
- or
- sourceSpec(e, package, type, _, name, signature, _, _) and
- endpointType = "source"
- |
- ExternalFlow::neutralModel(package, type, name, [signature, ""], endpointType, _)
- )
- }
-
- /**
- * Holds if the endpoint concerns a callable with the given package, type, name and signature.
- *
- * If `subtypes` is `false`, only the exact callable is considered. If `true`, the callable and
- * all its overrides are considered.
- */
- additional predicate endpointCallable(
- Endpoint e, string package, string type, boolean subtypes, string name, string signature
- ) {
- exists(Callable c |
- c = e.getCallable() and subtypes in [true, false]
- or
- e.getCallable().(Method).getSourceDeclaration().overrides+(c) and subtypes = true
- |
- c.hasQualifiedName(package, type, name) and
- signature = ExternalFlow::paramsString(c)
- )
- }
-
- additional predicate sinkSpec(
- Endpoint e, string package, string type, boolean subtypes, string name, string signature,
- string ext, string input
- ) {
- endpointCallable(e, package, type, subtypes, name, signature) and
- ext = "" and
- input = e.getMaDInput()
- }
-
- additional predicate sourceSpec(
- Endpoint e, string package, string type, boolean subtypes, string name, string signature,
- string ext, string output
- ) {
- endpointCallable(e, package, type, subtypes, name, signature) and
- ext = "" and
- output = e.getMaDOutput()
- }
-
- /**
- * Gets the related location for the given endpoint.
- *
- * The only related location we model is the the call expression surrounding to
- * which the endpoint is either argument or qualifier (known as the call context).
- */
- RelatedLocation getRelatedLocation(Endpoint e, RelatedLocationType type) {
- type = CallContext() and
- result = e.(CallArgument).getCall()
- or
- type = MethodDoc() and
- result = e.getCallable().(Documentable).getJavadoc()
- or
- type = ClassDoc() and
- result = e.getCallable().getDeclaringType().(Documentable).getJavadoc()
- }
-}
-
-/**
- * Contains endpoints that are defined in QL code rather than as a MaD model. Ideally this predicate
- * should be empty.
- */
-private predicate isCustomSink(Endpoint e, string kind) {
- e.asNode() instanceof QueryInjectionSink and kind = "sql"
-}
-
-module CharacteristicsImpl =
- SharedCharacteristics::SharedCharacteristics;
-
-class EndpointCharacteristic = CharacteristicsImpl::EndpointCharacteristic;
-
-class Endpoint = ApplicationCandidatesImpl::Endpoint;
-
-/*
- * Predicates that are used to surface prompt examples and candidates for classification with an ML model.
- */
-
-/**
- * A MetadataExtractor that extracts metadata for application mode.
- */
-class ApplicationModeMetadataExtractor extends string {
- ApplicationModeMetadataExtractor() { this = "ApplicationModeMetadataExtractor" }
-
- predicate hasMetadata(
- Endpoint e, string package, string type, string subtypes, string name, string signature,
- string input, string output, string isVarargsArray, string alreadyAiModeled,
- string extensibleType
- ) {
- exists(Callable callable | e.getCallable() = callable |
- (if exists(e.getMaDInput()) then input = e.getMaDInput() else input = "") and
- (if exists(e.getMaDOutput()) then output = e.getMaDOutput() else output = "") and
- package = callable.getDeclaringType().getPackage().getName() and
- // we're using the erased types because the MaD convention is to not specify type parameters.
- // Whether something is or isn't a sink doesn't usually depend on the type parameters.
- type = callable.getDeclaringType().getErasure().(RefType).getNestedName() and
- subtypes = AutomodelJavaUtil::considerSubtypes(callable).toString() and
- name = callable.getName() and
- signature = ExternalFlow::paramsString(callable) and
- (
- if e instanceof ImplicitVarargsArray
- then isVarargsArray = "true"
- else isVarargsArray = "false"
- ) and
- extensibleType = e.getExtensibleType()
- ) and
- (
- not CharacteristicsImpl::isModeled(e, _, extensibleType, _) and alreadyAiModeled = ""
- or
- CharacteristicsImpl::isModeled(e, _, extensibleType, alreadyAiModeled)
- )
- }
-}
-
-/**
- * Holds if the given `endpoint` should be considered a candidate for the `extensibleType`.
- *
- * The other parameters record various other properties of interest.
- */
-predicate isCandidate(
- Endpoint endpoint, string package, string type, string subtypes, string name, string signature,
- string input, string output, string isVarargs, string extensibleType, string alreadyAiModeled
-) {
- CharacteristicsImpl::isCandidate(endpoint, _) and
- not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
- u.appliesToEndpoint(endpoint)
- ) and
- any(ApplicationModeMetadataExtractor meta)
- .hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, isVarargs,
- alreadyAiModeled, extensibleType) and
- // If a node is already modeled in MaD, we don't include it as a candidate. Otherwise, we might include it as a
- // candidate for query A, but the model will label it as a sink for one of the sink types of query B, for which it's
- // already a known sink. This would result in overlap between our detected sinks and the pre-existing modeling. We
- // assume that, if a sink has already been modeled in a MaD model, then it doesn't belong to any additional sink
- // types, and we don't need to reexamine it.
- alreadyAiModeled.matches(["", "%ai-%"]) and
- AutomodelJavaUtil::includeAutomodelCandidate(package, type, name, signature)
-}
-
-/**
- * Holds if the given `endpoint` is a negative example for the `extensibleType`
- * because of the `characteristic`.
- *
- * The other parameters record various other properties of interest.
- */
-predicate isNegativeExample(
- Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string package,
- string type, string subtypes, string name, string signature, string input, string output,
- string isVarargsArray, string extensibleType
-) {
- characteristic.appliesToEndpoint(endpoint) and
- // the node is known not to be an endpoint of any appropriate type
- forall(AutomodelEndpointTypes::EndpointType tp |
- tp = CharacteristicsImpl::getAPotentialType(endpoint)
- |
- characteristic.hasImplications(tp, false, _)
- ) and
- // the lowest confidence across all endpoint types should be at least highConfidence
- confidence =
- min(float c |
- characteristic.hasImplications(CharacteristicsImpl::getAPotentialType(endpoint), false, c)
- ) and
- confidence >= SharedCharacteristics::highConfidence() and
- any(ApplicationModeMetadataExtractor meta)
- .hasMetadata(endpoint, package, type, subtypes, name, signature, input, output,
- isVarargsArray, _, extensibleType) and
- // It's valid for a node to be both a potential source/sanitizer and a sink. We don't want to include such nodes
- // as negative examples in the prompt, because they're ambiguous and might confuse the model, so we explicitly exclude them here.
- not exists(EndpointCharacteristic characteristic2, float confidence2 |
- characteristic2 != characteristic
- |
- characteristic2.appliesToEndpoint(endpoint) and
- confidence2 >= SharedCharacteristics::maximalConfidence() and
- characteristic2
- .hasImplications(CharacteristicsImpl::getAPotentialType(endpoint), true, confidence2)
- )
-}
-
-/**
- * Holds if the given `endpoint` is a positive example for the `endpointType`.
- *
- * The other parameters record various other properties of interest.
- */
-predicate isPositiveExample(
- Endpoint endpoint, string endpointType, string package, string type, string subtypes, string name,
- string signature, string input, string output, string isVarargsArray, string extensibleType
-) {
- any(ApplicationModeMetadataExtractor meta)
- .hasMetadata(endpoint, package, type, subtypes, name, signature, input, output,
- isVarargsArray, _, extensibleType) and
- CharacteristicsImpl::isKnownAs(endpoint, endpointType, _) and
- exists(CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()))
-}
-
-/*
- * EndpointCharacteristic classes that are specific to Automodel for Java.
- */
-
-/**
- * A negative characteristic that indicates that parameters of an is-style boolean method should not be considered sinks.
- *
- * A sink is highly unlikely to be exploitable if its callable's name starts with `is` and the callable has a boolean return
- * type (e.g. `isDirectory`). These kinds of calls normally do only checks, and appear before the proper call that does
- * the dangerous/interesting thing, so we want the latter to be modeled as the sink.
- *
- * TODO: this might filter too much, it's possible that methods with more than one parameter contain interesting sinks
- */
-private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
- UnexploitableIsCharacteristic() { this = "argument of is-style boolean method" }
-
- override predicate appliesToEndpoint(Endpoint e) {
- e.getCallable().getName().matches("is%") and
- e.getCallable().getReturnType() instanceof BooleanType and
- not ApplicationCandidatesImpl::isSink(e, _, _)
- }
-}
-
-/**
- * A negative characteristic that indicates that parameters of an existence-checking boolean method should not be
- * considered sinks.
- *
- * A sink is highly unlikely to be exploitable if its callable's name is `exists` or `notExists` and the callable has a
- * boolean return type. These kinds of calls normally do only checks, and appear before the proper call that does the
- * dangerous/interesting thing, so we want the latter to be modeled as the sink.
- */
-private class UnexploitableExistsCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
- UnexploitableExistsCharacteristic() { this = "argument of existence-checking boolean method" }
-
- override predicate appliesToEndpoint(Endpoint e) {
- exists(Callable callable | callable = e.getCallable() |
- callable.getName().toLowerCase() = ["exists", "notexists"] and
- callable.getReturnType() instanceof BooleanType
- )
- }
-}
-
-/**
- * A negative characteristic that indicates that parameters of an exception method or constructor should not be considered sinks,
- * and its return value should not be considered a source.
- */
-private class ExceptionCharacteristic extends CharacteristicsImpl::NeitherSourceNorSinkCharacteristic
-{
- ExceptionCharacteristic() { this = "argument/result of exception-related method" }
-
- override predicate appliesToEndpoint(Endpoint e) {
- e.getCallable().getDeclaringType().getASupertype*() instanceof TypeThrowable and
- (
- e.getExtensibleType() = "sinkModel" and
- not ApplicationCandidatesImpl::isSink(e, _, _)
- or
- e.getExtensibleType() = "sourceModel" and
- not ApplicationCandidatesImpl::isSource(e, _, _) and
- e.getMaDOutput() = "ReturnValue"
- )
- }
-}
-
-/**
- * A negative characteristic that indicates that an endpoint is a MaD taint step. MaD modeled taint steps are global,
- * so they are not sinks for any query. Non-MaD taint steps might be specific to a particular query, so we don't
- * filter those out.
- */
-private class IsMaDTaintStepCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
- IsMaDTaintStepCharacteristic() { this = "taint step" }
-
- override predicate appliesToEndpoint(Endpoint e) {
- FlowSummaryImpl::Private::Steps::summaryThroughStepValue(e.asNode(), _, _)
- or
- FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(e.asNode(), _, _)
- or
- FlowSummaryImpl::Private::Steps::summaryGetterStep(e.asNode(), _, _, _)
- or
- FlowSummaryImpl::Private::Steps::summarySetterStep(e.asNode(), _, _, _)
- }
-}
-
-/**
- * A call to a method that's known locally will not be considered as a candidate to model.
- *
- * The reason is that we would expect data/taint flow into the method implementation to uncover
- * any sinks that are present there.
- */
-private class LocalCall extends CharacteristicsImpl::UninterestingToModelCharacteristic {
- LocalCall() { this = "local call" }
-
- override predicate appliesToEndpoint(Endpoint e) {
- e.(CallArgument).getCallable().fromSource()
- or
- e.(MethodReturnValue).getCallable().fromSource()
- }
-}
-
-/**
- * A characteristic that marks endpoints as uninteresting to model, according to the Java ModelExclusions module.
- */
-private class ExcludedFromModeling extends CharacteristicsImpl::UninterestingToModelCharacteristic {
- ExcludedFromModeling() { this = "excluded from modeling" }
-
- override predicate appliesToEndpoint(Endpoint e) {
- ModelExclusions::isUninterestingForModels(e.getCallable())
- }
-}
-
-/**
- * A negative characteristic that filters out non-public methods. Non-public methods are not interesting to include in
- * the standard Java modeling, because they cannot be called from outside the package.
- */
-private class NonPublicMethodCharacteristic extends CharacteristicsImpl::UninterestingToModelCharacteristic
-{
- NonPublicMethodCharacteristic() { this = "non-public method" }
-
- override predicate appliesToEndpoint(Endpoint e) {
- exists(Callable c | c = e.getCallable() | not c.isPublic())
- }
-}
-
-/**
- * A negative characteristic that indicates that an endpoint is a non-sink argument to a method whose sinks have already
- * been modeled _manually_. This is restricted to manual sinks only, because only during the manual process do we have
- * the expectation that all sinks present in a method have been considered.
- *
- * WARNING: These endpoints should not be used as negative samples for training, because some sinks may have been missed
- * when the method was modeled. Specifically, as we start using ATM to merge in new declarations, we can be less sure
- * that a method with one argument modeled as a MaD sink has also had its remaining arguments manually reviewed. The
- * ML model might have predicted argument 0 of some method to be a sink but not argument 1, when in fact argument 1 is
- * also a sink.
- */
-private class OtherArgumentToModeledMethodCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic
-{
- OtherArgumentToModeledMethodCharacteristic() {
- this = "other argument to a method that has already been modeled manually"
- }
-
- override predicate appliesToEndpoint(Endpoint e) {
- not ApplicationCandidatesImpl::isSink(e, _, _) and
- exists(CallArgument otherSink |
- ApplicationCandidatesImpl::isSink(otherSink, _, "manual") and
- e.(CallArgument).getCall() = otherSink.getCall() and
- e != otherSink
- )
- }
-}
-
-/**
- * Holds if the type of the given expression is annotated with `@FunctionalInterface`.
- */
-predicate hasFunctionalInterfaceType(Expr e) {
- exists(RefType tp | tp = e.getType().getErasure() |
- tp.getAnAssociatedAnnotation().getType().hasQualifiedName("java.lang", "FunctionalInterface")
- )
-}
-
-/**
- * A characteristic that marks functional expression as likely not sinks.
- *
- * These expressions may well _contain_ sinks, but rarely are sinks themselves.
- */
-private class FunctionValueCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic {
- FunctionValueCharacteristic() { this = "function value" }
-
- override predicate appliesToEndpoint(Endpoint e) {
- exists(Expr expr | expr = e.asNode().asExpr() |
- expr instanceof FunctionalExpr or hasFunctionalInterfaceType(expr)
- )
- }
-}
-
-/**
- * A negative characteristic that indicates that an endpoint is not a `to` node for any known taint step. Such a node
- * cannot be tainted, because taint can't flow into it.
- *
- * WARNING: These endpoints should not be used as negative samples for training, because they may include sinks for
- * which our taint tracking modeling is incomplete.
- */
-private class CannotBeTaintedCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic
-{
- CannotBeTaintedCharacteristic() { this = "cannot be tainted" }
-
- override predicate appliesToEndpoint(Endpoint e) { not this.isKnownOutNodeForStep(e) }
-
- /**
- * Holds if the node `n` is known as the predecessor in a modeled flow step.
- */
- private predicate isKnownOutNodeForStep(Endpoint e) {
- e.asNode().asExpr() instanceof Call or // we just assume flow in that case
- TaintTracking::localTaintStep(_, e.asNode()) or
- FlowSummaryImpl::Private::Steps::summaryThroughStepValue(_, e.asNode(), _) or
- FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(_, e.asNode(), _) or
- FlowSummaryImpl::Private::Steps::summaryGetterStep(_, _, e.asNode(), _) or
- FlowSummaryImpl::Private::Steps::summarySetterStep(_, _, e.asNode(), _)
- }
-}
diff --git a/java/ql/automodel/src/AutomodelApplicationModeExtractCandidates.ql b/java/ql/automodel/src/AutomodelApplicationModeExtractCandidates.ql
deleted file mode 100644
index a3fa8b9b46fa..000000000000
--- a/java/ql/automodel/src/AutomodelApplicationModeExtractCandidates.ql
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * Surfaces the endpoints that are not already known to be sinks, and are therefore used as candidates for
- * classification with an ML model.
- *
- * Note: This query does not actually classify the endpoints using the model.
- *
- * @name Automodel candidates (application mode)
- * @description A query to extract automodel candidates in application mode.
- * @kind problem
- * @problem.severity recommendation
- * @id java/ml/extract-automodel-application-candidates
- * @tags internal extract automodel application-mode candidates
- */
-
-import java
-private import AutomodelApplicationModeCharacteristics
-private import AutomodelJavaUtil
-
-/**
- * Gets a sample of endpoints (of at most `limit` samples) with the given method signature.
- *
- * The main purpose of this helper predicate is to avoid selecting too many candidates, as this may
- * cause the SARIF file to exceed the maximum size limit.
- */
-bindingset[limit]
-private Endpoint getSampleForSignature(
- int limit, string package, string type, string subtypes, string name, string signature,
- string input, string output, string isVarargs, string extensibleType, string alreadyAiModeled
-) {
- exists(int n, int num_endpoints, ApplicationModeMetadataExtractor meta |
- num_endpoints =
- count(Endpoint e |
- meta.hasMetadata(e, package, type, subtypes, name, signature, input, output, isVarargs,
- alreadyAiModeled, extensibleType)
- )
- |
- result =
- rank[n](Endpoint e, Location loc |
- loc = e.asTop().getLocation() and
- meta.hasMetadata(e, package, type, subtypes, name, signature, input, output, isVarargs,
- alreadyAiModeled, extensibleType)
- |
- e
- order by
- loc.getFile().getAbsolutePath(), loc.getStartLine(), loc.getStartColumn(),
- loc.getEndLine(), loc.getEndColumn()
- ) and
- // To avoid selecting samples that are too close together (as the ranking above goes by file
- // path first), we select `limit` evenly spaced samples from the ranked list of endpoints. By
- // default this would always include the first sample, so we add a random-chosen prime offset
- // to the first sample index, and reduce modulo the number of endpoints.
- // Finally, we add 1 to the result, as ranking results in a 1-indexed relation.
- n = 1 + (([0 .. limit - 1] * (num_endpoints / limit).floor() + 46337) % num_endpoints)
- )
-}
-
-from
- Endpoint endpoint, DollarAtString package, DollarAtString type, DollarAtString subtypes,
- DollarAtString name, DollarAtString signature, DollarAtString input, DollarAtString output,
- DollarAtString isVarargsArray, DollarAtString alreadyAiModeled, DollarAtString extensibleType
-where
- isCandidate(endpoint, package, type, subtypes, name, signature, input, output, isVarargsArray,
- extensibleType, alreadyAiModeled) and
- endpoint =
- getSampleForSignature(9, package, type, subtypes, name, signature, input, output,
- isVarargsArray, extensibleType, alreadyAiModeled)
-select endpoint.asNode(),
- "Related locations: $@, $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
- CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
- CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
- CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
- package, "package", //
- type, "type", //
- subtypes, "subtypes", //
- name, "name", // method name
- signature, "signature", //
- input, "input", //
- output, "output", //
- isVarargsArray, "isVarargsArray", //
- alreadyAiModeled, "alreadyAiModeled", //
- extensibleType, "extensibleType"
diff --git a/java/ql/automodel/src/AutomodelApplicationModeExtractNegativeExamples.ql b/java/ql/automodel/src/AutomodelApplicationModeExtractNegativeExamples.ql
deleted file mode 100644
index a399c413fa4a..000000000000
--- a/java/ql/automodel/src/AutomodelApplicationModeExtractNegativeExamples.ql
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * Surfaces endpoints that are non-sinks with high confidence, for use as negative examples in the prompt.
- *
- * @name Negative examples (application mode)
- * @kind problem
- * @problem.severity recommendation
- * @id java/ml/extract-automodel-application-negative-examples
- * @tags internal extract automodel application-mode negative examples
- */
-
-private import java
-private import AutomodelApplicationModeCharacteristics
-private import AutomodelEndpointTypes
-private import AutomodelJavaUtil
-
-/**
- * Gets a sample of endpoints (of at most `limit` samples) for which the given characteristic applies.
- *
- * The main purpose of this helper predicate is to avoid selecting too many samples, as this may
- * cause the SARIF file to exceed the maximum size limit.
- */
-bindingset[limit]
-Endpoint getSampleForCharacteristic(EndpointCharacteristic c, int limit) {
- exists(int n, int num_endpoints | num_endpoints = count(Endpoint e | c.appliesToEndpoint(e)) |
- result =
- rank[n](Endpoint e, Location loc |
- loc = e.asTop().getLocation() and c.appliesToEndpoint(e)
- |
- e
- order by
- loc.getFile().getAbsolutePath(), loc.getStartLine(), loc.getStartColumn(),
- loc.getEndLine(), loc.getEndColumn()
- ) and
- // To avoid selecting samples that are too close together (as the ranking above goes by file
- // path first), we select `limit` evenly spaced samples from the ranked list of endpoints. By
- // default this would always include the first sample, so we add a random-chosen prime offset
- // to the first sample index, and reduce modulo the number of endpoints.
- // Finally, we add 1 to the result, as ranking results in a 1-indexed relation.
- n = 1 + (([0 .. limit - 1] * (num_endpoints / limit).floor() + 46337) % num_endpoints)
- )
-}
-
-from
- Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string message,
- DollarAtString package, DollarAtString type, DollarAtString subtypes, DollarAtString name,
- DollarAtString signature, DollarAtString input, DollarAtString output,
- DollarAtString isVarargsArray, DollarAtString extensibleType
-where
- endpoint = getSampleForCharacteristic(characteristic, 100) and
- isNegativeExample(endpoint, characteristic, confidence, package, type, subtypes, name, signature,
- input, output, isVarargsArray, extensibleType) and
- message = characteristic
-select endpoint.asNode(),
- message + "\nrelated locations: $@, $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
- CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
- CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
- CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
- package, "package", //
- type, "type", //
- subtypes, "subtypes", //
- name, "name", //
- signature, "signature", //
- input, "input", //
- output, "output", //
- isVarargsArray, "isVarargsArray", //
- extensibleType, "extensibleType"
diff --git a/java/ql/automodel/src/AutomodelApplicationModeExtractPositiveExamples.ql b/java/ql/automodel/src/AutomodelApplicationModeExtractPositiveExamples.ql
deleted file mode 100644
index faf49b73fc14..000000000000
--- a/java/ql/automodel/src/AutomodelApplicationModeExtractPositiveExamples.ql
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * Surfaces endpoints that are sinks with high confidence, for use as positive examples in the prompt.
- *
- * @name Positive examples (application mode)
- * @kind problem
- * @problem.severity recommendation
- * @id java/ml/extract-automodel-application-positive-examples
- * @tags internal extract automodel application-mode positive examples
- */
-
-private import AutomodelApplicationModeCharacteristics
-private import AutomodelEndpointTypes
-private import AutomodelJavaUtil
-
-from
- Endpoint endpoint, EndpointType endpointType, ApplicationModeMetadataExtractor meta,
- DollarAtString package, DollarAtString type, DollarAtString subtypes, DollarAtString name,
- DollarAtString signature, DollarAtString input, DollarAtString output,
- DollarAtString isVarargsArray, DollarAtString extensibleType
-where
- isPositiveExample(endpoint, endpointType, package, type, subtypes, name, signature, input, output,
- isVarargsArray, extensibleType)
-select endpoint.asNode(),
- endpointType + "\nrelated locations: $@, $@, $@." +
- "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
- CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
- CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
- CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
- package, "package", //
- type, "type", //
- subtypes, "subtypes", //
- name, "name", //
- signature, "signature", //
- input, "input", //
- output, "output", //
- isVarargsArray, "isVarargsArray", //
- extensibleType, "extensibleType"
diff --git a/java/ql/automodel/src/AutomodelCandidateFilter.yml b/java/ql/automodel/src/AutomodelCandidateFilter.yml
deleted file mode 100644
index c945ae3206fe..000000000000
--- a/java/ql/automodel/src/AutomodelCandidateFilter.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-extensions:
- - addsTo:
- pack: codeql/java-automodel-queries
- extensible: automodelCandidateFilter
- data: []
diff --git a/java/ql/automodel/src/AutomodelCountGeneratedSinks.ql b/java/ql/automodel/src/AutomodelCountGeneratedSinks.ql
deleted file mode 100644
index 475e3753810b..000000000000
--- a/java/ql/automodel/src/AutomodelCountGeneratedSinks.ql
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * @name Number of instances of each sink model
- * @description Counts the number of instances of `ai-generated` sink models.
- * @kind table
- * @id java/ml/metrics-count-instances-per-sink-model
- * @tags internal automodel metrics
- */
-
-private import java
-private import AutomodelAlertSinkUtil
-
-from int instanceCount, SinkModel s
-where
- instanceCount = s.getInstanceCount() and
- instanceCount > 0 and
- s.getProvenance() = "ai-generated"
-select instanceCount, s.getPackage() as package, s.getType() as type, s.getSubtypes() as subtypes,
- s.getName() as name, s.getSignature() as signature, s.getInput() as input, s.getExt() as ext,
- s.getKind() as kind, s.getProvenance() as provenance order by instanceCount desc
diff --git a/java/ql/automodel/src/AutomodelEndpointTypes.qll b/java/ql/automodel/src/AutomodelEndpointTypes.qll
deleted file mode 100644
index f4f7bc8eb7b0..000000000000
--- a/java/ql/automodel/src/AutomodelEndpointTypes.qll
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
- * For internal use only.
- *
- * Defines the set of classes that endpoint scoring models can predict. Endpoint scoring models must
- * only predict classes defined within this file. This file is the source of truth for the integer
- * representation of each of these classes.
- */
-
-/** A class that can be predicted by a classifier. */
-abstract class EndpointType extends string {
- /**
- * Holds when the string matches the name of the sink / source type.
- */
- bindingset[this]
- EndpointType() { any() }
-
- /**
- * Gets the name of the sink/source kind for this endpoint type as used in models-as-data.
- *
- * See https://github.com/github/codeql/blob/44213f0144fdd54bb679ca48d68b28dcf820f7a8/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll#LL353C11-L357C31
- * for sink types, and https://github.com/github/codeql/blob/44213f0144fdd54bb679ca48d68b28dcf820f7a8/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll#L365
- * for source types.
- */
- final string getKind() { result = this }
-}
-
-/** A class for sink types that can be predicted by a classifier. */
-abstract class SinkType extends EndpointType {
- bindingset[this]
- SinkType() { any() }
-}
-
-/** A sink relevant to the SQL injection query */
-class SqlInjectionSinkType extends SinkType {
- SqlInjectionSinkType() { this = "sql-injection" }
-}
-
-/** A sink relevant to the tainted path injection query. */
-class PathInjectionSinkType extends SinkType {
- PathInjectionSinkType() { this = "path-injection" }
-}
-
-/** A sink relevant to the SSRF query. */
-class RequestForgerySinkType extends SinkType {
- RequestForgerySinkType() { this = "request-forgery" }
-}
-
-/** A sink relevant to the command injection query. */
-class CommandInjectionSinkType extends SinkType {
- CommandInjectionSinkType() { this = "command-injection" }
-}
-
-/** A sink relevant to file storage. */
-class FileContentStoreSinkType extends SinkType {
- FileContentStoreSinkType() { this = "file-content-store" }
-}
-
-/** A sink relevant to HTML injection. */
-class HtmlInjectionSinkType extends SinkType {
- HtmlInjectionSinkType() { this = "html-injection" }
-}
-
-/** A sink relevant to LDAP injection. */
-class LdapInjectionSinkType extends SinkType {
- LdapInjectionSinkType() { this = "ldap-injection" }
-}
-
-/** A sink relevant to URL redirection. */
-class UrlRedirectionSinkType extends SinkType {
- UrlRedirectionSinkType() { this = "url-redirection" }
-}
-
-/** A class for source types that can be predicted by a classifier. */
-abstract class SourceType extends EndpointType {
- bindingset[this]
- SourceType() { any() }
-}
-
-/** A source of remote data. */
-class RemoteSourceType extends SourceType {
- RemoteSourceType() { this = "remote" }
-}
diff --git a/java/ql/automodel/src/AutomodelFrameworkModeCharacteristics.qll b/java/ql/automodel/src/AutomodelFrameworkModeCharacteristics.qll
deleted file mode 100644
index 7f385a41d1ea..000000000000
--- a/java/ql/automodel/src/AutomodelFrameworkModeCharacteristics.qll
+++ /dev/null
@@ -1,507 +0,0 @@
-/**
- * For internal use only.
- */
-
-private import java
-private import semmle.code.Location as Location
-private import semmle.code.java.dataflow.DataFlow
-private import semmle.code.java.dataflow.TaintTracking
-private import semmle.code.java.dataflow.ExternalFlow as ExternalFlow
-private import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
-private import semmle.code.java.security.ExternalAPIs as ExternalAPIs
-private import semmle.code.java.Expr as Expr
-private import semmle.code.java.security.QueryInjection
-private import semmle.code.java.security.RequestForgery
-private import semmle.code.java.dataflow.internal.ModelExclusions as ModelExclusions
-private import AutomodelJavaUtil as AutomodelJavaUtil
-import AutomodelSharedCharacteristics as SharedCharacteristics
-import AutomodelEndpointTypes as AutomodelEndpointTypes
-
-newtype JavaRelatedLocationType =
- MethodDoc() or
- ClassDoc()
-
-newtype TFrameworkModeEndpoint =
- TExplicitParameter(Parameter p) {
- AutomodelJavaUtil::isFromSource(p) and
- not AutomodelJavaUtil::isUnexploitableType(p.getType())
- } or
- TQualifier(Callable c) { AutomodelJavaUtil::isFromSource(c) and not c instanceof Constructor } or
- TReturnValue(Callable c) {
- AutomodelJavaUtil::isFromSource(c) and
- c instanceof Constructor
- or
- AutomodelJavaUtil::isFromSource(c) and
- c instanceof Method and
- not AutomodelJavaUtil::isUnexploitableType(c.getReturnType())
- } or
- TOverridableParameter(Method m, Parameter p) {
- AutomodelJavaUtil::isFromSource(p) and
- not AutomodelJavaUtil::isUnexploitableType(p.getType()) and
- p.getCallable() = m and
- m instanceof ModelExclusions::ModelApi and
- AutomodelJavaUtil::isOverridable(m)
- } or
- TOverridableQualifier(Method m) {
- AutomodelJavaUtil::isFromSource(m) and
- m instanceof ModelExclusions::ModelApi and
- AutomodelJavaUtil::isOverridable(m)
- }
-
-/**
- * A framework mode endpoint.
- */
-abstract class FrameworkModeEndpoint extends TFrameworkModeEndpoint {
- /**
- * Gets the input (if any) for this endpoint, eg.: `Argument[0]`.
- *
- * For endpoints that are source candidates, this will be `none()`.
- */
- abstract string getMaDInput();
-
- /**
- * Gets the output (if any) for this endpoint, eg.: `ReturnValue`.
- *
- * For endpoints that are sink candidates, this will be `none()`.
- */
- abstract string getMaDOutput();
-
- /**
- * Returns the name of the parameter of the endpoint.
- */
- abstract string getParamName();
-
- /**
- * Returns the callable that contains the endpoint.
- */
- abstract Callable getCallable();
-
- abstract Top asTop();
-
- abstract string getExtensibleType();
-
- string toString() { result = this.asTop().toString() }
-
- Location getLocation() { result = this.asTop().getLocation() }
-}
-
-class ExplicitParameterEndpoint extends FrameworkModeEndpoint, TExplicitParameter {
- Parameter param;
-
- ExplicitParameterEndpoint() { this = TExplicitParameter(param) and param.fromSource() }
-
- override string getMaDInput() { result = "Argument[" + param.getPosition() + "]" }
-
- override string getMaDOutput() { none() }
-
- override string getParamName() { result = param.getName() }
-
- override Callable getCallable() { result = param.getCallable() }
-
- override Top asTop() { result = param }
-
- override string getExtensibleType() { result = "sinkModel" }
-}
-
-class QualifierEndpoint extends FrameworkModeEndpoint, TQualifier {
- Callable callable;
-
- QualifierEndpoint() {
- this = TQualifier(callable) and not callable.isStatic() and callable.fromSource()
- }
-
- override string getMaDInput() { result = "Argument[this]" }
-
- override string getMaDOutput() { none() }
-
- override string getParamName() { result = "this" }
-
- override Callable getCallable() { result = callable }
-
- override Top asTop() { result = callable }
-
- override string getExtensibleType() { result = "sinkModel" }
-}
-
-class ReturnValue extends FrameworkModeEndpoint, TReturnValue {
- Callable callable;
-
- ReturnValue() { this = TReturnValue(callable) and callable.fromSource() }
-
- override string getMaDInput() { none() }
-
- override string getMaDOutput() { result = "ReturnValue" }
-
- override string getParamName() { none() }
-
- override Callable getCallable() { result = callable }
-
- override Top asTop() { result = callable }
-
- override string getExtensibleType() { result = "sourceModel" }
-}
-
-class OverridableParameter extends FrameworkModeEndpoint, TOverridableParameter {
- Method method;
- Parameter param;
-
- OverridableParameter() { this = TOverridableParameter(method, param) }
-
- override string getMaDInput() { none() }
-
- override string getMaDOutput() { result = "Parameter[" + param.getPosition() + "]" }
-
- override string getParamName() { result = param.getName() }
-
- override Callable getCallable() { result = method }
-
- override Top asTop() { result = param }
-
- override string getExtensibleType() { result = "sourceModel" }
-}
-
-class OverridableQualifier extends FrameworkModeEndpoint, TOverridableQualifier {
- Method m;
-
- OverridableQualifier() { this = TOverridableQualifier(m) }
-
- override string getMaDInput() { none() }
-
- override string getMaDOutput() { result = "Parameter[this]" }
-
- override string getParamName() { result = "this" }
-
- override Callable getCallable() { result = m }
-
- override Top asTop() { result = m }
-
- override string getExtensibleType() { result = "sourceModel" }
-}
-
-/**
- * A candidates implementation for framework mode.
- *
- * Some important notes:
- * - This mode is using parameters as endpoints.
- * - Sink- and neutral-information is being used from MaD models.
- * - When available, we use method- and class-java-docs as related locations.
- */
-module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig {
- // for documentation of the implementations here, see the QLDoc in the CandidateSig signature module.
- class Endpoint = FrameworkModeEndpoint;
-
- class EndpointType = AutomodelEndpointTypes::EndpointType;
-
- class SinkType = AutomodelEndpointTypes::SinkType;
-
- class SourceType = AutomodelEndpointTypes::SourceType;
-
- class RelatedLocation = Location::Top;
-
- class RelatedLocationType = JavaRelatedLocationType;
-
- // Sanitizers are currently not modeled in MaD. TODO: check if this has large negative impact.
- predicate isSanitizer(Endpoint e, EndpointType t) { none() }
-
- RelatedLocation asLocation(Endpoint e) { result = e.asTop() }
-
- predicate isKnownKind = AutomodelJavaUtil::isKnownKind/2;
-
- predicate isSink(Endpoint e, string kind, string provenance) {
- exists(
- string package, string type, boolean subtypes, string name, string signature, string ext,
- string input
- |
- sinkSpec(e, package, type, subtypes, name, signature, ext, input) and
- ExternalFlow::sinkModel(package, type, subtypes, name, [signature, ""], ext, input, kind,
- provenance, _)
- )
- }
-
- predicate isSource(Endpoint e, string kind, string provenance) {
- exists(
- string package, string type, boolean subtypes, string name, string signature, string ext,
- string output
- |
- sourceSpec(e, package, type, subtypes, name, signature, ext, output) and
- ExternalFlow::sourceModel(package, type, subtypes, name, [signature, ""], ext, output, kind,
- provenance, _)
- )
- }
-
- predicate isNeutral(Endpoint e) {
- exists(string package, string type, string name, string signature, string endpointType |
- sinkSpec(e, package, type, _, name, signature, _, _) and
- endpointType = "sink"
- or
- sourceSpec(e, package, type, _, name, signature, _, _) and
- endpointType = "source"
- |
- ExternalFlow::neutralModel(package, type, name, [signature, ""], endpointType, _)
- )
- }
-
- /**
- * Holds if the endpoint concerns a callable with the given package, type, name and signature.
- *
- * If `subtypes` is `false`, only the exact callable is considered. If `true`, the callable and
- * all its overrides are considered.
- */
- additional predicate endpointCallable(
- Endpoint e, string package, string type, boolean subtypes, string name, string signature
- ) {
- exists(Callable c |
- c = e.getCallable() and subtypes in [true, false]
- or
- e.getCallable().(Method).getSourceDeclaration().overrides+(c) and subtypes = true
- |
- c.hasQualifiedName(package, type, name) and
- signature = ExternalFlow::paramsString(c)
- )
- }
-
- additional predicate sinkSpec(
- Endpoint e, string package, string type, boolean subtypes, string name, string signature,
- string ext, string input
- ) {
- endpointCallable(e, package, type, subtypes, name, signature) and
- ext = "" and
- input = e.getMaDInput()
- }
-
- additional predicate sourceSpec(
- Endpoint e, string package, string type, boolean subtypes, string name, string signature,
- string ext, string output
- ) {
- endpointCallable(e, package, type, subtypes, name, signature) and
- ext = "" and
- output = e.getMaDOutput()
- }
-
- /**
- * Gets the related location for the given endpoint.
- *
- * Related locations can be JavaDoc comments of the class or the method.
- */
- RelatedLocation getRelatedLocation(Endpoint e, RelatedLocationType type) {
- type = MethodDoc() and
- result = e.getCallable().(Documentable).getJavadoc()
- or
- type = ClassDoc() and
- result = e.getCallable().getDeclaringType().(Documentable).getJavadoc()
- }
-}
-
-module CharacteristicsImpl = SharedCharacteristics::SharedCharacteristics;
-
-class EndpointCharacteristic = CharacteristicsImpl::EndpointCharacteristic;
-
-class Endpoint = FrameworkCandidatesImpl::Endpoint;
-
-/*
- * Predicates that are used to surface prompt examples and candidates for classification with an ML model.
- */
-
-/**
- * A MetadataExtractor that extracts metadata for framework mode.
- */
-class FrameworkModeMetadataExtractor extends string {
- FrameworkModeMetadataExtractor() { this = "FrameworkModeMetadataExtractor" }
-
- predicate hasMetadata(
- Endpoint e, string package, string type, string subtypes, string name, string signature,
- string input, string output, string parameterName, string alreadyAiModeled,
- string extensibleType
- ) {
- exists(Callable callable | e.getCallable() = callable |
- (if exists(e.getMaDInput()) then input = e.getMaDInput() else input = "") and
- (if exists(e.getMaDOutput()) then output = e.getMaDOutput() else output = "") and
- package = callable.getDeclaringType().getPackage().getName() and
- // we're using the erased types because the MaD convention is to not specify type parameters.
- // Whether something is or isn't a sink doesn't usually depend on the type parameters.
- type = callable.getDeclaringType().getErasure().(RefType).getNestedName() and
- subtypes = AutomodelJavaUtil::considerSubtypes(callable).toString() and
- name = callable.getName() and
- signature = ExternalFlow::paramsString(callable) and
- (if exists(e.getParamName()) then parameterName = e.getParamName() else parameterName = "") and
- e.getExtensibleType() = extensibleType
- ) and
- (
- not CharacteristicsImpl::isModeled(e, _, extensibleType, _) and alreadyAiModeled = ""
- or
- CharacteristicsImpl::isModeled(e, _, extensibleType, alreadyAiModeled)
- )
- }
-}
-
-/**
- * Holds if the given `endpoint` should be considered a candidate for the `extensibleType`.
- *
- * The other parameters record various other properties of interest.
- */
-predicate isCandidate(
- Endpoint endpoint, string package, string type, string subtypes, string name, string signature,
- string input, string output, string parameterName, string extensibleType, string alreadyAiModeled
-) {
- CharacteristicsImpl::isCandidate(endpoint, _) and
- not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
- u.appliesToEndpoint(endpoint)
- ) and
- any(FrameworkModeMetadataExtractor meta)
- .hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, parameterName,
- alreadyAiModeled, extensibleType) and
- // If a node is already modeled in MaD, we don't include it as a candidate. Otherwise, we might include it as a
- // candidate for query A, but the model will label it as a sink for one of the sink types of query B, for which it's
- // already a known sink. This would result in overlap between our detected sinks and the pre-existing modeling. We
- // assume that, if a sink has already been modeled in a MaD model, then it doesn't belong to any additional sink
- // types, and we don't need to reexamine it.
- alreadyAiModeled.matches(["", "%ai-%"]) and
- AutomodelJavaUtil::includeAutomodelCandidate(package, type, name, signature)
-}
-
-/**
- * Holds if the given `endpoint` is a negative example for the `extensibleType`
- * because of the `characteristic`.
- *
- * The other parameters record various other properties of interest.
- */
-predicate isNegativeExample(
- Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string package,
- string type, string subtypes, string name, string signature, string input, string output,
- string parameterName, string extensibleType
-) {
- characteristic.appliesToEndpoint(endpoint) and
- // the node is known not to be an endpoint of any appropriate type
- forall(AutomodelEndpointTypes::EndpointType tp |
- tp = CharacteristicsImpl::getAPotentialType(endpoint)
- |
- characteristic.hasImplications(tp, false, _)
- ) and
- // the lowest confidence across all endpoint types should be at least highConfidence
- confidence =
- min(float c |
- characteristic.hasImplications(CharacteristicsImpl::getAPotentialType(endpoint), false, c)
- ) and
- confidence >= SharedCharacteristics::highConfidence() and
- any(FrameworkModeMetadataExtractor meta)
- .hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, parameterName,
- _, extensibleType) and
- // It's valid for a node to be both a potential source/sanitizer and a sink. We don't want to include such nodes
- // as negative examples in the prompt, because they're ambiguous and might confuse the model, so we explicitly exclude them here.
- not exists(EndpointCharacteristic characteristic2, float confidence2 |
- characteristic2 != characteristic
- |
- characteristic2.appliesToEndpoint(endpoint) and
- confidence2 >= SharedCharacteristics::maximalConfidence() and
- characteristic2
- .hasImplications(CharacteristicsImpl::getAPotentialType(endpoint), true, confidence2)
- )
-}
-
-/**
- * Holds if the given `endpoint` is a positive example for the `endpointType`.
- *
- * The other parameters record various other properties of interest.
- */
-predicate isPositiveExample(
- Endpoint endpoint, string endpointType, string package, string type, string subtypes, string name,
- string signature, string input, string output, string parameterName, string extensibleType
-) {
- any(FrameworkModeMetadataExtractor meta)
- .hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, parameterName,
- _, extensibleType) and
- CharacteristicsImpl::isKnownAs(endpoint, endpointType, _)
-}
-
-/*
- * EndpointCharacteristic classes that are specific to Automodel for Java.
- */
-
-/**
- * A negative characteristic that indicates that parameters of an is-style boolean method should not be considered sinks,
- * and its return value should not be considered a source.
- *
- * A sink is highly unlikely to be exploitable if its callable's name starts with `is` and the callable has a boolean return
- * type (e.g. `isDirectory`). These kinds of calls normally do only checks, and appear before the proper call that does
- * the dangerous/interesting thing, so we want the latter to be modeled as the sink.
- *
- * TODO: this might filter too much, it's possible that methods with more than one parameter contain interesting sinks
- */
-private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NeitherSourceNorSinkCharacteristic
-{
- UnexploitableIsCharacteristic() { this = "argument of is-style boolean method" }
-
- override predicate appliesToEndpoint(Endpoint e) {
- e.getCallable().getName().matches("is%") and
- e.getCallable().getReturnType() instanceof BooleanType and
- (
- e.getExtensibleType() = "sinkModel" and
- not FrameworkCandidatesImpl::isSink(e, _, _)
- or
- e.getExtensibleType() = "sourceModel" and
- not FrameworkCandidatesImpl::isSource(e, _, _) and
- e.getMaDOutput() = "ReturnValue"
- )
- }
-}
-
-/**
- * A negative characteristic that indicates that parameters of an existence-checking boolean method should not be
- * considered sinks, and its return value should not be considered a source.
- *
- * A sink is highly unlikely to be exploitable if its callable's name is `exists` or `notExists` and the callable has a
- * boolean return type. These kinds of calls normally do only checks, and appear before the proper call that does the
- * dangerous/interesting thing, so we want the latter to be modeled as the sink.
- */
-private class UnexploitableExistsCharacteristic extends CharacteristicsImpl::NeitherSourceNorSinkCharacteristic
-{
- UnexploitableExistsCharacteristic() { this = "argument of existence-checking boolean method" }
-
- override predicate appliesToEndpoint(Endpoint e) {
- exists(Callable callable |
- callable = e.getCallable() and
- callable.getName().toLowerCase() = ["exists", "notexists"] and
- callable.getReturnType() instanceof BooleanType
- |
- e.getExtensibleType() = "sinkModel" and
- not FrameworkCandidatesImpl::isSink(e, _, _)
- or
- e.getExtensibleType() = "sourceModel" and
- not FrameworkCandidatesImpl::isSource(e, _, _) and
- e.getMaDOutput() = "ReturnValue"
- )
- }
-}
-
-/**
- * A negative characteristic that indicates that parameters of an exception method or constructor should not be considered sinks,
- * and its return value should not be considered a source.
- */
-private class ExceptionCharacteristic extends CharacteristicsImpl::NeitherSourceNorSinkCharacteristic
-{
- ExceptionCharacteristic() { this = "argument/result of exception-related method" }
-
- override predicate appliesToEndpoint(Endpoint e) {
- e.getCallable().getDeclaringType().getASupertype*() instanceof TypeThrowable and
- (
- e.getExtensibleType() = "sinkModel" and
- not FrameworkCandidatesImpl::isSink(e, _, _)
- or
- e.getExtensibleType() = "sourceModel" and
- not FrameworkCandidatesImpl::isSource(e, _, _) and
- e.getMaDOutput() = "ReturnValue"
- )
- }
-}
-
-/**
- * A characteristic that limits candidates to parameters of methods that are recognized as `ModelApi`, iow., APIs that
- * are considered worth modeling.
- */
-private class NotAModelApi extends CharacteristicsImpl::UninterestingToModelCharacteristic {
- NotAModelApi() { this = "not a model API" }
-
- override predicate appliesToEndpoint(Endpoint e) {
- not e.getCallable() instanceof ModelExclusions::ModelApi
- }
-}
diff --git a/java/ql/automodel/src/AutomodelFrameworkModeExtractCandidates.ql b/java/ql/automodel/src/AutomodelFrameworkModeExtractCandidates.ql
deleted file mode 100644
index 83683b4e78f5..000000000000
--- a/java/ql/automodel/src/AutomodelFrameworkModeExtractCandidates.ql
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * Surfaces the endpoints that are not already known to be sinks, and are therefore used as candidates for
- * classification with an ML model.
- *
- * Note: This query does not actually classify the endpoints using the model.
- *
- * @name Automodel candidates (framework mode)
- * @description A query to extract automodel candidates in framework mode.
- * @kind problem
- * @problem.severity recommendation
- * @id java/ml/extract-automodel-framework-candidates
- * @tags internal extract automodel framework-mode candidates
- */
-
-private import AutomodelFrameworkModeCharacteristics
-private import AutomodelJavaUtil
-
-from
- Endpoint endpoint, DollarAtString package, DollarAtString type, DollarAtString subtypes,
- DollarAtString name, DollarAtString signature, DollarAtString input, DollarAtString output,
- DollarAtString parameterName, DollarAtString alreadyAiModeled, DollarAtString extensibleType
-where
- isCandidate(endpoint, package, type, subtypes, name, signature, input, output, parameterName,
- extensibleType, alreadyAiModeled)
-select endpoint,
- "Related locations: $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
- CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
- CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
- package, "package", //
- type, "type", //
- subtypes, "subtypes", //
- name, "name", //
- signature, "signature", //
- input, "input", //
- output, "output", //
- parameterName, "parameterName", //
- alreadyAiModeled, "alreadyAiModeled", //
- extensibleType, "extensibleType"
diff --git a/java/ql/automodel/src/AutomodelFrameworkModeExtractNegativeExamples.ql b/java/ql/automodel/src/AutomodelFrameworkModeExtractNegativeExamples.ql
deleted file mode 100644
index 05e5951b0610..000000000000
--- a/java/ql/automodel/src/AutomodelFrameworkModeExtractNegativeExamples.ql
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * Surfaces endpoints that are non-sinks with high confidence, for use as negative examples in the prompt.
- *
- * @name Negative examples (framework mode)
- * @kind problem
- * @problem.severity recommendation
- * @id java/ml/extract-automodel-framework-negative-examples
- * @tags internal extract automodel framework-mode negative examples
- */
-
-private import AutomodelFrameworkModeCharacteristics
-private import AutomodelEndpointTypes
-private import AutomodelJavaUtil
-
-from
- Endpoint endpoint, EndpointCharacteristic characteristic, float confidence,
- DollarAtString package, DollarAtString type, DollarAtString subtypes, DollarAtString name,
- DollarAtString signature, DollarAtString input, DollarAtString output,
- DollarAtString parameterName, DollarAtString extensibleType
-where
- isNegativeExample(endpoint, characteristic, confidence, package, type, subtypes, name, signature,
- input, output, parameterName, extensibleType)
-select endpoint,
- characteristic + "\nrelated locations: $@, $@." +
- "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
- CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
- CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
- package, "package", //
- type, "type", //
- subtypes, "subtypes", //
- name, "name", //
- signature, "signature", //
- input, "input", //
- output, "output", //
- parameterName, "parameterName", //
- extensibleType, "extensibleType"
diff --git a/java/ql/automodel/src/AutomodelFrameworkModeExtractPositiveExamples.ql b/java/ql/automodel/src/AutomodelFrameworkModeExtractPositiveExamples.ql
deleted file mode 100644
index 7cb023949ed9..000000000000
--- a/java/ql/automodel/src/AutomodelFrameworkModeExtractPositiveExamples.ql
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * Surfaces endpoints that are sinks with high confidence, for use as positive examples in the prompt.
- *
- * @name Positive examples (framework mode)
- * @kind problem
- * @problem.severity recommendation
- * @id java/ml/extract-automodel-framework-positive-examples
- * @tags internal extract automodel framework-mode positive examples
- */
-
-private import AutomodelFrameworkModeCharacteristics
-private import AutomodelEndpointTypes
-private import AutomodelJavaUtil
-
-from
- Endpoint endpoint, EndpointType endpointType, DollarAtString package, DollarAtString type,
- DollarAtString subtypes, DollarAtString name, DollarAtString signature, DollarAtString input,
- DollarAtString output, DollarAtString parameterName, DollarAtString extensibleType
-where
- isPositiveExample(endpoint, endpointType, package, type, subtypes, name, signature, input, output,
- parameterName, extensibleType)
-select endpoint,
- endpointType + "\nrelated locations: $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
- CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
- CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
- package, "package", //
- type, "type", //
- subtypes, "subtypes", //
- name, "name", //
- signature, "signature", //
- input, "input", //
- output, "output", //
- parameterName, "parameterName", //
- extensibleType, "extensibleType"
diff --git a/java/ql/automodel/src/AutomodelJavaUtil.qll b/java/ql/automodel/src/AutomodelJavaUtil.qll
deleted file mode 100644
index 368fb172483b..000000000000
--- a/java/ql/automodel/src/AutomodelJavaUtil.qll
+++ /dev/null
@@ -1,111 +0,0 @@
-private import java
-private import AutomodelEndpointTypes as AutomodelEndpointTypes
-
-/**
- * A helper class to represent a string value that can be returned by a query using $@ notation.
- *
- * It extends `string`, but adds a mock `hasLocationInfo` method that returns the string itself as the file name.
- *
- * Use this, when you want to return a string value from a query using $@ notation - the string value
- * will be included in the sarif file.
- *
- *
- * Background information on `hasLocationInfo`:
- * https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-location-information
- */
-class DollarAtString extends string {
- bindingset[this]
- DollarAtString() { any() }
-
- bindingset[this]
- predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
- path = this and sl = 1 and sc = 1 and el = 1 and ec = 1
- }
-}
-
-/**
- * Holds for all combinations of MaD kinds (`kind`) and their human readable
- * descriptions.
- */
-predicate isKnownKind(string kind, AutomodelEndpointTypes::EndpointType type) {
- kind = type.getKind()
-}
-
-/**
- * By convention, the subtypes property of the MaD declaration should only be
- * true when there _can_ exist any subtypes with a different implementation.
- *
- * It would technically be ok to always use the value 'true', but this would
- * break convention.
- */
-pragma[nomagic]
-boolean considerSubtypes(Callable callable) {
- if
- callable.isStatic() or
- callable.getDeclaringType().isStatic() or
- callable.isFinal() or
- callable.getDeclaringType().isFinal()
- then result = false
- else result = true
-}
-
-/**
- * Holds if the given package, type, name and signature is a candidate for automodeling.
- *
- * This predicate is extensible, so that different endpoints can be selected at runtime.
- */
-extensible predicate automodelCandidateFilter(
- string package, string type, string name, string signature
-);
-
-/**
- * Holds if the given package, type, name and signature is a candidate for automodeling.
- *
- * This relies on an extensible predicate, and if that is not supplied then
- * all endpoints are considered candidates.
- */
-bindingset[package, type, name, signature]
-predicate includeAutomodelCandidate(string package, string type, string name, string signature) {
- not automodelCandidateFilter(_, _, _, _) or
- automodelCandidateFilter(package, type, name, signature)
-}
-
-/**
- * Holds if the given program element corresponds to a piece of source code,
- * that is, it is not compiler-generated.
- *
- * Note: This is a stricter check than `Element::fromSource`, which simply
- * checks whether the element is in a source file as opposed to a JAR file.
- * There can be compiler-generated elements in source files (especially for
- * Kotlin), which we also want to exclude.
- */
-predicate isFromSource(Element e) {
- // from a source file (not a JAR)
- e.fromSource() and
- // not explicitly marked as compiler-generated
- not e.isCompilerGenerated() and
- // does not have a dummy location
- not e.hasLocationInfo(_, 0, 0, 0, 0)
-}
-
-/**
- * Holds if taint cannot flow through the given type (because it is a numeric
- * type or some other type with a fixed set of values).
- */
-predicate isUnexploitableType(Type tp) {
- tp instanceof PrimitiveType or
- tp instanceof BoxedType or
- tp instanceof NumberType or
- tp instanceof VoidType
-}
-
-/**
- * Holds if the given method can be overridden, that is, it is not final,
- * static, or private.
- */
-predicate isOverridable(Method m) {
- not m.getDeclaringType().isFinal() and
- not m.isFinal() and
- not m.isStatic() and
- not m.isPrivate()
-}
diff --git a/java/ql/automodel/src/AutomodelSharedCharacteristics.qll b/java/ql/automodel/src/AutomodelSharedCharacteristics.qll
deleted file mode 100644
index 273c5d30dec9..000000000000
--- a/java/ql/automodel/src/AutomodelSharedCharacteristics.qll
+++ /dev/null
@@ -1,412 +0,0 @@
-float maximalConfidence() { result = 1.0 }
-
-float highConfidence() { result = 0.9 }
-
-float mediumConfidence() { result = 0.6 }
-
-/**
- * A specification of how to instantiate the shared characteristics for a given candidate class.
- *
- * The `CandidateSig` implementation specifies a type to use for Endpoints (eg., `ParameterNode`), as well as a type
- * to label endpoint classes (the `EndpointType`). One of the endpoint classes needs to be a 'negative' class, meaning
- * "not any of the other known endpoint types".
- */
-signature module CandidateSig {
- /**
- * An endpoint is a potential candidate for modeling. This will typically be bound to the language's
- * DataFlow node class, or a subtype thereof.
- */
- class Endpoint {
- /**
- * Gets the kind of this endpoint, either "sourceModel" or "sinkModel".
- */
- string getExtensibleType();
-
- /**
- * Gets a string representation of this endpoint.
- */
- string toString();
- }
-
- /**
- * A related location for an endpoint. This will typically be bound to the supertype of all AST nodes (eg., `Top`).
- */
- class RelatedLocation;
-
- /**
- * A label for a related location.
- *
- * Eg., method-doc, class-doc, etc.
- */
- class RelatedLocationType;
-
- /**
- * An endpoint type considered by this specification.
- */
- class EndpointType extends string;
-
- /**
- * A sink endpoint type considered by this specification.
- */
- class SinkType extends EndpointType;
-
- /**
- * A source endpoint type considered by this specification.
- */
- class SourceType extends EndpointType;
-
- /**
- * Gets the endpoint as a location.
- *
- * This is a utility function to convert an endpoint to its corresponding location.
- */
- RelatedLocation asLocation(Endpoint e);
-
- /**
- * Defines what MaD kinds are known, and what endpoint type they correspond to.
- */
- predicate isKnownKind(string kind, EndpointType type);
-
- /**
- * Holds if `e` is a flow sanitizer, and has type `t`.
- */
- predicate isSanitizer(Endpoint e, EndpointType t);
-
- /**
- * Holds if `e` is a sink with the label `kind`, and provenance `provenance`.
- */
- predicate isSink(Endpoint e, string kind, string provenance);
-
- /**
- * Holds if `e` is a source with the label `kind`, and provenance `provenance`.
- */
- predicate isSource(Endpoint e, string kind, string provenance);
-
- /**
- * Holds if `e` is not a source or sink of any kind.
- */
- predicate isNeutral(Endpoint e);
-
- /**
- * Gets a related location.
- *
- * A related location is a source code location that may hold extra information about an endpoint that can be useful
- * to the machine learning model.
- *
- * For example, a related location for a method call may be the documentation comment of a method.
- */
- RelatedLocation getRelatedLocation(Endpoint e, RelatedLocationType name);
-}
-
-/**
- * A set of shared characteristics for a given candidate class.
- *
- * This module is language-agnostic, although the `CandidateSig` module will be language-specific.
- *
- * The language specific implementation can also further extend the behavior of this module by adding additional
- * implementations of endpoint characteristics exported by this module.
- */
-module SharedCharacteristics {
- predicate isSink = Candidate::isSink/3;
-
- predicate isNeutral = Candidate::isNeutral/1;
-
- predicate isModeled(Candidate::Endpoint e, string kind, string extensibleKind, string provenance) {
- Candidate::isSink(e, kind, provenance) and extensibleKind = "sinkModel"
- or
- Candidate::isSource(e, kind, provenance) and extensibleKind = "sourceModel"
- }
-
- /**
- * Holds if `endpoint` is modeled as `endpointType`.
- */
- predicate isKnownAs(
- Candidate::Endpoint endpoint, Candidate::EndpointType endpointType,
- EndpointCharacteristic characteristic
- ) {
- // If the list of characteristics includes positive indicators with maximal confidence for this class, then it's a
- // known sink for the class.
- characteristic.appliesToEndpoint(endpoint) and
- characteristic.hasImplications(endpointType, true, maximalConfidence())
- }
-
- /**
- * Gets a potential type of this endpoint to make sure that sources are
- * associated with source types and sinks with sink types.
- */
- Candidate::EndpointType getAPotentialType(Candidate::Endpoint endpoint) {
- endpoint.getExtensibleType() = "sourceModel" and
- result instanceof Candidate::SourceType
- or
- endpoint.getExtensibleType() = "sinkModel" and
- result instanceof Candidate::SinkType
- }
-
- /**
- * Holds if the given `endpoint` should be considered as a candidate for type `endpointType`,
- * and classified by the ML model.
- *
- * A candidate is an endpoint that cannot be excluded from `endpointType` based on its characteristics.
- */
- predicate isCandidate(Candidate::Endpoint endpoint, Candidate::EndpointType endpointType) {
- endpointType = getAPotentialType(endpoint) and
- not exists(getAnExcludingCharacteristic(endpoint, endpointType))
- }
-
- /**
- * Gets the related location of `e` with name `name`, if it exists.
- * Otherwise, gets the candidate itself.
- */
- Candidate::RelatedLocation getRelatedLocationOrCandidate(
- Candidate::Endpoint e, Candidate::RelatedLocationType type
- ) {
- if exists(Candidate::getRelatedLocation(e, type))
- then result = Candidate::getRelatedLocation(e, type)
- else result = Candidate::asLocation(e)
- }
-
- /**
- * Gets a characteristics that disbar `endpoint` from being a candidate for `endpointType`
- * with at least medium confidence.
- */
- EndpointCharacteristic getAnExcludingCharacteristic(
- Candidate::Endpoint endpoint, Candidate::EndpointType endpointType
- ) {
- result.appliesToEndpoint(endpoint) and
- exists(float confidence |
- confidence >= mediumConfidence() and
- result.hasImplications(endpointType, false, confidence)
- )
- }
-
- /**
- * A set of characteristics that a particular endpoint might have. This set of characteristics is used to make decisions
- * about whether to include the endpoint in the training set and with what kind, as well as whether to score the
- * endpoint at inference time.
- */
- abstract class EndpointCharacteristic extends string {
- /**
- * Holds for the string that is the name of the characteristic. This should describe some property of an endpoint
- * that is meaningful for determining whether it's a sink, and if so, of which sink type.
- */
- bindingset[this]
- EndpointCharacteristic() { any() }
-
- /**
- * Holds for endpoints that have this characteristic.
- */
- abstract predicate appliesToEndpoint(Candidate::Endpoint n);
-
- /**
- * This predicate describes what the characteristic tells us about an endpoint.
- *
- * Params:
- * endpointType: The sink/source type.
- * isPositiveIndicator: If true, this characteristic indicates that this endpoint _is_ a member of the class; if
- * false, it indicates that it _isn't_ a member of the class.
- * confidence: A float in [0, 1], which tells us how strong an indicator this characteristic is for the endpoint
- * belonging / not belonging to the given class. A confidence near zero means this characteristic is a very weak
- * indicator of whether or not the endpoint belongs to the class. A confidence of 1 means that all endpoints with
- * this characteristic definitively do/don't belong to the class.
- */
- abstract predicate hasImplications(
- Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
- );
-
- /** Indicators with confidence at or above this threshold are considered to be high-confidence indicators. */
- final float getHighConfidenceThreshold() { result = 0.8 }
- }
-
- /**
- * A high-confidence characteristic that indicates that an endpoint is a sink of a specified type. These endpoints can
- * be used as positive samples for training or for a few-shot prompt.
- */
- abstract class SinkCharacteristic extends EndpointCharacteristic {
- bindingset[this]
- SinkCharacteristic() { any() }
-
- abstract Candidate::EndpointType getSinkType();
-
- final override predicate hasImplications(
- Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
- ) {
- endpointType = this.getSinkType() and
- isPositiveIndicator = true and
- confidence = maximalConfidence()
- }
- }
-
- /**
- * A high-confidence characteristic that indicates that an endpoint is a source of a specified type. These endpoints can
- * be used as positive samples for training or for a few-shot prompt.
- */
- abstract class SourceCharacteristic extends EndpointCharacteristic {
- bindingset[this]
- SourceCharacteristic() { any() }
-
- abstract Candidate::EndpointType getSourceType();
-
- final override predicate hasImplications(
- Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
- ) {
- endpointType = this.getSourceType() and
- isPositiveIndicator = true and
- confidence = maximalConfidence()
- }
- }
-
- /**
- * A high-confidence characteristic that indicates that an endpoint is not a sink of any type. These endpoints can be
- * used as negative samples for training or for a few-shot prompt.
- */
- abstract class NotASinkCharacteristic extends EndpointCharacteristic {
- bindingset[this]
- NotASinkCharacteristic() { any() }
-
- override predicate hasImplications(
- Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
- ) {
- endpointType instanceof Candidate::SinkType and
- isPositiveIndicator = false and
- confidence = highConfidence()
- }
- }
-
- /**
- * A high-confidence characteristic that indicates that an endpoint is not a source of any type. These endpoints can be
- * used as negative samples for training or for a few-shot prompt.
- */
- abstract class NotASourceCharacteristic extends EndpointCharacteristic {
- bindingset[this]
- NotASourceCharacteristic() { any() }
-
- override predicate hasImplications(
- Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
- ) {
- endpointType instanceof Candidate::SourceType and
- isPositiveIndicator = false and
- confidence = highConfidence()
- }
- }
-
- /**
- * A high-confidence characteristic that indicates that an endpoint is neither a source nor a sink of any type.
- */
- abstract class NeitherSourceNorSinkCharacteristic extends NotASinkCharacteristic,
- NotASourceCharacteristic
- {
- bindingset[this]
- NeitherSourceNorSinkCharacteristic() { any() }
-
- final override predicate hasImplications(
- Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
- ) {
- NotASinkCharacteristic.super.hasImplications(endpointType, isPositiveIndicator, confidence) or
- NotASourceCharacteristic.super.hasImplications(endpointType, isPositiveIndicator, confidence)
- }
- }
-
- /**
- * A medium-confidence characteristic that indicates that an endpoint is unlikely to be a sink of any type. These
- * endpoints can be excluded from scoring at inference time, both to save time and to avoid false positives. They should
- * not, however, be used as negative samples for training or for a few-shot prompt, because they may include a small
- * number of sinks.
- */
- abstract class LikelyNotASinkCharacteristic extends EndpointCharacteristic {
- bindingset[this]
- LikelyNotASinkCharacteristic() { any() }
-
- override predicate hasImplications(
- Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
- ) {
- endpointType instanceof Candidate::SinkType and
- isPositiveIndicator = false and
- confidence = mediumConfidence()
- }
- }
-
- /**
- * A characteristic that indicates not necessarily that an endpoint is not a sink, but rather that it is not a sink
- * that's interesting to model in the standard Java libraries. These filters should be removed when extracting sink
- * candidates within a user's codebase for customized modeling.
- *
- * These endpoints should not be used as negative samples for training or for a few-shot prompt, because they are not
- * necessarily non-sinks.
- */
- abstract class UninterestingToModelCharacteristic extends EndpointCharacteristic {
- bindingset[this]
- UninterestingToModelCharacteristic() { any() }
-
- override predicate hasImplications(
- Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
- ) {
- endpointType instanceof Candidate::SinkType and
- isPositiveIndicator = false and
- confidence = mediumConfidence()
- }
- }
-
- /**
- * Contains default implementations that are derived solely from the `CandidateSig` implementation.
- */
- private module DefaultCharacteristicImplementations {
- /**
- * Endpoints identified as sinks by the `CandidateSig` implementation are sinks with maximal confidence.
- */
- private class KnownSinkCharacteristic extends SinkCharacteristic {
- string madKind;
- Candidate::EndpointType endpointType;
- string provenance;
-
- KnownSinkCharacteristic() {
- Candidate::isKnownKind(madKind, endpointType) and
- // bind "this" to a unique string differing from that of the SinkType classes
- this = madKind + "_" + provenance + "_characteristic" and
- Candidate::isSink(_, madKind, provenance)
- }
-
- override predicate appliesToEndpoint(Candidate::Endpoint e) {
- Candidate::isSink(e, madKind, provenance)
- }
-
- override Candidate::EndpointType getSinkType() { result = endpointType }
- }
-
- private class KnownSourceCharacteristic extends SourceCharacteristic {
- string madKind;
- Candidate::EndpointType endpointType;
- string provenance;
-
- KnownSourceCharacteristic() {
- Candidate::isKnownKind(madKind, endpointType) and
- // bind "this" to a unique string differing from that of the SinkType classes
- this = madKind + "_" + provenance + "_characteristic" and
- Candidate::isSource(_, madKind, provenance)
- }
-
- override predicate appliesToEndpoint(Candidate::Endpoint e) {
- Candidate::isSource(e, madKind, provenance)
- }
-
- override Candidate::EndpointType getSourceType() { result = endpointType }
- }
-
- /**
- * A negative characteristic that indicates that an endpoint was manually modeled as a neutral model.
- */
- private class NeutralModelCharacteristic extends NeitherSourceNorSinkCharacteristic {
- NeutralModelCharacteristic() { this = "known non-sink" }
-
- override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isNeutral(e) }
- }
-
- /**
- * A negative characteristic that indicates that an endpoint is a sanitizer, and thus not a source.
- */
- private class IsSanitizerCharacteristic extends NotASourceCharacteristic {
- IsSanitizerCharacteristic() { this = "known sanitizer" }
-
- override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isSanitizer(e, _) }
- }
- }
-}
diff --git a/java/ql/automodel/src/AutomodelSinkModelMrvaQueries.ql b/java/ql/automodel/src/AutomodelSinkModelMrvaQueries.ql
deleted file mode 100644
index ed61ccfbbfdc..000000000000
--- a/java/ql/automodel/src/AutomodelSinkModelMrvaQueries.ql
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * This file contains query predicates for use when gathering metrics at scale using Multi Repo
- * Variant Analysis.
- */
-
-private import java
-private import AutomodelAlertSinkUtil
-
-/**
- * Holds if `alertCount` is the number of alerts for the query with ID `queryId` for which the
- * sinks correspond to the given `ai-generated` sink model.
- */
-query predicate sinkModelCountPerQuery(
- string queryId, int alertCount, string package, string type, boolean subtypes, string name,
- string signature, string input, string ext, string kind, string provenance
-) {
- exists(SinkModel s |
- sinkModelTallyPerQuery(queryId, alertCount, s) and
- s.getProvenance() = "ai-generated" and
- s.getPackage() = package and
- s.getType() = type and
- s.getSubtypes() = subtypes and
- s.getName() = name and
- s.getSignature() = signature and
- s.getInput() = input and
- s.getExt() = ext and
- s.getKind() = kind and
- s.getProvenance() = provenance
- )
-}
-
-/**
- * Holds if `instanceCount` is the number of instances corresponding to the given `ai-generated`
- * sink model (as identified by the `package`, `name`, `input`, etc.).
- */
-query predicate instanceCount(
- int instanceCount, string package, string type, boolean subtypes, string name, string signature,
- string input, string ext, string kind, string provenance
-) {
- exists(SinkModel s |
- instanceCount = s.getInstanceCount() and
- instanceCount > 0 and
- s.getProvenance() = "ai-generated" and
- s.getPackage() = package and
- s.getType() = type and
- s.getSubtypes() = subtypes and
- s.getName() = name and
- s.getSignature() = signature and
- s.getInput() = input and
- s.getExt() = ext and
- s.getKind() = kind and
- s.getProvenance() = provenance
- )
-}
-
-// MRVA requires a select clause, so we repurpose it to tell us which query predicates had results.
-from string hadResults
-where
- sinkModelCountPerQuery(_, _, _, _, _, _, _, _, _, _, _) and hadResults = "sinkModelCountPerQuery"
- or
- instanceCount(_, _, _, _, _, _, _, _, _, _) and hadResults = "instanceCount"
-select hadResults
diff --git a/java/ql/automodel/src/change-notes/2024-11-19-drop-automodel.md b/java/ql/automodel/src/change-notes/2024-11-19-drop-automodel.md
new file mode 100644
index 000000000000..2b554d6de204
--- /dev/null
+++ b/java/ql/automodel/src/change-notes/2024-11-19-drop-automodel.md
@@ -0,0 +1,4 @@
+---
+category: breaking
+---
+* Dropped the Java Automodel queries.
diff --git a/java/ql/automodel/src/codeql-pack.release.yml b/java/ql/automodel/src/codeql-pack.release.yml
deleted file mode 100644
index 56a2fb388725..000000000000
--- a/java/ql/automodel/src/codeql-pack.release.yml
+++ /dev/null
@@ -1,2 +0,0 @@
----
-lastReleaseVersion: 1.0.11
diff --git a/java/ql/automodel/src/qlpack.yml b/java/ql/automodel/src/qlpack.yml
deleted file mode 100644
index 4acf2219db30..000000000000
--- a/java/ql/automodel/src/qlpack.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-name: codeql/java-automodel-queries
-version: 1.0.12-dev
-groups:
- - java
- - automodel
-dependencies:
- codeql/java-all: ${workspace}
-dataExtensions:
- - AutomodelCandidateFilter.yml
-warnOnImplicitThis: true
\ No newline at end of file
diff --git a/java/ql/automodel/test/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractionTests.ql b/java/ql/automodel/test/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractionTests.ql
deleted file mode 100644
index b7e1efc45325..000000000000
--- a/java/ql/automodel/test/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractionTests.ql
+++ /dev/null
@@ -1,35 +0,0 @@
-import java
-import AutomodelApplicationModeCharacteristics as Characteristics
-import AutomodelExtractionTests
-
-module TestHelper implements TestHelperSig {
- Location getEndpointLocation(Characteristics::Endpoint endpoint) {
- result = endpoint.asTop().getLocation()
- }
-
- predicate isCandidate(
- Characteristics::Endpoint endpoint, string name, string signature, string input, string output,
- string extensibleType
- ) {
- Characteristics::isCandidate(endpoint, _, _, _, name, signature, input, output, _,
- extensibleType, _)
- }
-
- predicate isPositiveExample(
- Characteristics::Endpoint endpoint, string endpointType, string name, string signature,
- string input, string output, string extensibleType
- ) {
- Characteristics::isPositiveExample(endpoint, endpointType, _, _, _, name, signature, input,
- output, _, extensibleType)
- }
-
- predicate isNegativeExample(
- Characteristics::Endpoint endpoint, string name, string signature, string input, string output,
- string extensibleType
- ) {
- Characteristics::isNegativeExample(endpoint, _, _, _, _, _, name, signature, input, output, _,
- extensibleType)
- }
-}
-
-import MakeTest>
diff --git a/java/ql/automodel/test/AutomodelApplicationModeExtraction/PluginImpl.java b/java/ql/automodel/test/AutomodelApplicationModeExtraction/PluginImpl.java
deleted file mode 100644
index b0f3482a7322..000000000000
--- a/java/ql/automodel/test/AutomodelApplicationModeExtraction/PluginImpl.java
+++ /dev/null
@@ -1,8 +0,0 @@
-import hudson.Plugin;
-
-public class PluginImpl extends Plugin {
- @Override
- public void configure(String name, String value) { // $ sourceModelCandidate=configure(String,String):Parameter[0] sourceModelCandidate=configure(String,String):Parameter[1]
- // ...
- }
-}
diff --git a/java/ql/automodel/test/AutomodelApplicationModeExtraction/Test.java b/java/ql/automodel/test/AutomodelApplicationModeExtraction/Test.java
deleted file mode 100644
index 9691cf86c150..000000000000
--- a/java/ql/automodel/test/AutomodelApplicationModeExtraction/Test.java
+++ /dev/null
@@ -1,112 +0,0 @@
-package com.github.codeql.test;
-
-import java.io.InputStream;
-import java.io.PrintWriter;
-import java.nio.file.CopyOption;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Supplier;
-import java.io.File;
-import java.io.FileFilter;
-import java.nio.file.FileVisitOption;
-import java.net.URLConnection;
-import java.util.concurrent.FutureTask;
-
-class Test {
- public static void main(String[] args) throws Exception {
- AtomicReference reference = new AtomicReference<>(); // uninteresting (parameterless constructor)
- reference.set( // $ sinkModelCandidate=set(Object):Argument[this]
- args[0] // $ negativeSinkExample=set(Object):Argument[0] // modeled as a flow step
- ); // not a source candidate (return type is void)
- }
-
- public static void callSupplier(Supplier supplier) {
- supplier.get(); // not a source candidate (lambda flow)
- }
-
- public static void copyFiles(Path source, Path target, CopyOption option) throws Exception {
- Files.copy(
- source, // $ positiveSinkExample=copy(Path,Path,CopyOption[]):Argument[0](path-injection)
- target, // $ positiveSinkExample=copy(Path,Path,CopyOption[]):Argument[1](path-injection)
- option // no candidate (not modeled, but source and target are modeled)
- ); // $ sourceModelCandidate=copy(Path,Path,CopyOption[]):ReturnValue
- }
-
- public static InputStream getInputStream(Path openPath) throws Exception {
- return Files.newInputStream(
- openPath // $ sinkModelCandidate=newInputStream(Path,OpenOption[]):Argument[0] positiveSinkExample=newInputStream(Path,OpenOption[]):Argument[0](path-injection) // sink candidate because "only" ai-modeled, and useful as a candidate in regression testing
- ); // $ sourceModelCandidate=newInputStream(Path,OpenOption[]):ReturnValue
- }
-
- public static InputStream getInputStream(String openPath, String otherPath) throws Exception {
- return Test.getInputStream( // the call is not a source candidate (argument to local call)
- Paths.get(
- openPath, // $ negativeSinkExample=get(String,String[]):Argument[0] // modeled as a flow step
- otherPath
- ) // $ sourceModelCandidate=get(String,String[]):ReturnValue negativeSinkExample=get(String,String[]):Argument[1]
- );
- }
-
- public static int compareFiles(File f1, File f2) {
- return f1.compareTo( // $ negativeSinkExample=compareTo(File):Argument[this]
- f2 // $ negativeSinkExample=compareTo(File):Argument[0] // modeled as not a sink
- ); // not a source candidate (return type is int)
- }
-
- public static void FilesWalkExample(Path p, FileVisitOption o) throws Exception {
- Files.walk(
- p, // $ negativeSinkExample=walk(Path,FileVisitOption[]):Argument[0] // modeled as a flow step
- o, // the implicit varargs array is a candidate, annotated on the last line of the call
- o // not a candidate (only the first arg corresponding to a varargs array
- // is extracted)
- ); // $ sourceModelCandidate=walk(Path,FileVisitOption[]):ReturnValue sinkModelCandidate=walk(Path,FileVisitOption[]):Argument[1]
- }
-
- public static void WebSocketExample(URLConnection c) throws Exception {
- c.getInputStream(); // $ sinkModelCandidate=getInputStream():Argument[this] positiveSourceExample=getInputStream():ReturnValue(remote) // not a source candidate (manual modeling)
- c.connect(); // $ sinkModelCandidate=connect():Argument[this] // not a source candidate (return type is void)
- }
-
- public static void fileFilterExample(File f, FileFilter ff) {
- f.listFiles( // $ sinkModelCandidate=listFiles(FileFilter):Argument[this]
- ff
- ); // $ sourceModelCandidate=listFiles(FileFilter):ReturnValue
- }
-}
-
-class OverrideTest extends Exception {
- public void printStackTrace(PrintWriter writer) { // $ sourceModelCandidate=printStackTrace(PrintWriter):Parameter[0]
- return;
- }
-
-}
-
-class TaskUtils {
- public FutureTask getTask() {
- FutureTask ft = new FutureTask(() -> {
- // ^-- no sink candidate for the `this` qualifier of a constructor
- return 42;
- });
- return ft;
- }
-}
-
-class MoreTests {
- public static void FilesListExample(Path p) throws Exception {
- Files.list(
- Files.createDirectories(
- p // $ positiveSinkExample=createDirectories(Path,FileAttribute[]):Argument[0](path-injection)
- ) // $ sourceModelCandidate=createDirectories(Path,FileAttribute[]):ReturnValue negativeSinkExample=list(Path):Argument[0] // modeled as a flow step
- ); // $ sourceModelCandidate=list(Path):ReturnValue
-
- Files.delete(
- p // $ sinkModelCandidate=delete(Path):Argument[0] positiveSinkExample=delete(Path):Argument[0](path-injection)
- ); // not a source candidate (return type is void)
-
- Files.deleteIfExists(
- p // $ sinkModelCandidate=deleteIfExists(Path):Argument[0] positiveSinkExample=deleteIfExists(Path):Argument[0](path-injection)
- ); // not a source candidate (return type is boolean)
- }
-}
\ No newline at end of file
diff --git a/java/ql/automodel/test/AutomodelApplicationModeExtraction/hudson/Plugin.java b/java/ql/automodel/test/AutomodelApplicationModeExtraction/hudson/Plugin.java
deleted file mode 100644
index 3ad79abb8df1..000000000000
--- a/java/ql/automodel/test/AutomodelApplicationModeExtraction/hudson/Plugin.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package hudson;
-
-/** Plugin doc */
-public class Plugin {
- /** Configure method doc */
- public void configure(String name, String value) {}
-}
diff --git a/java/ql/automodel/test/AutomodelExtractionTests.qll b/java/ql/automodel/test/AutomodelExtractionTests.qll
deleted file mode 100644
index fbc407c67f05..000000000000
--- a/java/ql/automodel/test/AutomodelExtractionTests.qll
+++ /dev/null
@@ -1,77 +0,0 @@
-import java
-import TestUtilities.InlineExpectationsTest
-import AutomodelSharedCharacteristics
-
-signature module TestHelperSig {
- Location getEndpointLocation(Candidate::Endpoint e);
-
- predicate isCandidate(
- Candidate::Endpoint e, string name, string signature, string input, string output,
- string extensibleType
- );
-
- predicate isPositiveExample(
- Candidate::Endpoint e, string endpointType, string name, string signature, string input,
- string output, string extensibleType
- );
-
- predicate isNegativeExample(
- Candidate::Endpoint e, string name, string signature, string input, string output,
- string extensibleType
- );
-}
-
-module Extraction TestHelper> implements TestSig {
- string getARelevantTag() {
- result in [
- "sourceModelCandidate", "sinkModelCandidate", // a candidate source/sink
- "positiveSourceExample", "positiveSinkExample", // a known source/sink
- "negativeSourceExample", "negativeSinkExample" // a known non-source/non-sink
- ]
- }
-
- /**
- * If `extensibleType` is `sourceModel` then the result is `ifSource`, if it
- * is `sinkModel` then the result is `ifSink`.
- */
- bindingset[extensibleType, ifSource, ifSink]
- private string ifSource(string extensibleType, string ifSource, string ifSink) {
- extensibleType = "sourceModel" and result = ifSource
- or
- extensibleType = "sinkModel" and result = ifSink
- }
-
- additional predicate selectEndpoint(
- Candidate::Endpoint endpoint, string name, string signature, string input, string output,
- string extensibleType, string tag, string suffix
- ) {
- TestHelper::isCandidate(endpoint, name, signature, input, output, extensibleType) and
- tag = ifSource(extensibleType, "sourceModelCandidate", "sinkModelCandidate") and
- suffix = ""
- or
- TestHelper::isNegativeExample(endpoint, name, signature, input, output, extensibleType) and
- tag = "negative" + ifSource(extensibleType, "Source", "Sink") + "Example" and
- suffix = ""
- or
- exists(string endpointType |
- TestHelper::isPositiveExample(endpoint, endpointType, name, signature, input, output,
- extensibleType) and
- tag = "positive" + ifSource(extensibleType, "Source", "Sink") + "Example" and
- suffix = "(" + endpointType + ")"
- )
- }
-
- predicate hasActualResult(Location location, string element, string tag, string value) {
- exists(
- Candidate::Endpoint endpoint, string name, string signature, string input, string output,
- string extensibleType, string suffix
- |
- selectEndpoint(endpoint, name, signature, input, output, extensibleType, tag, suffix)
- |
- TestHelper::getEndpointLocation(endpoint) = location and
- endpoint.toString() = element and
- // for source models only the output is relevant, and vice versa for sink models
- value = name + signature + ":" + ifSource(extensibleType, output, input) + suffix
- )
- }
-}
diff --git a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractionTests.ql b/java/ql/automodel/test/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractionTests.ql
deleted file mode 100644
index 0d5e86118705..000000000000
--- a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractionTests.ql
+++ /dev/null
@@ -1,35 +0,0 @@
-import java
-import AutomodelFrameworkModeCharacteristics as Characteristics
-import AutomodelExtractionTests
-
-module TestHelper implements TestHelperSig {
- Location getEndpointLocation(Characteristics::Endpoint endpoint) {
- result = endpoint.asTop().getLocation()
- }
-
- predicate isCandidate(
- Characteristics::Endpoint endpoint, string name, string signature, string input, string output,
- string extensibleType
- ) {
- Characteristics::isCandidate(endpoint, _, _, _, name, signature, input, output, _,
- extensibleType, _)
- }
-
- predicate isPositiveExample(
- Characteristics::Endpoint endpoint, string endpointType, string name, string signature,
- string input, string output, string extensibleType
- ) {
- Characteristics::isPositiveExample(endpoint, endpointType, _, _, _, name, signature, input,
- output, _, extensibleType)
- }
-
- predicate isNegativeExample(
- Characteristics::Endpoint endpoint, string name, string signature, string input, string output,
- string extensibleType
- ) {
- Characteristics::isNegativeExample(endpoint, _, _, _, _, _, name, signature, input, output, _,
- extensibleType)
- }
-}
-
-import MakeTest>
diff --git a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/com/github/codeql/test/MyWriter.java b/java/ql/automodel/test/AutomodelFrameworkModeExtraction/com/github/codeql/test/MyWriter.java
deleted file mode 100644
index 62bd773cc2e2..000000000000
--- a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/com/github/codeql/test/MyWriter.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.github.codeql.test;
-
-public class MyWriter extends java.io.Writer {
- @Override
- public void write(char[] cbuf, int off, int len) { // $ sinkModelCandidate=write(char[],int,int):Argument[this] positiveSinkExample=write(char[],int,int):Argument[0](file-content-store) sourceModelCandidate=write(char[],int,int):Parameter[this] sourceModelCandidate=write(char[],int,int):Parameter[0]
- }
-
- @Override
- public void close() { // $ sinkModelCandidate=close():Argument[this] sourceModelCandidate=close():Parameter[this]
- }
-
- @Override
- public void flush() { // $ sinkModelCandidate=flush():Argument[this] sourceModelCandidate=flush():Parameter[this]
- }
-}
diff --git a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/com/github/codeql/test/NonPublicClass.java b/java/ql/automodel/test/AutomodelFrameworkModeExtraction/com/github/codeql/test/NonPublicClass.java
deleted file mode 100644
index b106d3da5940..000000000000
--- a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/com/github/codeql/test/NonPublicClass.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.github.codeql.test;
-
-/**
- * No candidates in this class, as it's not public!
- */
-class NonPublicClass {
- public void noCandidates(String here) {
- System.out.println(here);
- }
-}
diff --git a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/com/github/codeql/test/PublicClass.java b/java/ql/automodel/test/AutomodelFrameworkModeExtraction/com/github/codeql/test/PublicClass.java
deleted file mode 100644
index 79fabff0664b..000000000000
--- a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/com/github/codeql/test/PublicClass.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.github.codeql.test;
-
-public class PublicClass {
- public void stuff(String arg) { // $ sinkModelCandidate=stuff(String):Argument[this] sourceModelCandidate=stuff(String):Parameter[this] sinkModelCandidate=stuff(String):Argument[0] sourceModelCandidate=stuff(String):Parameter[0] // source candidates because it is an overrideable method
- System.out.println(arg);
- }
-
- public static void staticStuff(String arg) { // $ sinkModelCandidate=staticStuff(String):Argument[0] // `arg` is not a source candidate (not overrideabe); `this` is not a candidate (static method)
- System.out.println(arg);
- }
-
- protected void nonPublicStuff(String arg) { // $ sinkModelCandidate=nonPublicStuff(String):Argument[this] sourceModelCandidate=nonPublicStuff(String):Parameter[this] sinkModelCandidate=nonPublicStuff(String):Argument[0] sourceModelCandidate=nonPublicStuff(String):Parameter[0]
- System.out.println(arg);
- }
-
- void packagePrivateStuff(String arg) { // no candidates because the method is not public
- System.out.println(arg);
- }
-
- public PublicClass(Object input) { // $ sourceModelCandidate=PublicClass(Object):ReturnValue sinkModelCandidate=PublicClass(Object):Argument[0] // `this` is not a candidate because it is a constructor
- }
-
- // `input` and `input` are source candidates, but not sink candidates (is-style method)
- public Boolean isIgnored(Object input) { // $ negativeSinkExample=isIgnored(Object):Argument[this] sourceModelCandidate=isIgnored(Object):Parameter[this] negativeSinkExample=isIgnored(Object):Argument[0] sourceModelCandidate=isIgnored(Object):Parameter[0]
- return false;
- }
-}
diff --git a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/com/github/codeql/test/PublicInterface.java b/java/ql/automodel/test/AutomodelFrameworkModeExtraction/com/github/codeql/test/PublicInterface.java
deleted file mode 100644
index d4f80b3c6988..000000000000
--- a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/com/github/codeql/test/PublicInterface.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.github.codeql.test;
-
-public interface PublicInterface {
- public int stuff(String arg); // $ sinkModelCandidate=stuff(String):Argument[this] sourceModelCandidate=stuff(String):Parameter[this] sinkModelCandidate=stuff(String):Argument[0] sourceModelCandidate=stuff(String):Parameter[0] // result is _not_ a source candidate source (primitive return type)
-
- public static void staticStuff(String arg) { // $ sinkModelCandidate=staticStuff(String):Argument[0] // not a source candidate (static method)
- System.out.println(arg);
- }
-}
diff --git a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/java/io/File.java b/java/ql/automodel/test/AutomodelFrameworkModeExtraction/java/io/File.java
deleted file mode 100644
index 8bfe83e23397..000000000000
--- a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/java/io/File.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package java.io;
-
-public class File {
- public int compareTo( // $ negativeSinkExample=compareTo(File):Argument[this] sourceModelCandidate=compareTo(File):Parameter[this] // modeled as neutral for sinks
- File pathname // $ negativeSinkExample=compareTo(File):Argument[0] sourceModelCandidate=compareTo(File):Parameter[0] // modeled as neutral for sinks
- ) {
- return 0;
- }
-
- public boolean setLastModified(long time) { // $ sinkModelCandidate=setLastModified(long):Argument[this] sourceModelCandidate=setLastModified(long):Parameter[this] // time is not a candidate (primitive type)
- return false;
- } // return value is not a source candidate because it's a primitive
-}
diff --git a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/java/nio/file/Files.java b/java/ql/automodel/test/AutomodelFrameworkModeExtraction/java/nio/file/Files.java
deleted file mode 100644
index a833ba5f6e20..000000000000
--- a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/java/nio/file/Files.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package java.nio.file;
-
-import java.io.InputStream;
-import java.io.FileInputStream;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.file.OpenOption;
-
-public class Files {
- public static void copy( // method result is not a candidate source (void)
- Path source, // $ positiveSinkExample=copy(Path,OutputStream):Argument[0](path-injection) // manual model exists
- OutputStream out // $ sinkModelCandidate=copy(Path,OutputStream):Argument[1]
- /* NB: may be worthwhile to implement the
- same behavior as in application mode where out would not be a
- candidate because there already is a model for another parameter of
- the same method and we assume that methods are always modeled
- completely.
- */
- ) throws IOException {
- // ...
- }
-
- public static InputStream newInputStream( // $ sourceModelCandidate=newInputStream(Path,OpenOption[]):ReturnValue
- Path openPath, // $ positiveSinkExample=newInputStream(Path,OpenOption[]):Argument[0](path-injection) sinkModelCandidate=newInputStream(Path,OpenOption[]):Argument[0] // known sink, but still a candidate (ai-modeled, and useful as a candidate in regression testing)
- OpenOption... options // $ sinkModelCandidate=newInputStream(Path,OpenOption[]):Argument[1]
- ) throws IOException {
- return new FileInputStream(openPath.toFile());
- }
-}
diff --git a/java/ql/automodel/test/change-notes/2024-05-23-Version1.md b/java/ql/automodel/test/change-notes/2024-05-23-Version1.md
deleted file mode 100644
index 5840e51017be..000000000000
--- a/java/ql/automodel/test/change-notes/2024-05-23-Version1.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: breaking
----
-* CodeQL package management is now generally available, and all GitHub-produced CodeQL packages have had their version numbers increased to 1.0.0.
diff --git a/java/ql/automodel/test/qlpack.yml b/java/ql/automodel/test/qlpack.yml
deleted file mode 100644
index 46138d9435c0..000000000000
--- a/java/ql/automodel/test/qlpack.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-name: codeql/java-automodel-tests
-version: 1.0.0-dev
-groups:
- - java
- - automodel
- - test
-dependencies:
- codeql/java-all: ${workspace}
- codeql/java-automodel-queries: ${workspace}
- codeql/java-tests: ${workspace}
-extractor: java
-tests: .
-warnOnImplicitThis: true
\ No newline at end of file
diff --git a/java/ql/lib/change-notes/2024-11-04-list-of-constants-sanitizer.md b/java/ql/lib/change-notes/2024-11-04-list-of-constants-sanitizer.md
new file mode 100644
index 000000000000..dea1e7ff81e1
--- /dev/null
+++ b/java/ql/lib/change-notes/2024-11-04-list-of-constants-sanitizer.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Calling `coll.contains(x)` is now a taint sanitizer (for any query) for the value `x`, where `coll` is a collection of constants.
diff --git a/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll b/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll
index e7d34d166b12..c931817e00b4 100644
--- a/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll
@@ -28,6 +28,7 @@ private module Frameworks {
private import semmle.code.java.frameworks.ThreadLocal
private import semmle.code.java.frameworks.ratpack.RatpackExec
private import semmle.code.java.frameworks.stapler.Stapler
+ private import semmle.code.java.security.ListOfConstantsSanitizer
}
/**
@@ -189,3 +190,8 @@ private class NumberTaintPreservingCallable extends TaintPreservingCallable {
* map-key and map-value content, so that e.g. a tainted `Map` is assumed to have tainted keys and values.
*/
abstract class TaintInheritingContent extends DataFlow::Content { }
+
+/**
+ * A sanitizer in all global taint flow configurations but not in local taint.
+ */
+abstract class DefaultTaintSanitizer extends DataFlow::Node { }
diff --git a/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll b/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll
index c548c5db38be..9e05b69db4ad 100644
--- a/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll
@@ -13,24 +13,27 @@ private import semmle.code.java.dispatch.VirtualDispatch
private import semmle.code.java.dataflow.internal.BaseSSA
private import semmle.code.java.controlflow.Guards
private import codeql.typeflow.TypeFlow
+private import codeql.typeflow.UniversalFlow as UniversalFlow
-private module Input implements TypeFlowInput {
- private newtype TTypeFlowNode =
+/** Gets `t` if it is a `RefType` or the boxed type if `t` is a primitive type. */
+private RefType boxIfNeeded(J::Type t) {
+ t.(PrimitiveType).getBoxedType() = result or
+ result = t
+}
+
+/** Provides the input types and predicates for instantiation of `UniversalFlow`. */
+module FlowStepsInput implements UniversalFlow::UniversalFlowInput {
+ private newtype TFlowNode =
TField(Field f) { not f.getType() instanceof PrimitiveType } or
TSsa(BaseSsaVariable ssa) { not ssa.getSourceVariable().getType() instanceof PrimitiveType } or
TExpr(Expr e) or
TMethod(Method m) { not m.getReturnType() instanceof PrimitiveType }
- /** Gets `t` if it is a `RefType` or the boxed type if `t` is a primitive type. */
- private RefType boxIfNeeded(J::Type t) {
- t.(PrimitiveType).getBoxedType() = result or
- result = t
- }
-
/**
* A `Field`, `BaseSsaVariable`, `Expr`, or `Method`.
*/
- class TypeFlowNode extends TTypeFlowNode {
+ class FlowNode extends TFlowNode {
+ /** Gets a textual representation of this element. */
string toString() {
result = this.asField().toString() or
result = this.asSsa().toString() or
@@ -38,6 +41,7 @@ private module Input implements TypeFlowInput {
result = this.asMethod().toString()
}
+ /** Gets the source location for this element. */
Location getLocation() {
result = this.asField().getLocation() or
result = this.asSsa().getLocation() or
@@ -45,14 +49,19 @@ private module Input implements TypeFlowInput {
result = this.asMethod().getLocation()
}
+ /** Gets the field corresponding to this node, if any. */
Field asField() { this = TField(result) }
+ /** Gets the SSA variable corresponding to this node, if any. */
BaseSsaVariable asSsa() { this = TSsa(result) }
+ /** Gets the expression corresponding to this node, if any. */
Expr asExpr() { this = TExpr(result) }
+ /** Gets the method corresponding to this node, if any. */
Method asMethod() { this = TMethod(result) }
+ /** Gets the type of this node. */
RefType getType() {
result = this.asField().getType() or
result = this.asSsa().getSourceVariable().getType() or
@@ -61,8 +70,6 @@ private module Input implements TypeFlowInput {
}
}
- class Type = RefType;
-
private SrcCallable viableCallable_v1(Call c) {
result = viableImpl_v1(c)
or
@@ -88,7 +95,7 @@ private module Input implements TypeFlowInput {
*
* For a given `n2`, this predicate must include all possible `n1` that can flow to `n2`.
*/
- predicate step(TypeFlowNode n1, TypeFlowNode n2) {
+ predicate step(FlowNode n1, FlowNode n2) {
n2.asExpr().(ChooseExpr).getAResultExpr() = n1.asExpr()
or
exists(Field f, Expr e |
@@ -134,7 +141,7 @@ private module Input implements TypeFlowInput {
/**
* Holds if `null` is the only value that flows to `n`.
*/
- predicate isNullValue(TypeFlowNode n) {
+ predicate isNullValue(FlowNode n) {
n.asExpr() instanceof NullLiteral
or
exists(LocalVariableDeclExpr decl |
@@ -144,11 +151,21 @@ private module Input implements TypeFlowInput {
)
}
- predicate isExcludedFromNullAnalysis(TypeFlowNode n) {
+ predicate isExcludedFromNullAnalysis(FlowNode n) {
// Fields that are never assigned a non-null value are probably set by
// reflection and are thus not always null.
exists(n.asField())
}
+}
+
+private module Input implements TypeFlowInput {
+ import FlowStepsInput
+
+ class TypeFlowNode = FlowNode;
+
+ predicate isExcludedFromNullAnalysis = FlowStepsInput::isExcludedFromNullAnalysis/1;
+
+ class Type = RefType;
predicate exactTypeBase(TypeFlowNode n, RefType t) {
exists(ClassInstanceExpr e |
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll b/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll
index f33a6d7195f0..79f12e57f6a2 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll
@@ -38,6 +38,7 @@ class BaseSsaSourceVariable extends TBaseSsaSourceVariable {
/** Gets the `Callable` in which this `BaseSsaSourceVariable` is defined. */
Callable getEnclosingCallable() { this = TLocalVar(result, _) }
+ /** Gets a textual representation of this element. */
string toString() {
exists(LocalScopeVariable v, Callable c | this = TLocalVar(c, v) |
if c = v.getCallable()
@@ -46,6 +47,7 @@ class BaseSsaSourceVariable extends TBaseSsaSourceVariable {
)
}
+ /** Gets the source location for this element. */
Location getLocation() {
exists(LocalScopeVariable v | this = TLocalVar(_, v) and result = v.getLocation())
}
@@ -482,8 +484,10 @@ class BaseSsaVariable extends TBaseSsaVariable {
this = TSsaEntryDef(_, result)
}
+ /** Gets a textual representation of this element. */
string toString() { none() }
+ /** Gets the source location for this element. */
Location getLocation() { result = this.getCfgNode().getLocation() }
/** Gets the `BasicBlock` in which this SSA variable is defined. */
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll b/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
index ad770b75a3eb..1c7db851a2cc 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll
@@ -161,6 +161,7 @@ private module Cached {
*/
cached
predicate defaultTaintSanitizer(DataFlow::Node node) {
+ node instanceof DefaultTaintSanitizer or
// Ignore paths through test code.
node.getEnclosingCallable().getDeclaringType() instanceof NonSecurityTestClass or
node.asExpr() instanceof ValidatedVariableAccess
diff --git a/java/ql/lib/semmle/code/java/security/Encryption.qll b/java/ql/lib/semmle/code/java/security/Encryption.qll
index 6fc7f6b7d16a..7c9c5ec60dbf 100644
--- a/java/ql/lib/semmle/code/java/security/Encryption.qll
+++ b/java/ql/lib/semmle/code/java/security/Encryption.qll
@@ -223,10 +223,7 @@ string getAnInsecureHashAlgorithmName() {
}
private string rankedInsecureAlgorithm(int i) {
- // In this case we know these are being used for encryption, so we want to match
- // weak hash algorithms too.
- result =
- rank[i](string s | s = getAnInsecureAlgorithmName() or s = getAnInsecureHashAlgorithmName())
+ result = rank[i](string s | s = getAnInsecureAlgorithmName())
}
private string insecureAlgorithmString(int i) {
@@ -249,8 +246,8 @@ string getInsecureAlgorithmRegex() {
string getASecureAlgorithmName() {
result =
[
- "RSA", "SHA-?256", "SHA-?512", "CCM", "GCM", "AES(?)",
- "Blowfish", "ECIES"
+ "RSA", "SHA-?(256|384|512)", "CCM", "GCM", "AES(?)",
+ "Blowfish", "ECIES", "SHA3-(256|384|512)"
]
}
diff --git a/java/ql/lib/semmle/code/java/security/ListOfConstantsSanitizer.qll b/java/ql/lib/semmle/code/java/security/ListOfConstantsSanitizer.qll
new file mode 100644
index 000000000000..cc57fbce648d
--- /dev/null
+++ b/java/ql/lib/semmle/code/java/security/ListOfConstantsSanitizer.qll
@@ -0,0 +1,263 @@
+/**
+ * Provides a default taint sanitizer identifying comparisons against lists of
+ * compile-time constants.
+ */
+
+import java
+private import codeql.typeflow.UniversalFlow as UniversalFlow
+private import semmle.code.java.Collections
+private import semmle.code.java.controlflow.Guards
+private import semmle.code.java.dataflow.internal.BaseSSA
+private import semmle.code.java.dataflow.TaintTracking
+private import semmle.code.java.dataflow.TypeFlow
+private import semmle.code.java.dispatch.VirtualDispatch
+
+private class FlowNode = FlowStepsInput::FlowNode;
+
+/**
+ * Holds if `n2` is an unmodifiable collection constructed from input `n1`,
+ * which is either another collection or a number of elements.
+ */
+private predicate unmodifiableCollectionStep(FlowNode n1, FlowNode n2) {
+ exists(Call c, Callable tgt |
+ n2.asExpr() = c and
+ n1.asExpr() = c.getAnArgument() and
+ c.getCallee().getSourceDeclaration() = tgt
+ |
+ tgt.hasQualifiedName("java.util", "Collections",
+ ["unmodifiableCollection", "unmodifiableList", "unmodifiableSet"])
+ or
+ tgt.hasQualifiedName("java.util", ["List", "Set"], ["copyOf", "of"])
+ or
+ tgt.hasQualifiedName("com.google.common.collect", ["ImmutableList", "ImmutableSet"],
+ ["copyOf", "of"])
+ )
+}
+
+/**
+ * Holds if `n2` is a collection or array constructed from input `n1`, which is
+ * either a collection, an array, or a number of elements.
+ */
+private predicate collectionStep(FlowNode n1, FlowNode n2) {
+ n2.asExpr().(ArrayInit).getAnInit() = n1.asExpr()
+ or
+ n2.asExpr().(ArrayCreationExpr).getInit() = n1.asExpr()
+ or
+ unmodifiableCollectionStep(n1, n2)
+ or
+ exists(Call c, Callable tgt |
+ n2.asExpr() = c and
+ n1.asExpr() = c.getAnArgument() and
+ c.getCallee().getSourceDeclaration() = tgt
+ |
+ tgt.hasQualifiedName("java.util", "Arrays", "asList")
+ or
+ tgt.isStatic() and
+ tgt.hasName(["copyOf", "of"]) and
+ tgt.getDeclaringType().getASourceSupertype+().hasQualifiedName("java.util", "Collection")
+ or
+ tgt instanceof Constructor and
+ tgt.getNumberOfParameters() = 1 and
+ tgt.getParameterType(0) instanceof CollectionType and
+ tgt.getDeclaringType() instanceof CollectionType
+ )
+}
+
+private module BaseUniversalFlow = UniversalFlow::Make;
+
+private module UnmodifiableProp implements BaseUniversalFlow::NullaryPropertySig {
+ predicate hasPropertyBase(FlowNode n) { unmodifiableCollectionStep(_, n) }
+}
+
+/** Holds if the given node is an unmodifiable collection. */
+private predicate unmodifiableCollection =
+ BaseUniversalFlow::FlowNullary::hasProperty/1;
+
+/**
+ * Holds if `v` is a collection or array with an access, `coll`, at which the
+ * element `e` gets added.
+ */
+private predicate collectionAddition(Variable v, VarAccess coll, Expr e) {
+ exists(MethodCall mc, Method m, int arg |
+ mc.getMethod().getSourceDeclaration().overridesOrInstantiates*(m) and
+ mc.getQualifier() = coll and
+ v.getAnAccess() = coll and
+ mc.getArgument(arg) = e
+ |
+ m.hasQualifiedName("java.util", "Collection", ["add", "addAll"]) and
+ m.getNumberOfParameters() = 1 and
+ arg = 0
+ or
+ m.hasQualifiedName("java.util", "List", ["add", "addAll"]) and
+ m.getNumberOfParameters() = 2 and
+ arg = 1
+ or
+ m.hasQualifiedName("java.util", "SequencedCollection", ["addFirst", "addLast"]) and
+ m.getNumberOfParameters() = 1 and
+ arg = 0
+ )
+ or
+ v.getAnAccess() = coll and
+ exists(Assignment assign | assign.getSource() = e |
+ coll = assign.getDest().(ArrayAccess).getArray()
+ )
+}
+
+/**
+ * Holds if `n` represents a definition of `v` and `v` is a collection or
+ * array that has additions occurring as side-effects after its definition.
+ */
+private predicate nodeWithAddition(FlowNode n, Variable v) {
+ collectionAddition(v, _, _) and
+ (
+ n.asField() = v
+ or
+ n.asSsa().getSourceVariable().getVariable() = v and
+ (n.asSsa() instanceof BaseSsaUpdate or n.asSsa().(BaseSsaImplicitInit).isParameterDefinition(_))
+ )
+}
+
+/** Holds if `c` does not add elements to the given collection. */
+private predicate safeCallable(Callable c) {
+ c instanceof CollectionQueryMethod
+ or
+ c instanceof CollectionMethod and
+ c.hasName(["clear", "remove", "removeAll", "stream", "iterator", "toArray"])
+ or
+ c.hasQualifiedName("org.apache.commons.lang3", "StringUtils", "join")
+}
+
+/**
+ * Holds if `n` might be mutated in ways that adds elements that are not
+ * tracked by the `collectionAddition` predicate.
+ */
+private predicate collectionWithPossibleMutation(FlowNode n) {
+ not unmodifiableCollection(n) and
+ (
+ exists(Expr e |
+ n.asExpr() = e and
+ (e.getType() instanceof CollectionType or e.getType() instanceof Array) and
+ not collectionAddition(_, e, _) and
+ not collectionStep(n, _)
+ |
+ exists(ArrayAccess aa | e = aa.getArray())
+ or
+ exists(Call c, Callable tgt | c.getAnArgument() = e or c.getQualifier() = e |
+ tgt = c.getCallee().getSourceDeclaration() and
+ not safeCallable(tgt)
+ )
+ )
+ or
+ exists(FlowNode mid |
+ FlowStepsInput::step(n, mid) and
+ collectionWithPossibleMutation(mid)
+ )
+ )
+}
+
+/**
+ * A collection constructor that constructs an empty mutable collection.
+ */
+private class EmptyCollectionConstructor extends Constructor {
+ EmptyCollectionConstructor() {
+ this.getDeclaringType() instanceof CollectionType and
+ forall(Type t | t = this.getAParamType() | t instanceof PrimitiveType)
+ }
+}
+
+private module CollectionFlowStepsInput implements UniversalFlow::UniversalFlowInput {
+ import FlowStepsInput
+
+ /**
+ * Holds if `n2` is a collection/array/constant whose value(s) are
+ * determined completely from the range of `n1` nodes.
+ */
+ predicate step(FlowNode n1, FlowNode n2) {
+ // Exclude the regular input constraints for those nodes that are covered
+ // completely by `collectionStep`.
+ FlowStepsInput::step(n1, n2) and
+ not collectionStep(_, n2)
+ or
+ // For collections with side-effects in the form of additions, we add the
+ // sources of those additions as additional input that need to originate
+ // from constants.
+ exists(Variable v |
+ nodeWithAddition(n2, v) and
+ collectionAddition(v, _, n1.asExpr())
+ )
+ or
+ // Include various forms of collection transformation.
+ collectionStep(n1, n2)
+ }
+
+ predicate isExcludedFromNullAnalysis = FlowStepsInput::isExcludedFromNullAnalysis/1;
+}
+
+private module CollectionUniversalFlow = UniversalFlow::Make;
+
+private module ConstantCollectionProp implements CollectionUniversalFlow::NullaryPropertySig {
+ /**
+ * Holds if `n` forms the base case for finding collections of constants.
+ * These are individual constants and empty collections.
+ */
+ predicate hasPropertyBase(FlowNode n) {
+ n.asExpr().isCompileTimeConstant() or
+ n.asExpr().(ConstructorCall).getConstructor() instanceof EmptyCollectionConstructor
+ }
+
+ predicate barrier = collectionWithPossibleMutation/1;
+}
+
+/**
+ * Holds if the given node is either a constant or a collection/array of
+ * constants.
+ */
+private predicate constantCollection =
+ CollectionUniversalFlow::FlowNullary::hasProperty/1;
+
+/** Gets the result of a case normalization call of `arg`. */
+private MethodCall normalizeCaseCall(Expr arg) {
+ exists(Method changecase | result.getMethod() = changecase |
+ changecase.hasName(["toUpperCase", "toLowerCase"]) and
+ changecase.getDeclaringType() instanceof TypeString and
+ arg = result.getQualifier()
+ or
+ changecase
+ .hasQualifiedName(["org.apache.commons.lang", "org.apache.commons.lang3"], "StringUtils",
+ ["lowerCase", "upperCase"]) and
+ arg = result.getArgument(0)
+ or
+ changecase
+ .hasQualifiedName("org.apache.hadoop.util", "StringUtils", ["toLowerCase", "toUpperCase"]) and
+ arg = result.getArgument(0)
+ )
+}
+
+/**
+ * Holds if the guard `g` ensures that the expression `e` is one of a set of
+ * known constants upon evaluating to `branch`.
+ */
+private predicate constantCollectionContainsCheck(Guard g, Expr e, boolean branch) {
+ exists(MethodCall mc, Method m, FlowNode n, Expr checked |
+ g = mc and
+ mc.getMethod().getSourceDeclaration().overridesOrInstantiates*(m) and
+ m.hasQualifiedName("java.util", "Collection", "contains") and
+ n.asExpr() = mc.getQualifier() and
+ constantCollection(n) and
+ checked = mc.getAnArgument() and
+ branch = true
+ |
+ checked = e or
+ checked = normalizeCaseCall(e)
+ )
+}
+
+/**
+ * A comparison against a list of compile-time constants, sanitizing taint by
+ * restricting to a set of known values.
+ */
+private class ListOfConstantsComparisonSanitizerGuard extends TaintTracking::DefaultTaintSanitizer {
+ ListOfConstantsComparisonSanitizerGuard() {
+ this = DataFlow::BarrierGuard::getABarrierNode()
+ }
+}
diff --git a/java/ql/lib/semmle/code/java/security/MaybeBrokenCryptoAlgorithmQuery.qll b/java/ql/lib/semmle/code/java/security/MaybeBrokenCryptoAlgorithmQuery.qll
index 1533b61dd5e4..060a30f87e6a 100644
--- a/java/ql/lib/semmle/code/java/security/MaybeBrokenCryptoAlgorithmQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/MaybeBrokenCryptoAlgorithmQuery.qll
@@ -30,7 +30,11 @@ class InsecureAlgoLiteral extends InsecureAlgorithm, ShortStringLiteral {
s.length() > 1 and
not s.regexpMatch(getSecureAlgorithmRegex()) and
// Exclude results covered by another query.
- not s.regexpMatch(getInsecureAlgorithmRegex())
+ not s.regexpMatch(getInsecureAlgorithmRegex()) and
+ // Exclude results covered by `InsecureAlgoProperty`.
+ // This removes duplicates when a string literal is a default value for the property,
+ // such as "MD5" in the following: `props.getProperty("hashAlg2", "MD5")`.
+ not exists(InsecureAlgoProperty insecAlgoProp | this = insecAlgoProp.getAnArgument())
)
}
diff --git a/java/ql/src/change-notes/2024-10-29-weak-crypto-hash.md b/java/ql/src/change-notes/2024-10-29-weak-crypto-hash.md
new file mode 100644
index 000000000000..b4ac88bcdc6a
--- /dev/null
+++ b/java/ql/src/change-notes/2024-10-29-weak-crypto-hash.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* The `java/weak-cryptographic-algorithm` query has been updated to no longer report uses of hash functions such as `MD5` and `SHA1` even if they are known to be weak. These hash algorithms are used very often in non-sensitive contexts, making the query too imprecise in practice. The `java/potentially-weak-cryptographic-algorithm` query has been updated to report these uses instead.
diff --git a/java/ql/src/change-notes/2024-11-22-sha3.md b/java/ql/src/change-notes/2024-11-22-sha3.md
new file mode 100644
index 000000000000..61dbc35162e1
--- /dev/null
+++ b/java/ql/src/change-notes/2024-11-22-sha3.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Added SHA3 to the list of secure hashing algorithms. As a result the `java/potentially-weak-cryptographic-algorithm` query should no longer flag up uses of SHA3.
diff --git a/java/ql/src/change-notes/2024-11-24-sha2.md b/java/ql/src/change-notes/2024-11-24-sha2.md
new file mode 100644
index 000000000000..395ea04b782e
--- /dev/null
+++ b/java/ql/src/change-notes/2024-11-24-sha2.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Added SHA-384 to the list of secure hashing algorithms. As a result the `java/potentially-weak-cryptographic-algorithm` query should no longer flag up uses of SHA-384.
diff --git a/java/ql/test/library-tests/dataflow/getter/getter.expected b/java/ql/test/library-tests/dataflow/getter/getter.expected
index b5af3f91a593..9a36107f1983 100644
--- a/java/ql/test/library-tests/dataflow/getter/getter.expected
+++ b/java/ql/test/library-tests/dataflow/getter/getter.expected
@@ -1,6 +1,5 @@
| A.java:5:12:5:15 | this | A.java:5:12:5:19 | this.foo | A.java:2:7:2:9 | foo |
| A.java:21:13:21:13 | a | A.java:21:13:21:22 | getFoo(...) | A.java:2:7:2:9 | foo |
| A.java:23:9:23:9 | a | A.java:23:9:23:19 | aGetter(...) | A.java:2:7:2:9 | foo |
-| A.java:24:9:24:10 | a2 | A.java:24:9:24:23 | notAGetter(...) | A.java:2:7:2:9 | foo |
| A.java:45:12:45:38 | maybeIdWrap(...) | A.java:45:12:45:42 | maybeIdWrap(...).foo | A.java:2:7:2:9 | foo |
| A.java:49:12:49:38 | maybeIdWrap(...) | A.java:49:12:49:42 | maybeIdWrap(...).foo | A.java:2:7:2:9 | foo |
diff --git a/java/ql/test/library-tests/dataflow/range-analysis-inline/B.java b/java/ql/test/library-tests/dataflow/range-analysis-inline/B.java
new file mode 100644
index 000000000000..01c8656deead
--- /dev/null
+++ b/java/ql/test/library-tests/dataflow/range-analysis-inline/B.java
@@ -0,0 +1,158 @@
+public class B {
+
+ // Use this method to mark non-integer bounds
+ // that should also be annotated.
+ static void bound(int b) { }
+
+ public int forLoop() {
+ int result = 0;
+ for (int i = 0;
+ i < 10; // $ bound="i in [0..10]"
+ i++) { // $ bound="i in [0..9]"
+ result = i; // $ bound="i in [0..9]"
+ }
+ return result; // $ bound="result in [0..9]"
+ }
+
+ public int forLoopExit() {
+ int result = 0;
+ for (; result < 10;) { // $ bound="result in [0..10]"
+ result += 1; // $ bound="result in [0..9]"
+ }
+ return result; // $ bound="result = 10"
+ }
+
+ public int forLoopExitStep() {
+ int result = 0;
+ for (; result < 10;) { // $ bound="result in [0..12]"
+ result += 3; // $ bound="result in [0..9]"
+ }
+ return result; // $ bound="result = 12"
+ }
+
+ public int forLoopExitUpd() {
+ int result = 0;
+ for (; result < 10; // $ bound="result in [0..10]"
+ result++) { // $ bound="result in [0..9]"
+ }
+ return result; // $ bound="result = 10"
+ }
+
+ public int forLoopExitNested() {
+ int result = 0;
+ for (; result < 10;) {
+ int i = 0;
+ for (; i < 3;) { // $ bound="i in [0..3]"
+ i += 1; // $ bound="i in [0..2]"
+ }
+ result += i; // $ bound="result in [0..9]" bound="i = 3"
+ }
+ return result; // $ MISSING:bound="result = 12"
+ }
+
+ public int emptyForLoop() {
+ int result = 0;
+ for (int i = 0; i < 0; // $ bound="i = 0"
+ i++) { // $ bound="i in [0..-1]"
+ result = i; // $ bound="i in [0..-1]"
+ }
+ return result; // $ bound="result = 0"
+ }
+
+ public int noLoop() {
+ int result = 0;
+ result += 1; // $ bound="result = 0"
+ return result; // $ bound="result = 1"
+ }
+
+ public int foreachLoop() {
+ int result = 0;
+ for (int i : new int[] {1, 2, 3, 4, 5}) {
+ result = i;
+ }
+ return result;
+ }
+
+ public int emptyForeachLoop() {
+ int result = 0;
+ for (int i : new int[] {}) {
+ result = i;
+ }
+ return result;
+ }
+
+ public int whileLoop() {
+ int result = 100;
+ while (result > 5) { // $ bound="result in [4..100]"
+ result = result - 2; // $ bound="result in [6..100]"
+ }
+ return result; // $ bound="result = 4"
+ }
+
+ public int oddWhileLoop() {
+ int result = 101;
+ while (result > 5) { // $ bound="result in [5..101]"
+ result = result - 2; // $ bound="result in [7..101]"
+ }
+ return result; // $ bound="result = 5"
+ }
+
+ static void arrayLength(int[] arr) {
+ bound(arr.length);
+ for (int i = 0;
+ i < arr.length;
+ i++) { // $ bound="i <= arr.length - 1"
+ arr[i]++; // $ bound="i <= arr.length - 1"
+ }
+ }
+
+ static int varBound(int b) {
+ bound(b);
+ int result = 0;
+ for (int i = 0;
+ i < b;
+ i++) { // $ bound="i <= b - 1"
+ result = i; // $ bound="i <= b - 1"
+ }
+ return result; // We cannot conclude anything here, since we do not know that b > 0
+ }
+
+ static int varBoundPositiveGuard(int b) {
+ bound(b);
+ if (b > 0) {
+ int result = 0;
+ for (int i = 0;
+ i < b;
+ i++) { // $ bound="i <= b - 1"
+ result = i; // $ bound="i <= b - 1"
+ }
+ return result; // $ MISSING: bound="result <= b - 1"
+ } else {
+ return 0;
+ }
+ }
+
+ static int varBoundPositiveGuardEarlyReturn(int b) {
+ bound(b);
+ if (b <= 0) return 0;
+ int result = 0;
+ for (int i = 0;
+ i < b;
+ i++) { // $ bound="i <= b - 1"
+ result = i; // $ bound="i <= b - 1"
+ }
+ return result; // $ MISSING: bound="result <= b - 1"
+ }
+
+ static int varBoundPositiveAssert(int b) {
+ bound(b);
+ assert b > 0;
+ int result = 0;
+ for (int i = 0;
+ i < b;
+ i++) { // $ bound="i <= b - 1"
+ result = i; // $ bound="i <= b - 1"
+ }
+ return result; // $ MISSING: bound="result <= b - 1"
+ }
+}
\ No newline at end of file
diff --git a/rust/ql/test/extractor-tests/generated/FormatArgsArg/FormatArgsArg.expected b/java/ql/test/library-tests/dataflow/range-analysis-inline/range.expected
similarity index 100%
rename from rust/ql/test/extractor-tests/generated/FormatArgsArg/FormatArgsArg.expected
rename to java/ql/test/library-tests/dataflow/range-analysis-inline/range.expected
diff --git a/java/ql/test/library-tests/dataflow/range-analysis-inline/range.ql b/java/ql/test/library-tests/dataflow/range-analysis-inline/range.ql
new file mode 100644
index 000000000000..b0fcc1710d44
--- /dev/null
+++ b/java/ql/test/library-tests/dataflow/range-analysis-inline/range.ql
@@ -0,0 +1,71 @@
+/**
+ * Inline range analysis tests for Java.
+ * See `shared/util/codeql/dataflow/test/InlineFlowTest.qll`
+ */
+
+import java
+import semmle.code.java.dataflow.RangeAnalysis
+private import TestUtilities.InlineExpectationsTest as IET
+
+module RangeTest implements IET::TestSig {
+ string getARelevantTag() { result = "bound" }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "bound" and
+ (
+ // simple integer bounds (`ZeroBound`s)
+ exists(Expr e, int lower, int upper |
+ constrained(e, lower, upper) and
+ e instanceof VarRead and
+ e.getCompilationUnit().fromSource()
+ |
+ location = e.getLocation() and
+ element = e.toString() and
+ if lower = upper
+ then value = "\"" + e.toString() + " = " + lower.toString() + "\""
+ else
+ value = "\"" + e.toString() + " in [" + lower.toString() + ".." + upper.toString() + "]\""
+ )
+ or
+ // advanced bounds
+ exists(Expr e, int delta, string deltaStr, boolean upper, string cmp, Expr boundExpr |
+ annotatedBound(e, _, boundExpr, delta, upper) and
+ e instanceof VarRead and
+ e.getCompilationUnit().fromSource() and
+ (
+ if delta = 0
+ then deltaStr = ""
+ else
+ if delta > 0
+ then deltaStr = " + " + delta.toString()
+ else deltaStr = " - " + delta.abs().toString()
+ ) and
+ if upper = true then cmp = "<=" else cmp = ">="
+ |
+ location = e.getLocation() and
+ element = e.toString() and
+ value = "\"" + e.toString() + " " + cmp + " " + boundExpr.toString() + deltaStr + "\""
+ )
+ )
+ }
+
+ private predicate constrained(Expr e, int lower, int upper) {
+ bounded(e, any(ZeroBound z), lower, false, _) and
+ bounded(e, any(ZeroBound z), upper, true, _)
+ }
+
+ private predicate annotatedBound(Expr e, Bound b, Expr boundExpr, int delta, boolean upper) {
+ bounded(e, b, delta, upper, _) and
+ // the expression for the bound is explicitly requested as being annotated
+ // via a call such as
+ // ```java
+ // bound(expr);
+ // ```
+ boundExpr = b.getExpr() and
+ exists(Call c | c.getCallee().getName() = "bound" and c.getArgument(0) = boundExpr) and
+ // non-trivial bound
+ not e = b.getExpr()
+ }
+}
+
+import IET::MakeTest
diff --git a/java/ql/test/library-tests/listofconstants/A.java b/java/ql/test/library-tests/listofconstants/A.java
new file mode 100644
index 000000000000..74129692d71d
--- /dev/null
+++ b/java/ql/test/library-tests/listofconstants/A.java
@@ -0,0 +1,35 @@
+import java.util.*;
+
+public class A {
+ private static final Set SEPARATORS =
+ Collections.unmodifiableSet(
+ new HashSet<>(Arrays.asList("\t", "\n", ";")));
+
+ public static void sink(String s) { }
+
+ private void checkSeparator(String separator) {
+ if (SEPARATORS.contains(separator)) {
+ sink(separator);
+ }
+ }
+
+ public static final String URI1 = "yarn.io/gpu";
+ public static final String URI2 = "yarn.io/fpga";
+
+ public static final Set SCHEMAS = Set.of(URI1, URI2, "s3a", "wasb");
+
+ private void checkSchema(String schema) {
+ if (SCHEMAS.contains(schema)) {
+ sink(schema);
+ }
+ }
+
+ private void testAdd(String inp) {
+ Set s = new HashSet<>();
+ s.add("AA");
+ s.add("BB");
+ if (s.contains(inp.toUpperCase())) {
+ sink(inp);
+ }
+ }
+}
diff --git a/java/ql/test/library-tests/listofconstants/CONSISTENCY/typeParametersInScope.expected b/java/ql/test/library-tests/listofconstants/CONSISTENCY/typeParametersInScope.expected
new file mode 100644
index 000000000000..1ea902154a6e
--- /dev/null
+++ b/java/ql/test/library-tests/listofconstants/CONSISTENCY/typeParametersInScope.expected
@@ -0,0 +1 @@
+| Type A uses out-of-scope type variable E. Note the Java extractor is known to sometimes do this; the Kotlin extractor should not. |
diff --git a/java/ql/test/library-tests/listofconstants/test.expected b/java/ql/test/library-tests/listofconstants/test.expected
new file mode 100644
index 000000000000..203d64cd46dd
--- /dev/null
+++ b/java/ql/test/library-tests/listofconstants/test.expected
@@ -0,0 +1,3 @@
+| A.java:12:12:12:20 | separator |
+| A.java:23:12:23:17 | schema |
+| A.java:32:12:32:14 | inp |
diff --git a/java/ql/test/library-tests/listofconstants/test.ql b/java/ql/test/library-tests/listofconstants/test.ql
new file mode 100644
index 000000000000..f56dfc0f11f4
--- /dev/null
+++ b/java/ql/test/library-tests/listofconstants/test.ql
@@ -0,0 +1,5 @@
+import java
+import semmle.code.java.dataflow.FlowSteps
+
+from DefaultTaintSanitizer e
+select e
diff --git a/java/ql/test/query-tests/security/CWE-089/semmle/examples/AllowListSanitizerWithJavaUtilList.java b/java/ql/test/query-tests/security/CWE-089/semmle/examples/AllowListSanitizerWithJavaUtilList.java
new file mode 100644
index 000000000000..285f9bc49cb2
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-089/semmle/examples/AllowListSanitizerWithJavaUtilList.java
@@ -0,0 +1,306 @@
+// Test cases for CWE-089 (SQL injection and Java Persistence query injection)
+// http://cwe.mitre.org/data/definitions/89.html
+package test.cwe089.semmle.tests;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+
+class AllowListSanitizerWithJavaUtilList {
+ public static Connection connection;
+ public static final List goodAllowList1 = List.of("allowed1", "allowed2", "allowed3");
+ public static final List goodAllowList2 = Collections.unmodifiableList(Arrays.asList("allowed1"));
+ public static final List goodAllowList3;
+ public static final List goodAllowList4;
+ public static final List goodAllowList5;
+ public static final List badAllowList1 = List.of("allowed1", "allowed2", getNonConstantString());
+ public static final List badAllowList2 = Collections.unmodifiableList(Arrays.asList("allowed1", getNonConstantString()));
+ public static final List badAllowList3;
+ public static final List badAllowList4;
+ public static List badAllowList6 = List.of("allowed1", "allowed2", "allowed3");
+ public final List goodAllowList7 = List.of("allowed1", "allowed2", "allowed3");
+
+ static {
+ goodAllowList3 = List.of("allowed1", "allowed2", "allowed3");
+ goodAllowList4 = Collections.unmodifiableList(Arrays.asList("allowed1", "allowed2"));
+ badAllowList3 = List.of(getNonConstantString(), "allowed2", "allowed3");
+ badAllowList4 = Collections.unmodifiableList(Arrays.asList("allowed1", getNonConstantString()));
+ goodAllowList5 = new ArrayList();
+ goodAllowList5.add("allowed1");
+ goodAllowList5.add("allowed2");
+ goodAllowList5.add("allowed3");
+ }
+
+ public static String getNonConstantString() {
+ return String.valueOf(System.currentTimeMillis());
+ }
+
+ public static void main(String[] args) throws IOException, SQLException {
+ badAllowList6 = List.of("allowed1", getNonConstantString(), "allowed3");
+ testStaticFields(args);
+ testLocal(args);
+ var x = new AllowListSanitizerWithJavaUtilList();
+ x.testNonStaticFields(args);
+ testMultipleSources(args);
+ testEscape(args);
+ }
+
+ private static void testStaticFields(String[] args) throws IOException, SQLException {
+ String tainted = args[1];
+ // GOOD: an allowlist is used with constant strings
+ if(goodAllowList1.contains(tainted.toLowerCase())){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ // GOOD: an allowlist is used with constant strings
+ if(goodAllowList2.contains(tainted.toUpperCase())){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ // GOOD: an allowlist is used with constant strings
+ if(goodAllowList3.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ // GOOD: an allowlist is used with constant strings
+ if(goodAllowList4.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ // BAD: an allowlist is used with constant strings
+ if(badAllowList1.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ // BAD: an allowlist is used with constant strings
+ if(badAllowList2.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ // BAD: an allowlist is used with constant strings
+ if(badAllowList3.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ // BAD: an allowlist is used with constant strings
+ if(badAllowList4.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ // GOOD: an allowlist is used with constant strings
+ if(goodAllowList5.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ // BAD: the allowlist is in a non-final field
+ if(badAllowList6.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+
+ private void testNonStaticFields(String[] args) throws IOException, SQLException {
+ String tainted = args[0];
+ // GOOD: the allowlist is in a non-static field
+ if(goodAllowList7.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+
+ private static void testLocal(String[] args) throws IOException, SQLException {
+ String tainted = args[1];
+ // GOOD: an allowlist is used with constant strings
+ {
+ List allowlist = List.of("allowed1", "allowed2", "allowed3");
+ if(allowlist.contains(tainted.toLowerCase())){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // BAD: an allowlist is used but one of the entries is not a compile-time constant
+ {
+ List allowlist = List.of("allowed1", "allowed2", args[2]);
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // GOOD: an allowlist is used with constant strings
+ {
+ String[] allowedArray = {"allowed1", "allowed2", "allowed3"};
+ List allowlist = List.of(allowedArray);
+ if(allowlist.contains(tainted.toUpperCase())){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // BAD: an allowlist is used but one of the entries is not a compile-time constant
+ {
+ String[] allowedArray = {"allowed1", "allowed2", args[2]};
+ List allowlist = List.of(allowedArray);
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // GOOD: an allowlist is used with constant strings
+ {
+ List allowlist = Collections.unmodifiableList(Arrays.asList("allowed1"));
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // BAD: an allowlist is used but one of the entries is not a compile-time constant
+ {
+ List allowlist = Collections.unmodifiableList(Arrays.asList("allowed1", "allowed2", args[2]));
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // GOOD: an allowlist is used with constant strings
+ {
+ String[] allowedArray = {"allowed1", "allowed2", "allowed3"};
+ List allowlist = Collections.unmodifiableList(Arrays.asList(allowedArray));
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // BAD: an allowlist is used but one of the entries is not a compile-time constant
+ {
+ String[] allowedArray = {"allowed1", "allowed2", args[2]};
+ List allowlist = Collections.unmodifiableList(Arrays.asList(allowedArray));
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // GOOD: an allowlist is used with constant string
+ {
+ List allowlist = new ArrayList();
+ allowlist.add("allowed1");
+ allowlist.add("allowed2");
+ allowlist.add("allowed3");
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // BAD: an allowlist is used but one of the entries is not a compile-time constant
+ {
+ List allowlist = new ArrayList();
+ allowlist.add("allowed1");
+ allowlist.add(getNonConstantString());
+ allowlist.add("allowed3");
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // BAD: an allowlist is used but it contains a non-compile-time constant element
+ {
+ List allowlist = new ArrayList();
+ allowlist.add("allowed1");
+ addNonConstantStringDirectly(allowlist);
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ }
+
+ private static void testMultipleSources(String[] args) throws IOException, SQLException {
+ String tainted = args[1];
+ boolean b = args[2] == "True";
+ {
+ // BAD: an allowlist is used which might contain constant strings
+ List allowlist = new ArrayList();
+ allowlist.add("allowed1");
+ if (b) {
+ allowlist.add(getNonConstantString());
+ }
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ {
+ // BAD: an allowlist is used which might contain constant strings
+ List allowlist = b ? goodAllowList1 : badAllowList1;
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ {
+ // BAD: an allowlist is used which might contain constant strings
+ List allowlist = b ? goodAllowList1 : List.of("allowed1", "allowed2", args[2]);;
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ }
+
+ private static void testEscape(String[] args) throws IOException, SQLException {
+ String tainted = args[1];
+ boolean b = args[2] == "True";
+ {
+ // BAD: an allowlist is used which contains constant strings
+ List allowlist = new ArrayList();
+ addNonConstantStringViaLambda(e -> allowlist.add(e));
+ if(allowlist.contains(tainted)){ // missing result
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ }
+
+ private static void addNonConstantStringDirectly(List list) {
+ list.add(getNonConstantString());
+ }
+
+ private static void addNonConstantStringViaLambda(Consumer adder) {
+ adder.accept(getNonConstantString());
+ }
+
+}
diff --git a/java/ql/test/query-tests/security/CWE-089/semmle/examples/AllowListSanitizerWithJavaUtilSet.java b/java/ql/test/query-tests/security/CWE-089/semmle/examples/AllowListSanitizerWithJavaUtilSet.java
new file mode 100644
index 000000000000..e1a5f889c6fa
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-089/semmle/examples/AllowListSanitizerWithJavaUtilSet.java
@@ -0,0 +1,305 @@
+// Test cases for CWE-089 (SQL injection and Java Persistence query injection)
+// http://cwe.mitre.org/data/definitions/89.html
+package test.cwe089.semmle.tests;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+import java.util.function.Consumer;
+
+class AllowListSanitizerWithJavaUtilSet {
+ public static Connection connection;
+ public static final Set goodAllowList1 = Set.of("allowed1", "allowed2", "allowed3");
+ public static final Set goodAllowList2 = Collections.unmodifiableSet(new HashSet(Arrays.asList("allowed1","allowed2")));
+ public static final Set goodAllowList3;
+ public static final Set goodAllowList4;
+ public static final Set goodAllowList5;
+ public static final Set badAllowList1 = Set.of("allowed1", "allowed2", getNonConstantString());
+ public static final Set badAllowList2 = Collections.unmodifiableSet(new HashSet(Arrays.asList("allowed1", getNonConstantString())));
+ public static final Set badAllowList3;
+ public static final Set badAllowList4;
+ public static Set badAllowList6 = Set.of("allowed1", "allowed2", "allowed3");
+ public final Set goodAllowList7 = Set.of("allowed1", "allowed2", "allowed3");
+
+ static {
+ goodAllowList3 = Set.of("allowed1", "allowed2", "allowed3");
+ goodAllowList4 = Collections.unmodifiableSet(new HashSet(Arrays.asList("allowed1", "allowed2")));
+ badAllowList3 = Set.of(getNonConstantString(), "allowed2", "allowed3");
+ badAllowList4 = Collections.unmodifiableSet(new HashSet(Arrays.asList("allowed1", getNonConstantString())));
+ goodAllowList5 = new HashSet();
+ goodAllowList5.add("allowed1");
+ goodAllowList5.add("allowed2");
+ goodAllowList5.add("allowed3");
+ }
+
+ public static String getNonConstantString() {
+ return String.valueOf(System.currentTimeMillis());
+ }
+
+ public static void main(String[] args) throws IOException, SQLException {
+ badAllowList6 = Set.of("allowed1", getNonConstantString(), "allowed3");
+ testStaticFields(args);
+ testLocal(args);
+ var x = new AllowListSanitizerWithJavaUtilSet();
+ x.testNonStaticFields(args);
+ testMultipleSources(args);
+ testEscape(args);
+ }
+
+ private static void testStaticFields(String[] args) throws IOException, SQLException {
+ String tainted = args[1];
+ // GOOD: an allowlist is used with constant strings
+ if(goodAllowList1.contains(tainted.toLowerCase())){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ // GOOD: an allowlist is used with constant strings
+ if(goodAllowList2.contains(tainted.toUpperCase())){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ // GOOD: an allowlist is used with constant strings
+ if(goodAllowList3.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ // GOOD: an allowlist is used with constant strings
+ if(goodAllowList4.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ // BAD: an allowlist is used with constant strings
+ if(badAllowList1.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ // BAD: an allowlist is used with constant strings
+ if(badAllowList2.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ // BAD: an allowlist is used with constant strings
+ if(badAllowList3.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ // BAD: an allowlist is used with constant strings
+ if(badAllowList4.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ // GOOD: an allowlist is used with constant strings
+ if(goodAllowList5.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ // BAD: the allowlist is in a non-final field
+ if(badAllowList6.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+
+ private void testNonStaticFields(String[] args) throws IOException, SQLException {
+ String tainted = args[1];
+ // GOOD: the allowlist is in a non-static field
+ if(goodAllowList7.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+
+ private static void testLocal(String[] args) throws IOException, SQLException {
+ String tainted = args[1];
+ // GOOD: an allowlist is used with constant strings
+ {
+ Set allowlist = Set.of("allowed1", "allowed2", "allowed3");
+ if(allowlist.contains(tainted.toLowerCase())){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // BAD: an allowlist is used but one of the entries is not a compile-time constant
+ {
+ Set allowlist = Set.of("allowed1", "allowed2", args[2]);
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // GOOD: an allowlist is used with constant strings
+ {
+ String[] allowedArray = {"allowed1", "allowed2", "allowed3"};
+ Set allowlist = Set.of(allowedArray);
+ if(allowlist.contains(tainted.toUpperCase())){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // BAD: an allowlist is used but one of the entries is not a compile-time constant
+ {
+ String[] allowedArray = {"allowed1", "allowed2", args[2]};
+ Set allowlist = Set.of(allowedArray);
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // GOOD: an allowlist is used with constant strings
+ {
+ Set allowlist = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("allowed1")));
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // BAD: an allowlist is used but one of the entries is not a compile-time constant
+ {
+ Set allowlist = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("allowed1", "allowed2", args[2])));
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // GOOD: an allowlist is used with constant strings
+ {
+ String[] allowedArray = {"allowed1", "allowed2", "allowed3"};
+ Set allowlist = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(allowedArray)));
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // BAD: an allowlist is used but one of the entries is not a compile-time constant
+ {
+ String[] allowedArray = {"allowed1", "allowed2", args[2]};
+ Set allowlist = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(allowedArray)));
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // GOOD: an allowlist is used with constant string
+ {
+ Set allowlist = new HashSet();
+ allowlist.add("allowed1");
+ allowlist.add("allowed2");
+ allowlist.add("allowed3");
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // BAD: an allowlist is used but one of the entries is not a compile-time constant
+ {
+ Set allowlist = new HashSet();
+ allowlist.add("allowed1");
+ allowlist.add(getNonConstantString());
+ allowlist.add("allowed3");
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ // BAD: an allowlist is used but it contains a non-compile-time constant element
+ {
+ Set allowlist = new HashSet();
+ allowlist.add("allowed1");
+ addNonConstantStringDirectly(allowlist);
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ }
+
+ private static void testMultipleSources(String[] args) throws IOException, SQLException {
+ String tainted = args[1];
+ boolean b = args[2] == "True";
+ {
+ // BAD: an allowlist is used which might contain constant strings
+ Set allowlist = new HashSet();
+ allowlist.add("allowed1");
+ if (b) {
+ allowlist.add(getNonConstantString());
+ }
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ {
+ // BAD: an allowlist is used which might contain constant strings
+ Set allowlist = b ? goodAllowList1 : badAllowList1;
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ {
+ // BAD: an allowlist is used which might contain constant strings
+ Set allowlist = b ? goodAllowList1 : Set.of("allowed1", "allowed2", args[2]);;
+ if(allowlist.contains(tainted)){
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ }
+
+ private static void testEscape(String[] args) throws IOException, SQLException {
+ String tainted = args[1];
+ boolean b = args[2] == "True";
+ {
+ // BAD: an allowlist is used which contains constant strings
+ Set allowlist = new HashSet();
+ addNonConstantStringViaLambda(e -> allowlist.add(e));
+ if(allowlist.contains(tainted)){ // missing result
+ String query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ + tainted + "' ORDER BY PRICE";
+ ResultSet results = connection.createStatement().executeQuery(query);
+ }
+ }
+ }
+
+ private static void addNonConstantStringDirectly(Set set) {
+ set.add(getNonConstantString());
+ }
+
+ private static void addNonConstantStringViaLambda(Consumer adder) {
+ adder.accept(getNonConstantString());
+ }
+
+}
diff --git a/java/ql/test/query-tests/security/CWE-089/semmle/examples/CONSISTENCY/typeParametersInScope.expected b/java/ql/test/query-tests/security/CWE-089/semmle/examples/CONSISTENCY/typeParametersInScope.expected
new file mode 100644
index 000000000000..1f8028eff60e
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-089/semmle/examples/CONSISTENCY/typeParametersInScope.expected
@@ -0,0 +1 @@
+| Type AllowListSanitizerWithJavaUtilSet uses out-of-scope type variable E. Note the Java extractor is known to sometimes do this; the Kotlin extractor should not. |
diff --git a/java/ql/test/query-tests/security/CWE-089/semmle/examples/SqlConcatenated.expected b/java/ql/test/query-tests/security/CWE-089/semmle/examples/SqlConcatenated.expected
index fc1d87f06b17..1e560f03c3b9 100644
--- a/java/ql/test/query-tests/security/CWE-089/semmle/examples/SqlConcatenated.expected
+++ b/java/ql/test/query-tests/security/CWE-089/semmle/examples/SqlConcatenated.expected
@@ -1,3 +1,55 @@
+| AllowListSanitizerWithJavaUtilList.java:64:66:64:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:63:8:63:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:70:66:70:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:69:8:69:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:76:66:76:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:75:8:75:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:82:66:82:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:81:8:81:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:88:66:88:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:87:8:87:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:94:66:94:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:93:8:93:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:100:66:100:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:99:8:99:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:106:66:106:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:105:8:105:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:112:66:112:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:111:8:111:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:118:66:118:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:117:8:117:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:128:66:128:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:127:8:127:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:140:67:140:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:139:9:139:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:149:67:149:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:148:9:148:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:159:67:159:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:158:9:158:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:169:67:169:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:168:9:168:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:178:67:178:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:177:9:177:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:187:67:187:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:186:9:186:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:197:67:197:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:196:9:196:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:207:67:207:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:206:9:206:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:219:67:219:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:218:9:218:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:231:67:231:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:230:9:230:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:242:67:242:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:241:9:241:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:260:67:260:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:259:9:259:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:269:67:269:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:268:9:268:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:278:67:278:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:277:9:277:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilList.java:293:67:293:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilList.java:292:9:292:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:63:66:63:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:62:8:62:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:69:66:69:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:68:8:68:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:75:66:75:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:74:8:74:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:81:66:81:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:80:8:80:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:87:66:87:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:86:8:86:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:93:66:93:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:92:8:92:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:99:66:99:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:98:8:98:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:105:66:105:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:104:8:104:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:111:66:111:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:110:8:110:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:117:66:117:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:116:8:116:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:127:66:127:70 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:126:8:126:14 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:139:67:139:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:138:9:138:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:148:67:148:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:147:9:147:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:158:67:158:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:157:9:157:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:168:67:168:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:167:9:167:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:177:67:177:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:176:9:176:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:186:67:186:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:185:9:185:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:196:67:196:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:195:9:195:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:206:67:206:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:205:9:205:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:218:67:218:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:217:9:217:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:230:67:230:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:229:9:229:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:241:67:241:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:240:9:240:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:259:67:259:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:258:9:258:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:268:67:268:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:267:9:267:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:277:67:277:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:276:9:276:15 | tainted | this expression |
+| AllowListSanitizerWithJavaUtilSet.java:292:67:292:71 | query | Query built by concatenation with $@, which may be untrusted. | AllowListSanitizerWithJavaUtilSet.java:291:9:291:15 | tainted | this expression |
| Test.java:36:47:36:52 | query1 | Query built by concatenation with $@, which may be untrusted. | Test.java:35:8:35:15 | category | this expression |
| Test.java:42:57:42:62 | query2 | Query built by concatenation with $@, which may be untrusted. | Test.java:41:51:41:52 | id | this expression |
| Test.java:50:62:50:67 | query3 | Query built by concatenation with $@, which may be untrusted. | Test.java:49:8:49:15 | category | this expression |
diff --git a/java/ql/test/query-tests/security/CWE-089/semmle/examples/SqlTainted.expected b/java/ql/test/query-tests/security/CWE-089/semmle/examples/SqlTainted.expected
index d54bbdaec05d..a45f58bd54d6 100644
--- a/java/ql/test/query-tests/security/CWE-089/semmle/examples/SqlTainted.expected
+++ b/java/ql/test/query-tests/security/CWE-089/semmle/examples/SqlTainted.expected
@@ -1,4 +1,34 @@
#select
+| AllowListSanitizerWithJavaUtilList.java:88:66:88:70 | query | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:88:66:88:70 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilList.java:94:66:94:70 | query | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:94:66:94:70 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilList.java:100:66:100:70 | query | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:100:66:100:70 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilList.java:106:66:106:70 | query | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:106:66:106:70 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilList.java:118:66:118:70 | query | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:118:66:118:70 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilList.java:149:67:149:71 | query | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:149:67:149:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilList.java:169:67:169:71 | query | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:169:67:169:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilList.java:187:67:187:71 | query | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:187:67:187:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilList.java:207:67:207:71 | query | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:207:67:207:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilList.java:231:67:231:71 | query | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:231:67:231:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilList.java:242:67:242:71 | query | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:242:67:242:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilList.java:260:67:260:71 | query | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:260:67:260:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilList.java:269:67:269:71 | query | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:269:67:269:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilList.java:278:67:278:71 | query | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:278:67:278:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilList.java:293:67:293:71 | query | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:293:67:293:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilSet.java:87:66:87:70 | query | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:87:66:87:70 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilSet.java:93:66:93:70 | query | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:93:66:93:70 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilSet.java:99:66:99:70 | query | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:99:66:99:70 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilSet.java:105:66:105:70 | query | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:105:66:105:70 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilSet.java:117:66:117:70 | query | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:117:66:117:70 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilSet.java:148:67:148:71 | query | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:148:67:148:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilSet.java:168:67:168:71 | query | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:168:67:168:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilSet.java:186:67:186:71 | query | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:186:67:186:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilSet.java:206:67:206:71 | query | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:206:67:206:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilSet.java:230:67:230:71 | query | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:230:67:230:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilSet.java:241:67:241:71 | query | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:241:67:241:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilSet.java:259:67:259:71 | query | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:259:67:259:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilSet.java:268:67:268:71 | query | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:268:67:268:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilSet.java:277:67:277:71 | query | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:277:67:277:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args | user-provided value |
+| AllowListSanitizerWithJavaUtilSet.java:292:67:292:71 | query | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:292:67:292:71 | query | This query depends on a $@. | AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args | user-provided value |
| Mongo.java:17:45:17:67 | parse(...) | Mongo.java:10:29:10:41 | args : String[] | Mongo.java:17:45:17:67 | parse(...) | This query depends on a $@. | Mongo.java:10:29:10:41 | args | user-provided value |
| Mongo.java:21:49:21:52 | json | Mongo.java:10:29:10:41 | args : String[] | Mongo.java:21:49:21:52 | json | This query depends on a $@. | Mongo.java:10:29:10:41 | args | user-provided value |
| Test.java:36:47:36:52 | query1 | Test.java:227:26:227:38 | args : String[] | Test.java:36:47:36:52 | query1 | This query depends on a $@. | Test.java:227:26:227:38 | args | user-provided value |
@@ -10,6 +40,52 @@
| Test.java:209:47:209:68 | queryWithUserTableName | Test.java:227:26:227:38 | args : String[] | Test.java:209:47:209:68 | queryWithUserTableName | This query depends on a $@. | Test.java:227:26:227:38 | args | user-provided value |
| Test.java:221:81:221:111 | ... + ... | Test.java:227:26:227:38 | args : String[] | Test.java:221:81:221:111 | ... + ... | This query depends on a $@. | Test.java:227:26:227:38 | args | user-provided value |
edges
+| AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:50:20:50:23 | args : String[] | provenance | |
+| AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:51:13:51:16 | args : String[] | provenance | |
+| AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:54:23:54:26 | args : String[] | provenance | |
+| AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | AllowListSanitizerWithJavaUtilList.java:55:14:55:17 | args : String[] | provenance | |
+| AllowListSanitizerWithJavaUtilList.java:50:20:50:23 | args : String[] | AllowListSanitizerWithJavaUtilList.java:58:39:58:51 | args : String[] | provenance | |
+| AllowListSanitizerWithJavaUtilList.java:51:13:51:16 | args : String[] | AllowListSanitizerWithJavaUtilList.java:132:32:132:44 | args : String[] | provenance | |
+| AllowListSanitizerWithJavaUtilList.java:54:23:54:26 | args : String[] | AllowListSanitizerWithJavaUtilList.java:247:42:247:54 | args : String[] | provenance | |
+| AllowListSanitizerWithJavaUtilList.java:55:14:55:17 | args : String[] | AllowListSanitizerWithJavaUtilList.java:283:33:283:45 | args : String[] | provenance | |
+| AllowListSanitizerWithJavaUtilList.java:58:39:58:51 | args : String[] | AllowListSanitizerWithJavaUtilList.java:88:66:88:70 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilList.java:58:39:58:51 | args : String[] | AllowListSanitizerWithJavaUtilList.java:94:66:94:70 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilList.java:58:39:58:51 | args : String[] | AllowListSanitizerWithJavaUtilList.java:100:66:100:70 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilList.java:58:39:58:51 | args : String[] | AllowListSanitizerWithJavaUtilList.java:106:66:106:70 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilList.java:58:39:58:51 | args : String[] | AllowListSanitizerWithJavaUtilList.java:118:66:118:70 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilList.java:132:32:132:44 | args : String[] | AllowListSanitizerWithJavaUtilList.java:149:67:149:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilList.java:132:32:132:44 | args : String[] | AllowListSanitizerWithJavaUtilList.java:169:67:169:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilList.java:132:32:132:44 | args : String[] | AllowListSanitizerWithJavaUtilList.java:187:67:187:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilList.java:132:32:132:44 | args : String[] | AllowListSanitizerWithJavaUtilList.java:207:67:207:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilList.java:132:32:132:44 | args : String[] | AllowListSanitizerWithJavaUtilList.java:231:67:231:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilList.java:132:32:132:44 | args : String[] | AllowListSanitizerWithJavaUtilList.java:242:67:242:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilList.java:247:42:247:54 | args : String[] | AllowListSanitizerWithJavaUtilList.java:260:67:260:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilList.java:247:42:247:54 | args : String[] | AllowListSanitizerWithJavaUtilList.java:269:67:269:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilList.java:247:42:247:54 | args : String[] | AllowListSanitizerWithJavaUtilList.java:278:67:278:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilList.java:283:33:283:45 | args : String[] | AllowListSanitizerWithJavaUtilList.java:293:67:293:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:49:20:49:23 | args : String[] | provenance | |
+| AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:50:13:50:16 | args : String[] | provenance | |
+| AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:53:23:53:26 | args : String[] | provenance | |
+| AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:54:14:54:17 | args : String[] | provenance | |
+| AllowListSanitizerWithJavaUtilSet.java:49:20:49:23 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:57:39:57:51 | args : String[] | provenance | |
+| AllowListSanitizerWithJavaUtilSet.java:50:13:50:16 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:131:32:131:44 | args : String[] | provenance | |
+| AllowListSanitizerWithJavaUtilSet.java:53:23:53:26 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:246:42:246:54 | args : String[] | provenance | |
+| AllowListSanitizerWithJavaUtilSet.java:54:14:54:17 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:282:33:282:45 | args : String[] | provenance | |
+| AllowListSanitizerWithJavaUtilSet.java:57:39:57:51 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:87:66:87:70 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilSet.java:57:39:57:51 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:93:66:93:70 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilSet.java:57:39:57:51 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:99:66:99:70 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilSet.java:57:39:57:51 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:105:66:105:70 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilSet.java:57:39:57:51 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:117:66:117:70 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilSet.java:131:32:131:44 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:148:67:148:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilSet.java:131:32:131:44 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:168:67:168:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilSet.java:131:32:131:44 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:186:67:186:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilSet.java:131:32:131:44 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:206:67:206:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilSet.java:131:32:131:44 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:230:67:230:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilSet.java:131:32:131:44 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:241:67:241:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilSet.java:246:42:246:54 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:259:67:259:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilSet.java:246:42:246:54 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:268:67:268:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilSet.java:246:42:246:54 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:277:67:277:71 | query | provenance | Sink:MaD:4 |
+| AllowListSanitizerWithJavaUtilSet.java:282:33:282:45 | args : String[] | AllowListSanitizerWithJavaUtilSet.java:292:67:292:71 | query | provenance | Sink:MaD:4 |
| Mongo.java:10:29:10:41 | args : String[] | Mongo.java:17:56:17:66 | stringQuery : String | provenance | |
| Mongo.java:10:29:10:41 | args : String[] | Mongo.java:21:49:21:52 | json | provenance | |
| Mongo.java:17:56:17:66 | stringQuery : String | Mongo.java:17:45:17:67 | parse(...) | provenance | Config |
@@ -40,6 +116,54 @@ models
| 6 | Summary: java.lang; AbstractStringBuilder; true; append; ; ; Argument[0]; Argument[this]; taint; manual |
| 7 | Summary: java.lang; CharSequence; true; toString; ; ; Argument[this]; ReturnValue; taint; manual |
nodes
+| AllowListSanitizerWithJavaUtilList.java:48:26:48:38 | args : String[] | semmle.label | args : String[] |
+| AllowListSanitizerWithJavaUtilList.java:50:20:50:23 | args : String[] | semmle.label | args : String[] |
+| AllowListSanitizerWithJavaUtilList.java:51:13:51:16 | args : String[] | semmle.label | args : String[] |
+| AllowListSanitizerWithJavaUtilList.java:54:23:54:26 | args : String[] | semmle.label | args : String[] |
+| AllowListSanitizerWithJavaUtilList.java:55:14:55:17 | args : String[] | semmle.label | args : String[] |
+| AllowListSanitizerWithJavaUtilList.java:58:39:58:51 | args : String[] | semmle.label | args : String[] |
+| AllowListSanitizerWithJavaUtilList.java:88:66:88:70 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilList.java:94:66:94:70 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilList.java:100:66:100:70 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilList.java:106:66:106:70 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilList.java:118:66:118:70 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilList.java:132:32:132:44 | args : String[] | semmle.label | args : String[] |
+| AllowListSanitizerWithJavaUtilList.java:149:67:149:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilList.java:169:67:169:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilList.java:187:67:187:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilList.java:207:67:207:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilList.java:231:67:231:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilList.java:242:67:242:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilList.java:247:42:247:54 | args : String[] | semmle.label | args : String[] |
+| AllowListSanitizerWithJavaUtilList.java:260:67:260:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilList.java:269:67:269:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilList.java:278:67:278:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilList.java:283:33:283:45 | args : String[] | semmle.label | args : String[] |
+| AllowListSanitizerWithJavaUtilList.java:293:67:293:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilSet.java:47:26:47:38 | args : String[] | semmle.label | args : String[] |
+| AllowListSanitizerWithJavaUtilSet.java:49:20:49:23 | args : String[] | semmle.label | args : String[] |
+| AllowListSanitizerWithJavaUtilSet.java:50:13:50:16 | args : String[] | semmle.label | args : String[] |
+| AllowListSanitizerWithJavaUtilSet.java:53:23:53:26 | args : String[] | semmle.label | args : String[] |
+| AllowListSanitizerWithJavaUtilSet.java:54:14:54:17 | args : String[] | semmle.label | args : String[] |
+| AllowListSanitizerWithJavaUtilSet.java:57:39:57:51 | args : String[] | semmle.label | args : String[] |
+| AllowListSanitizerWithJavaUtilSet.java:87:66:87:70 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilSet.java:93:66:93:70 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilSet.java:99:66:99:70 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilSet.java:105:66:105:70 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilSet.java:117:66:117:70 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilSet.java:131:32:131:44 | args : String[] | semmle.label | args : String[] |
+| AllowListSanitizerWithJavaUtilSet.java:148:67:148:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilSet.java:168:67:168:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilSet.java:186:67:186:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilSet.java:206:67:206:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilSet.java:230:67:230:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilSet.java:241:67:241:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilSet.java:246:42:246:54 | args : String[] | semmle.label | args : String[] |
+| AllowListSanitizerWithJavaUtilSet.java:259:67:259:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilSet.java:268:67:268:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilSet.java:277:67:277:71 | query | semmle.label | query |
+| AllowListSanitizerWithJavaUtilSet.java:282:33:282:45 | args : String[] | semmle.label | args : String[] |
+| AllowListSanitizerWithJavaUtilSet.java:292:67:292:71 | query | semmle.label | query |
| Mongo.java:10:29:10:41 | args : String[] | semmle.label | args : String[] |
| Mongo.java:17:45:17:67 | parse(...) | semmle.label | parse(...) |
| Mongo.java:17:56:17:66 | stringQuery : String | semmle.label | stringQuery : String |
diff --git a/java/ql/test/query-tests/security/CWE-327/semmle/tests/BrokenCryptoAlgorithm.expected b/java/ql/test/query-tests/security/CWE-327/semmle/tests/BrokenCryptoAlgorithm.expected
index 612e1c730544..94719b477391 100644
--- a/java/ql/test/query-tests/security/CWE-327/semmle/tests/BrokenCryptoAlgorithm.expected
+++ b/java/ql/test/query-tests/security/CWE-327/semmle/tests/BrokenCryptoAlgorithm.expected
@@ -1,14 +1,8 @@
#select
| Test.java:19:20:19:50 | getInstance(...) | Test.java:19:45:19:49 | "DES" | Test.java:19:45:19:49 | "DES" | Cryptographic algorithm $@ is weak and should not be used. | Test.java:19:45:19:49 | "DES" | DES |
| Test.java:42:14:42:38 | getInstance(...) | Test.java:42:33:42:37 | "RC2" | Test.java:42:33:42:37 | "RC2" | Cryptographic algorithm $@ is weak and should not be used. | Test.java:42:33:42:37 | "RC2" | RC2 |
-| WeakHashing.java:21:30:21:92 | getInstance(...) | WeakHashing.java:21:86:21:90 | "MD5" : String | WeakHashing.java:21:56:21:91 | getProperty(...) | Cryptographic algorithm $@ is weak and should not be used. | WeakHashing.java:21:86:21:90 | "MD5" | MD5 |
edges
-| WeakHashing.java:21:86:21:90 | "MD5" : String | WeakHashing.java:21:56:21:91 | getProperty(...) | provenance | MaD:1 |
-models
-| 1 | Summary: java.util; Properties; true; getProperty; (String,String); ; Argument[1]; ReturnValue; value; manual |
nodes
| Test.java:19:45:19:49 | "DES" | semmle.label | "DES" |
| Test.java:42:33:42:37 | "RC2" | semmle.label | "RC2" |
-| WeakHashing.java:21:56:21:91 | getProperty(...) | semmle.label | getProperty(...) |
-| WeakHashing.java:21:86:21:90 | "MD5" : String | semmle.label | "MD5" : String |
subpaths
diff --git a/java/ql/test/query-tests/security/CWE-327/semmle/tests/WeakHashing.java b/java/ql/test/query-tests/security/CWE-327/semmle/tests/WeakHashing.java
index 6a3565fc1412..c79c025a41c8 100644
--- a/java/ql/test/query-tests/security/CWE-327/semmle/tests/WeakHashing.java
+++ b/java/ql/test/query-tests/security/CWE-327/semmle/tests/WeakHashing.java
@@ -19,11 +19,17 @@ void hashing() throws NoSuchAlgorithmException, IOException {
// BAD: Using a strong hashing algorithm but with a weak default
MessageDigest bad3 = MessageDigest.getInstance(props.getProperty("hashAlg2", "MD5"));
-
+
// GOOD: Using a strong hashing algorithm
MessageDigest ok = MessageDigest.getInstance(props.getProperty("hashAlg2"));
// OK: Property does not exist and default is secure
MessageDigest ok2 = MessageDigest.getInstance(props.getProperty("hashAlg3", "SHA-256"));
+
+ // GOOD: Using a strong hashing algorithm
+ MessageDigest ok3 = MessageDigest.getInstance("SHA3-512");
+
+ // GOOD: Using a strong hashing algorithm
+ MessageDigest ok4 = MessageDigest.getInstance("SHA384");
}
-}
\ No newline at end of file
+}
diff --git a/javascript/extractor/lib/typescript/package-lock.json b/javascript/extractor/lib/typescript/package-lock.json
index 50a9e0a66caa..1978e3963246 100644
--- a/javascript/extractor/lib/typescript/package-lock.json
+++ b/javascript/extractor/lib/typescript/package-lock.json
@@ -6,7 +6,7 @@
"": {
"name": "typescript-parser-wrapper",
"dependencies": {
- "typescript": "^5.6.2"
+ "typescript": "^5.7.2"
},
"devDependencies": {
"@types/node": "18.15.3"
@@ -20,9 +20,9 @@
"license": "MIT"
},
"node_modules/typescript": {
- "version": "5.6.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
- "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
+ "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
diff --git a/javascript/extractor/lib/typescript/package.json b/javascript/extractor/lib/typescript/package.json
index bf650ec457a4..9d77f4ab740a 100644
--- a/javascript/extractor/lib/typescript/package.json
+++ b/javascript/extractor/lib/typescript/package.json
@@ -2,7 +2,7 @@
"name": "typescript-parser-wrapper",
"private": true,
"dependencies": {
- "typescript": "5.6.2"
+ "typescript": "^5.7.2"
},
"scripts": {
"build": "tsc --project tsconfig.json",
diff --git a/javascript/ql/integration-tests/diagnostics/internal-error/src/my_failure.ts b/javascript/ql/integration-tests/diagnostics/internal-error/src/my_failure.ts
index 29e78b136e82..f2a1644e18ff 100644
--- a/javascript/ql/integration-tests/diagnostics/internal-error/src/my_failure.ts
+++ b/javascript/ql/integration-tests/diagnostics/internal-error/src/my_failure.ts
@@ -1,17 +1,3022 @@
-type Output = {
- (...args: S): any;
-};
-
-declare function createThing(
- type: K,
- fn: (...args: S) => any
-): Output;
-
-const one = createThing("one", () => ({}));
-
-const two = createThing("two", () => ({}));
-
-const three = createThing("three", (cursor: string) => null);
-const four = createThing("four", (error: number) => null);
-
-type Events = Array;
+console.log(
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1 +
+ 1
+);
diff --git a/javascript/ql/lib/change-notes/2024-11-18-ES2022-find-functions.md b/javascript/ql/lib/change-notes/2024-11-18-ES2022-find-functions.md
new file mode 100644
index 000000000000..e3fe3b6aef25
--- /dev/null
+++ b/javascript/ql/lib/change-notes/2024-11-18-ES2022-find-functions.md
@@ -0,0 +1,5 @@
+---
+category: minorAnalysis
+---
+* Added taint-steps for `Array.prototype.findLast`
+* Added taint-steps for `Array.prototype.findLastIndex`
diff --git a/javascript/ql/lib/change-notes/2024-11-20-ES2023-string-protytpe-toWellFormed.md b/javascript/ql/lib/change-notes/2024-11-20-ES2023-string-protytpe-toWellFormed.md
new file mode 100644
index 000000000000..dda4d8787605
--- /dev/null
+++ b/javascript/ql/lib/change-notes/2024-11-20-ES2023-string-protytpe-toWellFormed.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Added taint-steps for `String.prototype.toWellFormed`.
diff --git a/javascript/ql/lib/change-notes/2024-11-20-ES2024-group-functions.md b/javascript/ql/lib/change-notes/2024-11-20-ES2024-group-functions.md
new file mode 100644
index 000000000000..8511727f8e77
--- /dev/null
+++ b/javascript/ql/lib/change-notes/2024-11-20-ES2024-group-functions.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Added taint-steps for `Map.groupBy` and `Object.groupBy`.
diff --git a/javascript/ql/lib/semmle/javascript/Arrays.qll b/javascript/ql/lib/semmle/javascript/Arrays.qll
index 7ce37130996a..bec711b835a7 100644
--- a/javascript/ql/lib/semmle/javascript/Arrays.qll
+++ b/javascript/ql/lib/semmle/javascript/Arrays.qll
@@ -384,10 +384,10 @@ private module ArrayLibraries {
}
/**
- * Gets a call to `Array.prototype.find` or a polyfill implementing the same functionality.
+ * Gets a call to `Array.prototype.find` or `Array.prototype.findLast` or a polyfill implementing the same functionality.
*/
DataFlow::CallNode arrayFindCall(DataFlow::Node array) {
- result.(DataFlow::MethodCallNode).getMethodName() = "find" and
+ result.(DataFlow::MethodCallNode).getMethodName() in ["find", "findLast"] and
array = result.getReceiver()
or
result = DataFlow::moduleImport(["array.prototype.find", "array-find"]).getACall() and
@@ -483,4 +483,31 @@ private module ArrayLibraries {
)
}
}
+
+ /**
+ * Defines a data flow step that tracks the flow of data through callback functions in arrays.
+ */
+ private class ArrayCallBackDataFlowStep extends PreCallGraphStep {
+ override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
+ exists(DataFlow::MethodCallNode call |
+ call.getMethodName() = ["findLast", "find", "findLastIndex"] and
+ prop = arrayLikeElement() and
+ obj = call.getReceiver() and
+ element = call.getCallback(0).getParameter(0)
+ )
+ }
+ }
+
+ /**
+ * This step models the propagation of data from the array to the callback function's parameter.
+ */
+ private class ArrayCallBackDataTaintStep extends TaintTracking::SharedTaintStep {
+ override predicate step(DataFlow::Node obj, DataFlow::Node element) {
+ exists(DataFlow::MethodCallNode call |
+ call.getMethodName() = ["findLast", "find", "findLastIndex"] and
+ obj = call.getReceiver() and
+ element = call.getCallback(0).getParameter(0)
+ )
+ }
+ }
}
diff --git a/javascript/ql/lib/semmle/javascript/Collections.qll b/javascript/ql/lib/semmle/javascript/Collections.qll
index a0e251554ff7..028c3abe4b3b 100644
--- a/javascript/ql/lib/semmle/javascript/Collections.qll
+++ b/javascript/ql/lib/semmle/javascript/Collections.qll
@@ -151,4 +151,32 @@ private module CollectionDataFlow {
)
}
}
+
+ /**
+ * A step for a call to `groupBy` on an iterable object.
+ */
+ private class GroupByTaintStep extends TaintTracking::SharedTaintStep {
+ override predicate heapStep(DataFlow::Node pred, DataFlow::Node succ) {
+ exists(DataFlow::MethodCallNode call |
+ call = DataFlow::globalVarRef(["Map", "Object"]).getAMemberCall("groupBy") and
+ pred = call.getArgument(0) and
+ (succ = call.getCallback(1).getParameter(0) or succ = call)
+ )
+ }
+ }
+
+ /**
+ * A step for handling data flow and taint tracking for the groupBy method on iterable objects.
+ * Ensures propagation of taint and data flow through the groupBy operation.
+ */
+ private class GroupByDataFlowStep extends PreCallGraphStep {
+ override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
+ exists(DataFlow::MethodCallNode call |
+ call = DataFlow::globalVarRef("Map").getAMemberCall("groupBy") and
+ pred = call.getArgument(0) and
+ succ = call and
+ prop = mapValueUnknownKey()
+ )
+ }
+ }
}
diff --git a/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll
index a19691e94480..6b6fc9c4b07d 100644
--- a/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll
+++ b/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll
@@ -612,7 +612,7 @@ module TaintTracking {
"italics", "link", "padEnd", "padStart", "repeat", "replace", "replaceAll", "slice",
"small", "split", "strike", "sub", "substr", "substring", "sup",
"toLocaleLowerCase", "toLocaleUpperCase", "toLowerCase", "toUpperCase", "trim",
- "trimLeft", "trimRight"
+ "trimLeft", "trimRight", "toWellFormed"
]
or
// sorted, interesting, properties of Object.prototype
diff --git a/javascript/ql/src/Statements/UseOfReturnlessFunction.ql b/javascript/ql/src/Statements/UseOfReturnlessFunction.ql
index ea5f2fb97558..818f0d922d48 100644
--- a/javascript/ql/src/Statements/UseOfReturnlessFunction.ql
+++ b/javascript/ql/src/Statements/UseOfReturnlessFunction.ql
@@ -114,7 +114,7 @@ predicate hasNonVoidCallbackMethod(string name) {
name =
[
"every", "filter", "find", "findIndex", "flatMap", "map", "reduce", "reduceRight", "some",
- "sort"
+ "sort", "findLastIndex", "findLast"
]
}
diff --git a/javascript/ql/test/library-tests/Arrays/DataFlow.expected b/javascript/ql/test/library-tests/Arrays/DataFlow.expected
index 4332f14c45e2..07fabfb7270c 100644
--- a/javascript/ql/test/library-tests/Arrays/DataFlow.expected
+++ b/javascript/ql/test/library-tests/Arrays/DataFlow.expected
@@ -14,6 +14,7 @@
| arrays.js:2:16:2:23 | "source" | arrays.js:90:10:90:10 | x |
| arrays.js:2:16:2:23 | "source" | arrays.js:93:8:93:17 | arr.at(-1) |
| arrays.js:2:16:2:23 | "source" | arrays.js:109:8:109:24 | arr8_spread.pop() |
+| arrays.js:2:16:2:23 | "source" | arrays.js:111:8:111:33 | arr.fin ... llback) |
| arrays.js:18:22:18:29 | "source" | arrays.js:18:50:18:50 | e |
| arrays.js:22:15:22:22 | "source" | arrays.js:23:8:23:17 | arr2.pop() |
| arrays.js:25:15:25:22 | "source" | arrays.js:26:8:26:17 | arr3.pop() |
@@ -25,3 +26,8 @@
| arrays.js:53:4:53:11 | "source" | arrays.js:54:10:54:18 | ary.pop() |
| arrays.js:99:31:99:38 | "source" | arrays.js:100:8:100:17 | arr8.pop() |
| arrays.js:103:55:103:62 | "source" | arrays.js:105:8:105:25 | arr8_variant.pop() |
+| arrays.js:114:19:114:26 | "source" | arrays.js:115:50:115:53 | item |
+| arrays.js:114:19:114:26 | "source" | arrays.js:116:10:116:16 | element |
+| arrays.js:120:19:120:26 | "source" | arrays.js:121:46:121:49 | item |
+| arrays.js:120:19:120:26 | "source" | arrays.js:122:10:122:16 | element |
+| arrays.js:126:19:126:26 | "source" | arrays.js:127:55:127:58 | item |
diff --git a/javascript/ql/test/library-tests/Arrays/DataFlow.ql b/javascript/ql/test/library-tests/Arrays/DataFlow.ql
index 80c9f068a10f..5c5f4a0d10e6 100644
--- a/javascript/ql/test/library-tests/Arrays/DataFlow.ql
+++ b/javascript/ql/test/library-tests/Arrays/DataFlow.ql
@@ -3,7 +3,10 @@ import javascript
class ArrayFlowConfig extends DataFlow::Configuration {
ArrayFlowConfig() { this = "ArrayFlowConfig" }
- override predicate isSource(DataFlow::Node source) { source.asExpr().getStringValue() = "source" }
+ override predicate isSource(DataFlow::Node source) {
+ source.asExpr().getStringValue() = "source" or
+ source.(DataFlow::CallNode).getCalleeName() = "source"
+ }
override predicate isSink(DataFlow::Node sink) {
sink = any(DataFlow::CallNode call | call.getCalleeName() = "sink").getAnArgument()
diff --git a/javascript/ql/test/library-tests/Arrays/TaintFlow.expected b/javascript/ql/test/library-tests/Arrays/TaintFlow.expected
index a531715bfb6b..246a52e803b2 100644
--- a/javascript/ql/test/library-tests/Arrays/TaintFlow.expected
+++ b/javascript/ql/test/library-tests/Arrays/TaintFlow.expected
@@ -15,6 +15,7 @@
| arrays.js:2:16:2:23 | "source" | arrays.js:90:10:90:10 | x |
| arrays.js:2:16:2:23 | "source" | arrays.js:93:8:93:17 | arr.at(-1) |
| arrays.js:2:16:2:23 | "source" | arrays.js:109:8:109:24 | arr8_spread.pop() |
+| arrays.js:2:16:2:23 | "source" | arrays.js:111:8:111:33 | arr.fin ... llback) |
| arrays.js:18:22:18:29 | "source" | arrays.js:18:50:18:50 | e |
| arrays.js:22:15:22:22 | "source" | arrays.js:23:8:23:17 | arr2.pop() |
| arrays.js:25:15:25:22 | "source" | arrays.js:26:8:26:17 | arr3.pop() |
@@ -29,3 +30,13 @@
| arrays.js:96:9:96:16 | "source" | arrays.js:96:8:96:36 | ["sourc ... => !!x) |
| arrays.js:99:31:99:38 | "source" | arrays.js:100:8:100:17 | arr8.pop() |
| arrays.js:103:55:103:62 | "source" | arrays.js:105:8:105:25 | arr8_variant.pop() |
+| arrays.js:114:19:114:26 | "source" | arrays.js:115:50:115:53 | item |
+| arrays.js:114:19:114:26 | "source" | arrays.js:116:10:116:16 | element |
+| arrays.js:120:19:120:26 | "source" | arrays.js:121:46:121:49 | item |
+| arrays.js:120:19:120:26 | "source" | arrays.js:122:10:122:16 | element |
+| arrays.js:126:19:126:26 | "source" | arrays.js:127:55:127:58 | item |
+| arrays.js:131:17:131:24 | source() | arrays.js:132:46:132:49 | item |
+| arrays.js:131:17:131:24 | source() | arrays.js:133:10:133:17 | element1 |
+| arrays.js:137:17:137:24 | source() | arrays.js:138:50:138:53 | item |
+| arrays.js:137:17:137:24 | source() | arrays.js:139:10:139:17 | element1 |
+| arrays.js:143:17:143:24 | source() | arrays.js:144:55:144:58 | item |
diff --git a/javascript/ql/test/library-tests/Arrays/TaintFlow.ql b/javascript/ql/test/library-tests/Arrays/TaintFlow.ql
index cee2f294a349..d8f187591624 100644
--- a/javascript/ql/test/library-tests/Arrays/TaintFlow.ql
+++ b/javascript/ql/test/library-tests/Arrays/TaintFlow.ql
@@ -3,7 +3,10 @@ import javascript
class ArrayTaintFlowConfig extends TaintTracking::Configuration {
ArrayTaintFlowConfig() { this = "ArrayTaintFlowConfig" }
- override predicate isSource(DataFlow::Node source) { source.asExpr().getStringValue() = "source" }
+ override predicate isSource(DataFlow::Node source) {
+ source.asExpr().getStringValue() = "source" or
+ source.(DataFlow::CallNode).getCalleeName() = "source"
+ }
override predicate isSink(DataFlow::Node sink) {
sink = any(DataFlow::CallNode call | call.getCalleeName() = "sink").getAnArgument()
diff --git a/javascript/ql/test/library-tests/Arrays/arrays.js b/javascript/ql/test/library-tests/Arrays/arrays.js
index 579741fa3aac..deedf29f6f67 100644
--- a/javascript/ql/test/library-tests/Arrays/arrays.js
+++ b/javascript/ql/test/library-tests/Arrays/arrays.js
@@ -107,4 +107,41 @@
var arr8_spread = [];
arr8_spread = arr8_spread.toSpliced(0, 0, ...arr);
sink(arr8_spread.pop()); // NOT OK
+
+ sink(arr.findLast(someCallback)); // NOT OK
+
+ { // Test for findLast function
+ const list = ["source"];
+ const element = list.findLast((item) => sink(item)); // NOT OK
+ sink(element); // NOT OK
+ }
+
+ { // Test for find function
+ const list = ["source"];
+ const element = list.find((item) => sink(item)); // NOT OK
+ sink(element); // NOT OK
+ }
+
+ { // Test for findLastIndex function
+ const list = ["source"];
+ const element = list.findLastIndex((item) => sink(item)); // NOT OK
+ sink(element); // OK
+ }
+ {
+ const arr = source();
+ const element1 = arr.find((item) => sink(item)); // NOT OK
+ sink(element1); // NOT OK
+ }
+
+ {
+ const arr = source();
+ const element1 = arr.findLast((item) => sink(item)); // NOT OK
+ sink(element1); // NOT OK
+ }
+
+ {
+ const arr = source();
+ const element1 = arr.findLastIndex((item) => sink(item)); // NOT OK
+ sink(element1); // OK
+ }
});
diff --git a/javascript/ql/test/library-tests/Arrays/printAst.expected b/javascript/ql/test/library-tests/Arrays/printAst.expected
index a7333b294853..a825b12f3fbd 100644
--- a/javascript/ql/test/library-tests/Arrays/printAst.expected
+++ b/javascript/ql/test/library-tests/Arrays/printAst.expected
@@ -1,9 +1,9 @@
nodes
-| arrays.js:1:1:110:2 | [ParExpr] (functi ... T OK }) | semmle.label | [ParExpr] (functi ... T OK }) |
-| arrays.js:1:1:110:3 | [ExprStmt] (functi ... OK }); | semmle.label | [ExprStmt] (functi ... OK }); |
-| arrays.js:1:1:110:3 | [ExprStmt] (functi ... OK }); | semmle.order | 1 |
-| arrays.js:1:2:110:1 | [FunctionExpr] functio ... OT OK } | semmle.label | [FunctionExpr] functio ... OT OK } |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | semmle.label | [BlockStmt] { let ... OT OK } |
+| arrays.js:1:1:147:2 | [ParExpr] (functi ... } }) | semmle.label | [ParExpr] (functi ... } }) |
+| arrays.js:1:1:147:3 | [ExprStmt] (functi ... } }); | semmle.label | [ExprStmt] (functi ... } }); |
+| arrays.js:1:1:147:3 | [ExprStmt] (functi ... } }); | semmle.order | 1 |
+| arrays.js:1:2:147:1 | [FunctionExpr] functio ... K } } | semmle.label | [FunctionExpr] functio ... K } } |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | semmle.label | [BlockStmt] { let ... K } } |
| arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.label | [DeclStmt] let source = ... |
| arrays.js:2:7:2:12 | [VarDecl] source | semmle.label | [VarDecl] source |
| arrays.js:2:7:2:23 | [VariableDeclarator] source = "source" | semmle.label | [VariableDeclarator] source = "source" |
@@ -487,6 +487,146 @@ nodes
| arrays.js:109:8:109:22 | [DotExpr] arr8_spread.pop | semmle.label | [DotExpr] arr8_spread.pop |
| arrays.js:109:8:109:24 | [MethodCallExpr] arr8_spread.pop() | semmle.label | [MethodCallExpr] arr8_spread.pop() |
| arrays.js:109:20:109:22 | [Label] pop | semmle.label | [Label] pop |
+| arrays.js:111:3:111:6 | [VarRef] sink | semmle.label | [VarRef] sink |
+| arrays.js:111:3:111:34 | [CallExpr] sink(ar ... lback)) | semmle.label | [CallExpr] sink(ar ... lback)) |
+| arrays.js:111:3:111:35 | [ExprStmt] sink(ar ... back)); | semmle.label | [ExprStmt] sink(ar ... back)); |
+| arrays.js:111:8:111:10 | [VarRef] arr | semmle.label | [VarRef] arr |
+| arrays.js:111:8:111:19 | [DotExpr] arr.findLast | semmle.label | [DotExpr] arr.findLast |
+| arrays.js:111:8:111:33 | [MethodCallExpr] arr.fin ... llback) | semmle.label | [MethodCallExpr] arr.fin ... llback) |
+| arrays.js:111:12:111:19 | [Label] findLast | semmle.label | [Label] findLast |
+| arrays.js:111:21:111:32 | [VarRef] someCallback | semmle.label | [VarRef] someCallback |
+| arrays.js:113:3:117:3 | [BlockStmt] { // T ... OK } | semmle.label | [BlockStmt] { // T ... OK } |
+| arrays.js:114:5:114:28 | [DeclStmt] const list = ... | semmle.label | [DeclStmt] const list = ... |
+| arrays.js:114:11:114:14 | [VarDecl] list | semmle.label | [VarDecl] list |
+| arrays.js:114:11:114:27 | [VariableDeclarator] list = ["source"] | semmle.label | [VariableDeclarator] list = ["source"] |
+| arrays.js:114:18:114:27 | [ArrayExpr] ["source"] | semmle.label | [ArrayExpr] ["source"] |
+| arrays.js:114:19:114:26 | [Literal] "source" | semmle.label | [Literal] "source" |
+| arrays.js:115:5:115:56 | [DeclStmt] const element = ... | semmle.label | [DeclStmt] const element = ... |
+| arrays.js:115:11:115:17 | [VarDecl] element | semmle.label | [VarDecl] element |
+| arrays.js:115:11:115:55 | [VariableDeclarator] element ... (item)) | semmle.label | [VariableDeclarator] element ... (item)) |
+| arrays.js:115:21:115:24 | [VarRef] list | semmle.label | [VarRef] list |
+| arrays.js:115:21:115:33 | [DotExpr] list.findLast | semmle.label | [DotExpr] list.findLast |
+| arrays.js:115:21:115:55 | [MethodCallExpr] list.fi ... (item)) | semmle.label | [MethodCallExpr] list.fi ... (item)) |
+| arrays.js:115:26:115:33 | [Label] findLast | semmle.label | [Label] findLast |
+| arrays.js:115:35:115:54 | [ArrowFunctionExpr] (item) => sink(item) | semmle.label | [ArrowFunctionExpr] (item) => sink(item) |
+| arrays.js:115:36:115:39 | [SimpleParameter] item | semmle.label | [SimpleParameter] item |
+| arrays.js:115:45:115:48 | [VarRef] sink | semmle.label | [VarRef] sink |
+| arrays.js:115:45:115:54 | [CallExpr] sink(item) | semmle.label | [CallExpr] sink(item) |
+| arrays.js:115:50:115:53 | [VarRef] item | semmle.label | [VarRef] item |
+| arrays.js:116:5:116:8 | [VarRef] sink | semmle.label | [VarRef] sink |
+| arrays.js:116:5:116:17 | [CallExpr] sink(element) | semmle.label | [CallExpr] sink(element) |
+| arrays.js:116:5:116:18 | [ExprStmt] sink(element); | semmle.label | [ExprStmt] sink(element); |
+| arrays.js:116:10:116:16 | [VarRef] element | semmle.label | [VarRef] element |
+| arrays.js:119:3:123:3 | [BlockStmt] { // T ... OK } | semmle.label | [BlockStmt] { // T ... OK } |
+| arrays.js:120:5:120:28 | [DeclStmt] const list = ... | semmle.label | [DeclStmt] const list = ... |
+| arrays.js:120:11:120:14 | [VarDecl] list | semmle.label | [VarDecl] list |
+| arrays.js:120:11:120:27 | [VariableDeclarator] list = ["source"] | semmle.label | [VariableDeclarator] list = ["source"] |
+| arrays.js:120:18:120:27 | [ArrayExpr] ["source"] | semmle.label | [ArrayExpr] ["source"] |
+| arrays.js:120:19:120:26 | [Literal] "source" | semmle.label | [Literal] "source" |
+| arrays.js:121:5:121:52 | [DeclStmt] const element = ... | semmle.label | [DeclStmt] const element = ... |
+| arrays.js:121:11:121:17 | [VarDecl] element | semmle.label | [VarDecl] element |
+| arrays.js:121:11:121:51 | [VariableDeclarator] element ... (item)) | semmle.label | [VariableDeclarator] element ... (item)) |
+| arrays.js:121:21:121:24 | [VarRef] list | semmle.label | [VarRef] list |
+| arrays.js:121:21:121:29 | [DotExpr] list.find | semmle.label | [DotExpr] list.find |
+| arrays.js:121:21:121:51 | [MethodCallExpr] list.fi ... (item)) | semmle.label | [MethodCallExpr] list.fi ... (item)) |
+| arrays.js:121:26:121:29 | [Label] find | semmle.label | [Label] find |
+| arrays.js:121:31:121:50 | [ArrowFunctionExpr] (item) => sink(item) | semmle.label | [ArrowFunctionExpr] (item) => sink(item) |
+| arrays.js:121:32:121:35 | [SimpleParameter] item | semmle.label | [SimpleParameter] item |
+| arrays.js:121:41:121:44 | [VarRef] sink | semmle.label | [VarRef] sink |
+| arrays.js:121:41:121:50 | [CallExpr] sink(item) | semmle.label | [CallExpr] sink(item) |
+| arrays.js:121:46:121:49 | [VarRef] item | semmle.label | [VarRef] item |
+| arrays.js:122:5:122:8 | [VarRef] sink | semmle.label | [VarRef] sink |
+| arrays.js:122:5:122:17 | [CallExpr] sink(element) | semmle.label | [CallExpr] sink(element) |
+| arrays.js:122:5:122:18 | [ExprStmt] sink(element); | semmle.label | [ExprStmt] sink(element); |
+| arrays.js:122:10:122:16 | [VarRef] element | semmle.label | [VarRef] element |
+| arrays.js:125:3:129:3 | [BlockStmt] { // T ... OK } | semmle.label | [BlockStmt] { // T ... OK } |
+| arrays.js:126:5:126:28 | [DeclStmt] const list = ... | semmle.label | [DeclStmt] const list = ... |
+| arrays.js:126:11:126:14 | [VarDecl] list | semmle.label | [VarDecl] list |
+| arrays.js:126:11:126:27 | [VariableDeclarator] list = ["source"] | semmle.label | [VariableDeclarator] list = ["source"] |
+| arrays.js:126:18:126:27 | [ArrayExpr] ["source"] | semmle.label | [ArrayExpr] ["source"] |
+| arrays.js:126:19:126:26 | [Literal] "source" | semmle.label | [Literal] "source" |
+| arrays.js:127:5:127:61 | [DeclStmt] const element = ... | semmle.label | [DeclStmt] const element = ... |
+| arrays.js:127:11:127:17 | [VarDecl] element | semmle.label | [VarDecl] element |
+| arrays.js:127:11:127:60 | [VariableDeclarator] element ... (item)) | semmle.label | [VariableDeclarator] element ... (item)) |
+| arrays.js:127:21:127:24 | [VarRef] list | semmle.label | [VarRef] list |
+| arrays.js:127:21:127:38 | [DotExpr] list.findLastIndex | semmle.label | [DotExpr] list.findLastIndex |
+| arrays.js:127:21:127:60 | [MethodCallExpr] list.fi ... (item)) | semmle.label | [MethodCallExpr] list.fi ... (item)) |
+| arrays.js:127:26:127:38 | [Label] findLastIndex | semmle.label | [Label] findLastIndex |
+| arrays.js:127:40:127:59 | [ArrowFunctionExpr] (item) => sink(item) | semmle.label | [ArrowFunctionExpr] (item) => sink(item) |
+| arrays.js:127:41:127:44 | [SimpleParameter] item | semmle.label | [SimpleParameter] item |
+| arrays.js:127:50:127:53 | [VarRef] sink | semmle.label | [VarRef] sink |
+| arrays.js:127:50:127:59 | [CallExpr] sink(item) | semmle.label | [CallExpr] sink(item) |
+| arrays.js:127:55:127:58 | [VarRef] item | semmle.label | [VarRef] item |
+| arrays.js:128:5:128:8 | [VarRef] sink | semmle.label | [VarRef] sink |
+| arrays.js:128:5:128:17 | [CallExpr] sink(element) | semmle.label | [CallExpr] sink(element) |
+| arrays.js:128:5:128:18 | [ExprStmt] sink(element); | semmle.label | [ExprStmt] sink(element); |
+| arrays.js:128:10:128:16 | [VarRef] element | semmle.label | [VarRef] element |
+| arrays.js:130:3:134:3 | [BlockStmt] { c ... OK } | semmle.label | [BlockStmt] { c ... OK } |
+| arrays.js:131:5:131:25 | [DeclStmt] const arr = ... | semmle.label | [DeclStmt] const arr = ... |
+| arrays.js:131:11:131:13 | [VarDecl] arr | semmle.label | [VarDecl] arr |
+| arrays.js:131:11:131:24 | [VariableDeclarator] arr = source() | semmle.label | [VariableDeclarator] arr = source() |
+| arrays.js:131:17:131:22 | [VarRef] source | semmle.label | [VarRef] source |
+| arrays.js:131:17:131:24 | [CallExpr] source() | semmle.label | [CallExpr] source() |
+| arrays.js:132:5:132:52 | [DeclStmt] const element1 = ... | semmle.label | [DeclStmt] const element1 = ... |
+| arrays.js:132:11:132:18 | [VarDecl] element1 | semmle.label | [VarDecl] element1 |
+| arrays.js:132:11:132:51 | [VariableDeclarator] element ... (item)) | semmle.label | [VariableDeclarator] element ... (item)) |
+| arrays.js:132:22:132:24 | [VarRef] arr | semmle.label | [VarRef] arr |
+| arrays.js:132:22:132:29 | [DotExpr] arr.find | semmle.label | [DotExpr] arr.find |
+| arrays.js:132:22:132:51 | [MethodCallExpr] arr.fin ... (item)) | semmle.label | [MethodCallExpr] arr.fin ... (item)) |
+| arrays.js:132:26:132:29 | [Label] find | semmle.label | [Label] find |
+| arrays.js:132:31:132:50 | [ArrowFunctionExpr] (item) => sink(item) | semmle.label | [ArrowFunctionExpr] (item) => sink(item) |
+| arrays.js:132:32:132:35 | [SimpleParameter] item | semmle.label | [SimpleParameter] item |
+| arrays.js:132:41:132:44 | [VarRef] sink | semmle.label | [VarRef] sink |
+| arrays.js:132:41:132:50 | [CallExpr] sink(item) | semmle.label | [CallExpr] sink(item) |
+| arrays.js:132:46:132:49 | [VarRef] item | semmle.label | [VarRef] item |
+| arrays.js:133:5:133:8 | [VarRef] sink | semmle.label | [VarRef] sink |
+| arrays.js:133:5:133:18 | [CallExpr] sink(element1) | semmle.label | [CallExpr] sink(element1) |
+| arrays.js:133:5:133:19 | [ExprStmt] sink(element1); | semmle.label | [ExprStmt] sink(element1); |
+| arrays.js:133:10:133:17 | [VarRef] element1 | semmle.label | [VarRef] element1 |
+| arrays.js:136:3:140:3 | [BlockStmt] { c ... OK } | semmle.label | [BlockStmt] { c ... OK } |
+| arrays.js:137:5:137:25 | [DeclStmt] const arr = ... | semmle.label | [DeclStmt] const arr = ... |
+| arrays.js:137:11:137:13 | [VarDecl] arr | semmle.label | [VarDecl] arr |
+| arrays.js:137:11:137:24 | [VariableDeclarator] arr = source() | semmle.label | [VariableDeclarator] arr = source() |
+| arrays.js:137:17:137:22 | [VarRef] source | semmle.label | [VarRef] source |
+| arrays.js:137:17:137:24 | [CallExpr] source() | semmle.label | [CallExpr] source() |
+| arrays.js:138:5:138:56 | [DeclStmt] const element1 = ... | semmle.label | [DeclStmt] const element1 = ... |
+| arrays.js:138:11:138:18 | [VarDecl] element1 | semmle.label | [VarDecl] element1 |
+| arrays.js:138:11:138:55 | [VariableDeclarator] element ... (item)) | semmle.label | [VariableDeclarator] element ... (item)) |
+| arrays.js:138:22:138:24 | [VarRef] arr | semmle.label | [VarRef] arr |
+| arrays.js:138:22:138:33 | [DotExpr] arr.findLast | semmle.label | [DotExpr] arr.findLast |
+| arrays.js:138:22:138:55 | [MethodCallExpr] arr.fin ... (item)) | semmle.label | [MethodCallExpr] arr.fin ... (item)) |
+| arrays.js:138:26:138:33 | [Label] findLast | semmle.label | [Label] findLast |
+| arrays.js:138:35:138:54 | [ArrowFunctionExpr] (item) => sink(item) | semmle.label | [ArrowFunctionExpr] (item) => sink(item) |
+| arrays.js:138:36:138:39 | [SimpleParameter] item | semmle.label | [SimpleParameter] item |
+| arrays.js:138:45:138:48 | [VarRef] sink | semmle.label | [VarRef] sink |
+| arrays.js:138:45:138:54 | [CallExpr] sink(item) | semmle.label | [CallExpr] sink(item) |
+| arrays.js:138:50:138:53 | [VarRef] item | semmle.label | [VarRef] item |
+| arrays.js:139:5:139:8 | [VarRef] sink | semmle.label | [VarRef] sink |
+| arrays.js:139:5:139:18 | [CallExpr] sink(element1) | semmle.label | [CallExpr] sink(element1) |
+| arrays.js:139:5:139:19 | [ExprStmt] sink(element1); | semmle.label | [ExprStmt] sink(element1); |
+| arrays.js:139:10:139:17 | [VarRef] element1 | semmle.label | [VarRef] element1 |
+| arrays.js:142:3:146:3 | [BlockStmt] { c ... OK } | semmle.label | [BlockStmt] { c ... OK } |
+| arrays.js:143:5:143:25 | [DeclStmt] const arr = ... | semmle.label | [DeclStmt] const arr = ... |
+| arrays.js:143:11:143:13 | [VarDecl] arr | semmle.label | [VarDecl] arr |
+| arrays.js:143:11:143:24 | [VariableDeclarator] arr = source() | semmle.label | [VariableDeclarator] arr = source() |
+| arrays.js:143:17:143:22 | [VarRef] source | semmle.label | [VarRef] source |
+| arrays.js:143:17:143:24 | [CallExpr] source() | semmle.label | [CallExpr] source() |
+| arrays.js:144:5:144:61 | [DeclStmt] const element1 = ... | semmle.label | [DeclStmt] const element1 = ... |
+| arrays.js:144:11:144:18 | [VarDecl] element1 | semmle.label | [VarDecl] element1 |
+| arrays.js:144:11:144:60 | [VariableDeclarator] element ... (item)) | semmle.label | [VariableDeclarator] element ... (item)) |
+| arrays.js:144:22:144:24 | [VarRef] arr | semmle.label | [VarRef] arr |
+| arrays.js:144:22:144:38 | [DotExpr] arr.findLastIndex | semmle.label | [DotExpr] arr.findLastIndex |
+| arrays.js:144:22:144:60 | [MethodCallExpr] arr.fin ... (item)) | semmle.label | [MethodCallExpr] arr.fin ... (item)) |
+| arrays.js:144:26:144:38 | [Label] findLastIndex | semmle.label | [Label] findLastIndex |
+| arrays.js:144:40:144:59 | [ArrowFunctionExpr] (item) => sink(item) | semmle.label | [ArrowFunctionExpr] (item) => sink(item) |
+| arrays.js:144:41:144:44 | [SimpleParameter] item | semmle.label | [SimpleParameter] item |
+| arrays.js:144:50:144:53 | [VarRef] sink | semmle.label | [VarRef] sink |
+| arrays.js:144:50:144:59 | [CallExpr] sink(item) | semmle.label | [CallExpr] sink(item) |
+| arrays.js:144:55:144:58 | [VarRef] item | semmle.label | [VarRef] item |
+| arrays.js:145:5:145:8 | [VarRef] sink | semmle.label | [VarRef] sink |
+| arrays.js:145:5:145:18 | [CallExpr] sink(element1) | semmle.label | [CallExpr] sink(element1) |
+| arrays.js:145:5:145:19 | [ExprStmt] sink(element1); | semmle.label | [ExprStmt] sink(element1); |
+| arrays.js:145:10:145:17 | [VarRef] element1 | semmle.label | [VarRef] element1 |
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
@@ -544,6 +684,32 @@ nodes
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
+| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
+| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
+| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
+| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
+| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
+| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
@@ -552,128 +718,142 @@ nodes
| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
edges
-| arrays.js:1:1:110:2 | [ParExpr] (functi ... T OK }) | arrays.js:1:2:110:1 | [FunctionExpr] functio ... OT OK } | semmle.label | 1 |
-| arrays.js:1:1:110:2 | [ParExpr] (functi ... T OK }) | arrays.js:1:2:110:1 | [FunctionExpr] functio ... OT OK } | semmle.order | 1 |
-| arrays.js:1:1:110:3 | [ExprStmt] (functi ... OK }); | arrays.js:1:1:110:2 | [ParExpr] (functi ... T OK }) | semmle.label | 1 |
-| arrays.js:1:1:110:3 | [ExprStmt] (functi ... OK }); | arrays.js:1:1:110:2 | [ParExpr] (functi ... T OK }) | semmle.order | 1 |
-| arrays.js:1:2:110:1 | [FunctionExpr] functio ... OT OK } | arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | semmle.label | 5 |
-| arrays.js:1:2:110:1 | [FunctionExpr] functio ... OT OK } | arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | semmle.order | 5 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.label | 1 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.order | 1 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.label | 2 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.order | 2 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.label | 3 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.order | 3 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.label | 4 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.order | 4 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.label | 5 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.order | 5 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.label | 6 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.order | 6 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.label | 7 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.order | 7 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.label | 8 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.order | 8 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.label | 9 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.order | 9 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.label | 10 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.order | 10 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.label | 11 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.order | 11 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.label | 12 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.order | 12 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.label | 13 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.order | 13 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.label | 14 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.order | 14 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.label | 15 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.order | 15 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.label | 16 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.order | 16 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.label | 17 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.order | 17 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:32:3:32:24 | [DeclStmt] var arr4_variant = ... | semmle.label | 18 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:32:3:32:24 | [DeclStmt] var arr4_variant = ... | semmle.order | 18 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:33:3:33:46 | [ExprStmt] arr4_va ... urce"); | semmle.label | 19 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:33:3:33:46 | [ExprStmt] arr4_va ... urce"); | semmle.order | 19 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:34:3:34:21 | [ExprStmt] arr4_variant.pop(); | semmle.label | 20 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:34:3:34:21 | [ExprStmt] arr4_variant.pop(); | semmle.order | 20 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:35:3:35:27 | [ExprStmt] sink(ar ... pop()); | semmle.label | 21 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:35:3:35:27 | [ExprStmt] sink(ar ... pop()); | semmle.order | 21 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:37:3:37:23 | [DeclStmt] var arr4_spread = ... | semmle.label | 22 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:37:3:37:23 | [DeclStmt] var arr4_spread = ... | semmle.order | 22 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:38:3:38:35 | [ExprStmt] arr4_sp ... ..arr); | semmle.label | 23 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:38:3:38:35 | [ExprStmt] arr4_sp ... ..arr); | semmle.order | 23 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:39:3:39:26 | [ExprStmt] sink(ar ... pop()); | semmle.label | 24 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:39:3:39:26 | [ExprStmt] sink(ar ... pop()); | semmle.order | 24 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:41:3:41:29 | [DeclStmt] var arr5 = ... | semmle.label | 25 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:41:3:41:29 | [DeclStmt] var arr5 = ... | semmle.order | 25 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:42:3:42:19 | [ExprStmt] sink(arr5.pop()); | semmle.label | 26 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:42:3:42:19 | [ExprStmt] sink(arr5.pop()); | semmle.order | 26 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:44:3:44:28 | [ExprStmt] sink(ar ... pop()); | semmle.label | 27 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:44:3:44:28 | [ExprStmt] sink(ar ... pop()); | semmle.order | 27 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:46:3:46:16 | [DeclStmt] var arr6 = ... | semmle.label | 28 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:46:3:46:16 | [DeclStmt] var arr6 = ... | semmle.order | 28 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:47:3:49:3 | [ForStmt] for (va ... i]; } | semmle.label | 29 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:47:3:49:3 | [ForStmt] for (va ... i]; } | semmle.order | 29 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:50:3:50:19 | [ExprStmt] sink(arr6.pop()); | semmle.label | 30 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:50:3:50:19 | [ExprStmt] sink(arr6.pop()); | semmle.order | 30 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:53:3:56:5 | [ExprStmt] ["sourc ... . }); | semmle.label | 31 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:53:3:56:5 | [ExprStmt] ["sourc ... . }); | semmle.order | 31 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:58:3:58:15 | [ExprStmt] sink(arr[0]); | semmle.label | 32 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:58:3:58:15 | [ExprStmt] sink(arr[0]); | semmle.order | 32 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:60:3:62:3 | [ForOfStmt] for (co ... OK } | semmle.label | 33 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:60:3:62:3 | [ForOfStmt] for (co ... OK } | semmle.order | 33 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:64:3:66:3 | [ForOfStmt] for (co ... OK } | semmle.label | 34 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:64:3:66:3 | [ForOfStmt] for (co ... OK } | semmle.order | 34 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:68:3:70:3 | [ForOfStmt] for (co ... OK } | semmle.label | 35 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:68:3:70:3 | [ForOfStmt] for (co ... OK } | semmle.order | 35 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:72:3:72:16 | [DeclStmt] var arr7 = ... | semmle.label | 36 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:72:3:72:16 | [DeclStmt] var arr7 = ... | semmle.order | 36 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:73:3:73:20 | [ExprStmt] arr7.push(...arr); | semmle.label | 37 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:73:3:73:20 | [ExprStmt] arr7.push(...arr); | semmle.order | 37 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:74:3:76:3 | [ForOfStmt] for (co ... OK } | semmle.label | 38 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:74:3:76:3 | [ForOfStmt] for (co ... OK } | semmle.order | 38 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:78:3:78:42 | [DeclStmt] const arrayFrom = ... | semmle.label | 39 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:78:3:78:42 | [DeclStmt] const arrayFrom = ... | semmle.order | 39 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:79:3:81:3 | [ForOfStmt] for (co ... OK } | semmle.label | 40 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:79:3:81:3 | [ForOfStmt] for (co ... OK } | semmle.order | 40 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:83:3:83:31 | [ExprStmt] sink(ar ... back)); | semmle.label | 41 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:83:3:83:31 | [ExprStmt] sink(ar ... back)); | semmle.order | 41 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:85:3:85:42 | [DeclStmt] const arrayFind = ... | semmle.label | 42 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:85:3:85:42 | [DeclStmt] const arrayFind = ... | semmle.order | 42 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:86:3:86:37 | [ExprStmt] sink(ar ... back)); | semmle.label | 43 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:86:3:86:37 | [ExprStmt] sink(ar ... back)); | semmle.order | 43 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:88:3:88:31 | [DeclStmt] const uniq = ... | semmle.label | 44 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:88:3:88:31 | [DeclStmt] const uniq = ... | semmle.order | 44 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:89:3:91:3 | [ForOfStmt] for (co ... OK } | semmle.label | 45 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:89:3:91:3 | [ForOfStmt] for (co ... OK } | semmle.order | 45 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:93:3:93:19 | [ExprStmt] sink(arr.at(-1)); | semmle.label | 46 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:93:3:93:19 | [ExprStmt] sink(arr.at(-1)); | semmle.order | 46 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:95:3:95:36 | [ExprStmt] sink([" ... => x)); | semmle.label | 47 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:95:3:95:36 | [ExprStmt] sink([" ... => x)); | semmle.order | 47 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:96:3:96:38 | [ExprStmt] sink([" ... !!x)); | semmle.label | 48 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:96:3:96:38 | [ExprStmt] sink([" ... !!x)); | semmle.order | 48 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:98:3:98:16 | [DeclStmt] var arr8 = ... | semmle.label | 49 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:98:3:98:16 | [DeclStmt] var arr8 = ... | semmle.order | 49 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:99:3:99:40 | [ExprStmt] arr8 = ... urce"); | semmle.label | 50 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:99:3:99:40 | [ExprStmt] arr8 = ... urce"); | semmle.order | 50 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:100:3:100:19 | [ExprStmt] sink(arr8.pop()); | semmle.label | 51 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:100:3:100:19 | [ExprStmt] sink(arr8.pop()); | semmle.order | 51 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:102:3:102:24 | [DeclStmt] var arr8_variant = ... | semmle.label | 52 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:102:3:102:24 | [DeclStmt] var arr8_variant = ... | semmle.order | 52 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:103:3:103:64 | [ExprStmt] arr8_va ... urce"); | semmle.label | 53 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:103:3:103:64 | [ExprStmt] arr8_va ... urce"); | semmle.order | 53 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:104:3:104:21 | [ExprStmt] arr8_variant.pop(); | semmle.label | 54 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:104:3:104:21 | [ExprStmt] arr8_variant.pop(); | semmle.order | 54 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:105:3:105:27 | [ExprStmt] sink(ar ... pop()); | semmle.label | 55 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:105:3:105:27 | [ExprStmt] sink(ar ... pop()); | semmle.order | 55 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:107:3:107:23 | [DeclStmt] var arr8_spread = ... | semmle.label | 56 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:107:3:107:23 | [DeclStmt] var arr8_spread = ... | semmle.order | 56 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:108:3:108:52 | [ExprStmt] arr8_sp ... ..arr); | semmle.label | 57 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:108:3:108:52 | [ExprStmt] arr8_sp ... ..arr); | semmle.order | 57 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:109:3:109:26 | [ExprStmt] sink(ar ... pop()); | semmle.label | 58 |
-| arrays.js:1:14:110:1 | [BlockStmt] { let ... OT OK } | arrays.js:109:3:109:26 | [ExprStmt] sink(ar ... pop()); | semmle.order | 58 |
+| arrays.js:1:1:147:2 | [ParExpr] (functi ... } }) | arrays.js:1:2:147:1 | [FunctionExpr] functio ... K } } | semmle.label | 1 |
+| arrays.js:1:1:147:2 | [ParExpr] (functi ... } }) | arrays.js:1:2:147:1 | [FunctionExpr] functio ... K } } | semmle.order | 1 |
+| arrays.js:1:1:147:3 | [ExprStmt] (functi ... } }); | arrays.js:1:1:147:2 | [ParExpr] (functi ... } }) | semmle.label | 1 |
+| arrays.js:1:1:147:3 | [ExprStmt] (functi ... } }); | arrays.js:1:1:147:2 | [ParExpr] (functi ... } }) | semmle.order | 1 |
+| arrays.js:1:2:147:1 | [FunctionExpr] functio ... K } } | arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | semmle.label | 5 |
+| arrays.js:1:2:147:1 | [FunctionExpr] functio ... K } } | arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | semmle.order | 5 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.label | 1 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.order | 1 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.label | 2 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.order | 2 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.label | 3 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.order | 3 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.label | 4 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.order | 4 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.label | 5 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.order | 5 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.label | 6 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.order | 6 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.label | 7 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.order | 7 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.label | 8 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.order | 8 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.label | 9 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.order | 9 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.label | 10 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.order | 10 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.label | 11 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.order | 11 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.label | 12 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.order | 12 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.label | 13 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.order | 13 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.label | 14 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.order | 14 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.label | 15 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.order | 15 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.label | 16 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.order | 16 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.label | 17 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.order | 17 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:32:3:32:24 | [DeclStmt] var arr4_variant = ... | semmle.label | 18 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:32:3:32:24 | [DeclStmt] var arr4_variant = ... | semmle.order | 18 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:33:3:33:46 | [ExprStmt] arr4_va ... urce"); | semmle.label | 19 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:33:3:33:46 | [ExprStmt] arr4_va ... urce"); | semmle.order | 19 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:34:3:34:21 | [ExprStmt] arr4_variant.pop(); | semmle.label | 20 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:34:3:34:21 | [ExprStmt] arr4_variant.pop(); | semmle.order | 20 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:35:3:35:27 | [ExprStmt] sink(ar ... pop()); | semmle.label | 21 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:35:3:35:27 | [ExprStmt] sink(ar ... pop()); | semmle.order | 21 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:37:3:37:23 | [DeclStmt] var arr4_spread = ... | semmle.label | 22 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:37:3:37:23 | [DeclStmt] var arr4_spread = ... | semmle.order | 22 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:38:3:38:35 | [ExprStmt] arr4_sp ... ..arr); | semmle.label | 23 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:38:3:38:35 | [ExprStmt] arr4_sp ... ..arr); | semmle.order | 23 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:39:3:39:26 | [ExprStmt] sink(ar ... pop()); | semmle.label | 24 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:39:3:39:26 | [ExprStmt] sink(ar ... pop()); | semmle.order | 24 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:41:3:41:29 | [DeclStmt] var arr5 = ... | semmle.label | 25 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:41:3:41:29 | [DeclStmt] var arr5 = ... | semmle.order | 25 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:42:3:42:19 | [ExprStmt] sink(arr5.pop()); | semmle.label | 26 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:42:3:42:19 | [ExprStmt] sink(arr5.pop()); | semmle.order | 26 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:44:3:44:28 | [ExprStmt] sink(ar ... pop()); | semmle.label | 27 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:44:3:44:28 | [ExprStmt] sink(ar ... pop()); | semmle.order | 27 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:46:3:46:16 | [DeclStmt] var arr6 = ... | semmle.label | 28 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:46:3:46:16 | [DeclStmt] var arr6 = ... | semmle.order | 28 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:47:3:49:3 | [ForStmt] for (va ... i]; } | semmle.label | 29 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:47:3:49:3 | [ForStmt] for (va ... i]; } | semmle.order | 29 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:50:3:50:19 | [ExprStmt] sink(arr6.pop()); | semmle.label | 30 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:50:3:50:19 | [ExprStmt] sink(arr6.pop()); | semmle.order | 30 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:53:3:56:5 | [ExprStmt] ["sourc ... . }); | semmle.label | 31 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:53:3:56:5 | [ExprStmt] ["sourc ... . }); | semmle.order | 31 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:58:3:58:15 | [ExprStmt] sink(arr[0]); | semmle.label | 32 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:58:3:58:15 | [ExprStmt] sink(arr[0]); | semmle.order | 32 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:60:3:62:3 | [ForOfStmt] for (co ... OK } | semmle.label | 33 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:60:3:62:3 | [ForOfStmt] for (co ... OK } | semmle.order | 33 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:64:3:66:3 | [ForOfStmt] for (co ... OK } | semmle.label | 34 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:64:3:66:3 | [ForOfStmt] for (co ... OK } | semmle.order | 34 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:68:3:70:3 | [ForOfStmt] for (co ... OK } | semmle.label | 35 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:68:3:70:3 | [ForOfStmt] for (co ... OK } | semmle.order | 35 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:72:3:72:16 | [DeclStmt] var arr7 = ... | semmle.label | 36 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:72:3:72:16 | [DeclStmt] var arr7 = ... | semmle.order | 36 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:73:3:73:20 | [ExprStmt] arr7.push(...arr); | semmle.label | 37 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:73:3:73:20 | [ExprStmt] arr7.push(...arr); | semmle.order | 37 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:74:3:76:3 | [ForOfStmt] for (co ... OK } | semmle.label | 38 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:74:3:76:3 | [ForOfStmt] for (co ... OK } | semmle.order | 38 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:78:3:78:42 | [DeclStmt] const arrayFrom = ... | semmle.label | 39 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:78:3:78:42 | [DeclStmt] const arrayFrom = ... | semmle.order | 39 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:79:3:81:3 | [ForOfStmt] for (co ... OK } | semmle.label | 40 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:79:3:81:3 | [ForOfStmt] for (co ... OK } | semmle.order | 40 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:83:3:83:31 | [ExprStmt] sink(ar ... back)); | semmle.label | 41 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:83:3:83:31 | [ExprStmt] sink(ar ... back)); | semmle.order | 41 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:85:3:85:42 | [DeclStmt] const arrayFind = ... | semmle.label | 42 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:85:3:85:42 | [DeclStmt] const arrayFind = ... | semmle.order | 42 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:86:3:86:37 | [ExprStmt] sink(ar ... back)); | semmle.label | 43 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:86:3:86:37 | [ExprStmt] sink(ar ... back)); | semmle.order | 43 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:88:3:88:31 | [DeclStmt] const uniq = ... | semmle.label | 44 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:88:3:88:31 | [DeclStmt] const uniq = ... | semmle.order | 44 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:89:3:91:3 | [ForOfStmt] for (co ... OK } | semmle.label | 45 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:89:3:91:3 | [ForOfStmt] for (co ... OK } | semmle.order | 45 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:93:3:93:19 | [ExprStmt] sink(arr.at(-1)); | semmle.label | 46 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:93:3:93:19 | [ExprStmt] sink(arr.at(-1)); | semmle.order | 46 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:95:3:95:36 | [ExprStmt] sink([" ... => x)); | semmle.label | 47 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:95:3:95:36 | [ExprStmt] sink([" ... => x)); | semmle.order | 47 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:96:3:96:38 | [ExprStmt] sink([" ... !!x)); | semmle.label | 48 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:96:3:96:38 | [ExprStmt] sink([" ... !!x)); | semmle.order | 48 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:98:3:98:16 | [DeclStmt] var arr8 = ... | semmle.label | 49 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:98:3:98:16 | [DeclStmt] var arr8 = ... | semmle.order | 49 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:99:3:99:40 | [ExprStmt] arr8 = ... urce"); | semmle.label | 50 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:99:3:99:40 | [ExprStmt] arr8 = ... urce"); | semmle.order | 50 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:100:3:100:19 | [ExprStmt] sink(arr8.pop()); | semmle.label | 51 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:100:3:100:19 | [ExprStmt] sink(arr8.pop()); | semmle.order | 51 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:102:3:102:24 | [DeclStmt] var arr8_variant = ... | semmle.label | 52 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:102:3:102:24 | [DeclStmt] var arr8_variant = ... | semmle.order | 52 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:103:3:103:64 | [ExprStmt] arr8_va ... urce"); | semmle.label | 53 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:103:3:103:64 | [ExprStmt] arr8_va ... urce"); | semmle.order | 53 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:104:3:104:21 | [ExprStmt] arr8_variant.pop(); | semmle.label | 54 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:104:3:104:21 | [ExprStmt] arr8_variant.pop(); | semmle.order | 54 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:105:3:105:27 | [ExprStmt] sink(ar ... pop()); | semmle.label | 55 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:105:3:105:27 | [ExprStmt] sink(ar ... pop()); | semmle.order | 55 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:107:3:107:23 | [DeclStmt] var arr8_spread = ... | semmle.label | 56 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:107:3:107:23 | [DeclStmt] var arr8_spread = ... | semmle.order | 56 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:108:3:108:52 | [ExprStmt] arr8_sp ... ..arr); | semmle.label | 57 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:108:3:108:52 | [ExprStmt] arr8_sp ... ..arr); | semmle.order | 57 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:109:3:109:26 | [ExprStmt] sink(ar ... pop()); | semmle.label | 58 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:109:3:109:26 | [ExprStmt] sink(ar ... pop()); | semmle.order | 58 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:111:3:111:35 | [ExprStmt] sink(ar ... back)); | semmle.label | 59 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:111:3:111:35 | [ExprStmt] sink(ar ... back)); | semmle.order | 59 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:113:3:117:3 | [BlockStmt] { // T ... OK } | semmle.label | 60 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:113:3:117:3 | [BlockStmt] { // T ... OK } | semmle.order | 60 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:119:3:123:3 | [BlockStmt] { // T ... OK } | semmle.label | 61 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:119:3:123:3 | [BlockStmt] { // T ... OK } | semmle.order | 61 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:125:3:129:3 | [BlockStmt] { // T ... OK } | semmle.label | 62 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:125:3:129:3 | [BlockStmt] { // T ... OK } | semmle.order | 62 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:130:3:134:3 | [BlockStmt] { c ... OK } | semmle.label | 63 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:130:3:134:3 | [BlockStmt] { c ... OK } | semmle.order | 63 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:136:3:140:3 | [BlockStmt] { c ... OK } | semmle.label | 64 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:136:3:140:3 | [BlockStmt] { c ... OK } | semmle.order | 64 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:142:3:146:3 | [BlockStmt] { c ... OK } | semmle.label | 65 |
+| arrays.js:1:14:147:1 | [BlockStmt] { let ... K } } | arrays.js:142:3:146:3 | [BlockStmt] { c ... OK } | semmle.order | 65 |
| arrays.js:2:3:2:24 | [DeclStmt] let source = ... | arrays.js:2:7:2:23 | [VariableDeclarator] source = "source" | semmle.label | 1 |
| arrays.js:2:3:2:24 | [DeclStmt] let source = ... | arrays.js:2:7:2:23 | [VariableDeclarator] source = "source" | semmle.order | 1 |
| arrays.js:2:7:2:23 | [VariableDeclarator] source = "source" | arrays.js:2:7:2:12 | [VarDecl] source | semmle.label | 1 |
@@ -1490,6 +1670,272 @@ edges
| arrays.js:109:8:109:22 | [DotExpr] arr8_spread.pop | arrays.js:109:20:109:22 | [Label] pop | semmle.order | 2 |
| arrays.js:109:8:109:24 | [MethodCallExpr] arr8_spread.pop() | arrays.js:109:8:109:22 | [DotExpr] arr8_spread.pop | semmle.label | 0 |
| arrays.js:109:8:109:24 | [MethodCallExpr] arr8_spread.pop() | arrays.js:109:8:109:22 | [DotExpr] arr8_spread.pop | semmle.order | 0 |
+| arrays.js:111:3:111:34 | [CallExpr] sink(ar ... lback)) | arrays.js:111:3:111:6 | [VarRef] sink | semmle.label | 0 |
+| arrays.js:111:3:111:34 | [CallExpr] sink(ar ... lback)) | arrays.js:111:3:111:6 | [VarRef] sink | semmle.order | 0 |
+| arrays.js:111:3:111:34 | [CallExpr] sink(ar ... lback)) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:111:3:111:34 | [CallExpr] sink(ar ... lback)) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:111:3:111:35 | [ExprStmt] sink(ar ... back)); | arrays.js:111:3:111:34 | [CallExpr] sink(ar ... lback)) | semmle.label | 1 |
+| arrays.js:111:3:111:35 | [ExprStmt] sink(ar ... back)); | arrays.js:111:3:111:34 | [CallExpr] sink(ar ... lback)) | semmle.order | 1 |
+| arrays.js:111:8:111:19 | [DotExpr] arr.findLast | arrays.js:111:8:111:10 | [VarRef] arr | semmle.label | 1 |
+| arrays.js:111:8:111:19 | [DotExpr] arr.findLast | arrays.js:111:8:111:10 | [VarRef] arr | semmle.order | 1 |
+| arrays.js:111:8:111:19 | [DotExpr] arr.findLast | arrays.js:111:12:111:19 | [Label] findLast | semmle.label | 2 |
+| arrays.js:111:8:111:19 | [DotExpr] arr.findLast | arrays.js:111:12:111:19 | [Label] findLast | semmle.order | 2 |
+| arrays.js:111:8:111:33 | [MethodCallExpr] arr.fin ... llback) | arrays.js:111:8:111:19 | [DotExpr] arr.findLast | semmle.label | 0 |
+| arrays.js:111:8:111:33 | [MethodCallExpr] arr.fin ... llback) | arrays.js:111:8:111:19 | [DotExpr] arr.findLast | semmle.order | 0 |
+| arrays.js:111:8:111:33 | [MethodCallExpr] arr.fin ... llback) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:111:8:111:33 | [MethodCallExpr] arr.fin ... llback) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:113:3:117:3 | [BlockStmt] { // T ... OK } | arrays.js:114:5:114:28 | [DeclStmt] const list = ... | semmle.label | 1 |
+| arrays.js:113:3:117:3 | [BlockStmt] { // T ... OK } | arrays.js:114:5:114:28 | [DeclStmt] const list = ... | semmle.order | 1 |
+| arrays.js:113:3:117:3 | [BlockStmt] { // T ... OK } | arrays.js:115:5:115:56 | [DeclStmt] const element = ... | semmle.label | 2 |
+| arrays.js:113:3:117:3 | [BlockStmt] { // T ... OK } | arrays.js:115:5:115:56 | [DeclStmt] const element = ... | semmle.order | 2 |
+| arrays.js:113:3:117:3 | [BlockStmt] { // T ... OK } | arrays.js:116:5:116:18 | [ExprStmt] sink(element); | semmle.label | 3 |
+| arrays.js:113:3:117:3 | [BlockStmt] { // T ... OK } | arrays.js:116:5:116:18 | [ExprStmt] sink(element); | semmle.order | 3 |
+| arrays.js:114:5:114:28 | [DeclStmt] const list = ... | arrays.js:114:11:114:27 | [VariableDeclarator] list = ["source"] | semmle.label | 1 |
+| arrays.js:114:5:114:28 | [DeclStmt] const list = ... | arrays.js:114:11:114:27 | [VariableDeclarator] list = ["source"] | semmle.order | 1 |
+| arrays.js:114:11:114:27 | [VariableDeclarator] list = ["source"] | arrays.js:114:11:114:14 | [VarDecl] list | semmle.label | 1 |
+| arrays.js:114:11:114:27 | [VariableDeclarator] list = ["source"] | arrays.js:114:11:114:14 | [VarDecl] list | semmle.order | 1 |
+| arrays.js:114:11:114:27 | [VariableDeclarator] list = ["source"] | arrays.js:114:18:114:27 | [ArrayExpr] ["source"] | semmle.label | 2 |
+| arrays.js:114:11:114:27 | [VariableDeclarator] list = ["source"] | arrays.js:114:18:114:27 | [ArrayExpr] ["source"] | semmle.order | 2 |
+| arrays.js:114:18:114:27 | [ArrayExpr] ["source"] | arrays.js:114:19:114:26 | [Literal] "source" | semmle.label | 1 |
+| arrays.js:114:18:114:27 | [ArrayExpr] ["source"] | arrays.js:114:19:114:26 | [Literal] "source" | semmle.order | 1 |
+| arrays.js:115:5:115:56 | [DeclStmt] const element = ... | arrays.js:115:11:115:55 | [VariableDeclarator] element ... (item)) | semmle.label | 1 |
+| arrays.js:115:5:115:56 | [DeclStmt] const element = ... | arrays.js:115:11:115:55 | [VariableDeclarator] element ... (item)) | semmle.order | 1 |
+| arrays.js:115:11:115:55 | [VariableDeclarator] element ... (item)) | arrays.js:115:11:115:17 | [VarDecl] element | semmle.label | 1 |
+| arrays.js:115:11:115:55 | [VariableDeclarator] element ... (item)) | arrays.js:115:11:115:17 | [VarDecl] element | semmle.order | 1 |
+| arrays.js:115:11:115:55 | [VariableDeclarator] element ... (item)) | arrays.js:115:21:115:55 | [MethodCallExpr] list.fi ... (item)) | semmle.label | 2 |
+| arrays.js:115:11:115:55 | [VariableDeclarator] element ... (item)) | arrays.js:115:21:115:55 | [MethodCallExpr] list.fi ... (item)) | semmle.order | 2 |
+| arrays.js:115:21:115:33 | [DotExpr] list.findLast | arrays.js:115:21:115:24 | [VarRef] list | semmle.label | 1 |
+| arrays.js:115:21:115:33 | [DotExpr] list.findLast | arrays.js:115:21:115:24 | [VarRef] list | semmle.order | 1 |
+| arrays.js:115:21:115:33 | [DotExpr] list.findLast | arrays.js:115:26:115:33 | [Label] findLast | semmle.label | 2 |
+| arrays.js:115:21:115:33 | [DotExpr] list.findLast | arrays.js:115:26:115:33 | [Label] findLast | semmle.order | 2 |
+| arrays.js:115:21:115:55 | [MethodCallExpr] list.fi ... (item)) | arrays.js:115:21:115:33 | [DotExpr] list.findLast | semmle.label | 0 |
+| arrays.js:115:21:115:55 | [MethodCallExpr] list.fi ... (item)) | arrays.js:115:21:115:33 | [DotExpr] list.findLast | semmle.order | 0 |
+| arrays.js:115:21:115:55 | [MethodCallExpr] list.fi ... (item)) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:115:21:115:55 | [MethodCallExpr] list.fi ... (item)) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:115:35:115:54 | [ArrowFunctionExpr] (item) => sink(item) | arrays.js:115:45:115:54 | [CallExpr] sink(item) | semmle.label | 5 |
+| arrays.js:115:35:115:54 | [ArrowFunctionExpr] (item) => sink(item) | arrays.js:115:45:115:54 | [CallExpr] sink(item) | semmle.order | 5 |
+| arrays.js:115:35:115:54 | [ArrowFunctionExpr] (item) => sink(item) | file://:0:0:0:0 | (Parameters) | semmle.label | 1 |
+| arrays.js:115:35:115:54 | [ArrowFunctionExpr] (item) => sink(item) | file://:0:0:0:0 | (Parameters) | semmle.order | 1 |
+| arrays.js:115:45:115:54 | [CallExpr] sink(item) | arrays.js:115:45:115:48 | [VarRef] sink | semmle.label | 0 |
+| arrays.js:115:45:115:54 | [CallExpr] sink(item) | arrays.js:115:45:115:48 | [VarRef] sink | semmle.order | 0 |
+| arrays.js:115:45:115:54 | [CallExpr] sink(item) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:115:45:115:54 | [CallExpr] sink(item) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:116:5:116:17 | [CallExpr] sink(element) | arrays.js:116:5:116:8 | [VarRef] sink | semmle.label | 0 |
+| arrays.js:116:5:116:17 | [CallExpr] sink(element) | arrays.js:116:5:116:8 | [VarRef] sink | semmle.order | 0 |
+| arrays.js:116:5:116:17 | [CallExpr] sink(element) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:116:5:116:17 | [CallExpr] sink(element) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:116:5:116:18 | [ExprStmt] sink(element); | arrays.js:116:5:116:17 | [CallExpr] sink(element) | semmle.label | 1 |
+| arrays.js:116:5:116:18 | [ExprStmt] sink(element); | arrays.js:116:5:116:17 | [CallExpr] sink(element) | semmle.order | 1 |
+| arrays.js:119:3:123:3 | [BlockStmt] { // T ... OK } | arrays.js:120:5:120:28 | [DeclStmt] const list = ... | semmle.label | 1 |
+| arrays.js:119:3:123:3 | [BlockStmt] { // T ... OK } | arrays.js:120:5:120:28 | [DeclStmt] const list = ... | semmle.order | 1 |
+| arrays.js:119:3:123:3 | [BlockStmt] { // T ... OK } | arrays.js:121:5:121:52 | [DeclStmt] const element = ... | semmle.label | 2 |
+| arrays.js:119:3:123:3 | [BlockStmt] { // T ... OK } | arrays.js:121:5:121:52 | [DeclStmt] const element = ... | semmle.order | 2 |
+| arrays.js:119:3:123:3 | [BlockStmt] { // T ... OK } | arrays.js:122:5:122:18 | [ExprStmt] sink(element); | semmle.label | 3 |
+| arrays.js:119:3:123:3 | [BlockStmt] { // T ... OK } | arrays.js:122:5:122:18 | [ExprStmt] sink(element); | semmle.order | 3 |
+| arrays.js:120:5:120:28 | [DeclStmt] const list = ... | arrays.js:120:11:120:27 | [VariableDeclarator] list = ["source"] | semmle.label | 1 |
+| arrays.js:120:5:120:28 | [DeclStmt] const list = ... | arrays.js:120:11:120:27 | [VariableDeclarator] list = ["source"] | semmle.order | 1 |
+| arrays.js:120:11:120:27 | [VariableDeclarator] list = ["source"] | arrays.js:120:11:120:14 | [VarDecl] list | semmle.label | 1 |
+| arrays.js:120:11:120:27 | [VariableDeclarator] list = ["source"] | arrays.js:120:11:120:14 | [VarDecl] list | semmle.order | 1 |
+| arrays.js:120:11:120:27 | [VariableDeclarator] list = ["source"] | arrays.js:120:18:120:27 | [ArrayExpr] ["source"] | semmle.label | 2 |
+| arrays.js:120:11:120:27 | [VariableDeclarator] list = ["source"] | arrays.js:120:18:120:27 | [ArrayExpr] ["source"] | semmle.order | 2 |
+| arrays.js:120:18:120:27 | [ArrayExpr] ["source"] | arrays.js:120:19:120:26 | [Literal] "source" | semmle.label | 1 |
+| arrays.js:120:18:120:27 | [ArrayExpr] ["source"] | arrays.js:120:19:120:26 | [Literal] "source" | semmle.order | 1 |
+| arrays.js:121:5:121:52 | [DeclStmt] const element = ... | arrays.js:121:11:121:51 | [VariableDeclarator] element ... (item)) | semmle.label | 1 |
+| arrays.js:121:5:121:52 | [DeclStmt] const element = ... | arrays.js:121:11:121:51 | [VariableDeclarator] element ... (item)) | semmle.order | 1 |
+| arrays.js:121:11:121:51 | [VariableDeclarator] element ... (item)) | arrays.js:121:11:121:17 | [VarDecl] element | semmle.label | 1 |
+| arrays.js:121:11:121:51 | [VariableDeclarator] element ... (item)) | arrays.js:121:11:121:17 | [VarDecl] element | semmle.order | 1 |
+| arrays.js:121:11:121:51 | [VariableDeclarator] element ... (item)) | arrays.js:121:21:121:51 | [MethodCallExpr] list.fi ... (item)) | semmle.label | 2 |
+| arrays.js:121:11:121:51 | [VariableDeclarator] element ... (item)) | arrays.js:121:21:121:51 | [MethodCallExpr] list.fi ... (item)) | semmle.order | 2 |
+| arrays.js:121:21:121:29 | [DotExpr] list.find | arrays.js:121:21:121:24 | [VarRef] list | semmle.label | 1 |
+| arrays.js:121:21:121:29 | [DotExpr] list.find | arrays.js:121:21:121:24 | [VarRef] list | semmle.order | 1 |
+| arrays.js:121:21:121:29 | [DotExpr] list.find | arrays.js:121:26:121:29 | [Label] find | semmle.label | 2 |
+| arrays.js:121:21:121:29 | [DotExpr] list.find | arrays.js:121:26:121:29 | [Label] find | semmle.order | 2 |
+| arrays.js:121:21:121:51 | [MethodCallExpr] list.fi ... (item)) | arrays.js:121:21:121:29 | [DotExpr] list.find | semmle.label | 0 |
+| arrays.js:121:21:121:51 | [MethodCallExpr] list.fi ... (item)) | arrays.js:121:21:121:29 | [DotExpr] list.find | semmle.order | 0 |
+| arrays.js:121:21:121:51 | [MethodCallExpr] list.fi ... (item)) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:121:21:121:51 | [MethodCallExpr] list.fi ... (item)) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:121:31:121:50 | [ArrowFunctionExpr] (item) => sink(item) | arrays.js:121:41:121:50 | [CallExpr] sink(item) | semmle.label | 5 |
+| arrays.js:121:31:121:50 | [ArrowFunctionExpr] (item) => sink(item) | arrays.js:121:41:121:50 | [CallExpr] sink(item) | semmle.order | 5 |
+| arrays.js:121:31:121:50 | [ArrowFunctionExpr] (item) => sink(item) | file://:0:0:0:0 | (Parameters) | semmle.label | 1 |
+| arrays.js:121:31:121:50 | [ArrowFunctionExpr] (item) => sink(item) | file://:0:0:0:0 | (Parameters) | semmle.order | 1 |
+| arrays.js:121:41:121:50 | [CallExpr] sink(item) | arrays.js:121:41:121:44 | [VarRef] sink | semmle.label | 0 |
+| arrays.js:121:41:121:50 | [CallExpr] sink(item) | arrays.js:121:41:121:44 | [VarRef] sink | semmle.order | 0 |
+| arrays.js:121:41:121:50 | [CallExpr] sink(item) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:121:41:121:50 | [CallExpr] sink(item) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:122:5:122:17 | [CallExpr] sink(element) | arrays.js:122:5:122:8 | [VarRef] sink | semmle.label | 0 |
+| arrays.js:122:5:122:17 | [CallExpr] sink(element) | arrays.js:122:5:122:8 | [VarRef] sink | semmle.order | 0 |
+| arrays.js:122:5:122:17 | [CallExpr] sink(element) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:122:5:122:17 | [CallExpr] sink(element) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:122:5:122:18 | [ExprStmt] sink(element); | arrays.js:122:5:122:17 | [CallExpr] sink(element) | semmle.label | 1 |
+| arrays.js:122:5:122:18 | [ExprStmt] sink(element); | arrays.js:122:5:122:17 | [CallExpr] sink(element) | semmle.order | 1 |
+| arrays.js:125:3:129:3 | [BlockStmt] { // T ... OK } | arrays.js:126:5:126:28 | [DeclStmt] const list = ... | semmle.label | 1 |
+| arrays.js:125:3:129:3 | [BlockStmt] { // T ... OK } | arrays.js:126:5:126:28 | [DeclStmt] const list = ... | semmle.order | 1 |
+| arrays.js:125:3:129:3 | [BlockStmt] { // T ... OK } | arrays.js:127:5:127:61 | [DeclStmt] const element = ... | semmle.label | 2 |
+| arrays.js:125:3:129:3 | [BlockStmt] { // T ... OK } | arrays.js:127:5:127:61 | [DeclStmt] const element = ... | semmle.order | 2 |
+| arrays.js:125:3:129:3 | [BlockStmt] { // T ... OK } | arrays.js:128:5:128:18 | [ExprStmt] sink(element); | semmle.label | 3 |
+| arrays.js:125:3:129:3 | [BlockStmt] { // T ... OK } | arrays.js:128:5:128:18 | [ExprStmt] sink(element); | semmle.order | 3 |
+| arrays.js:126:5:126:28 | [DeclStmt] const list = ... | arrays.js:126:11:126:27 | [VariableDeclarator] list = ["source"] | semmle.label | 1 |
+| arrays.js:126:5:126:28 | [DeclStmt] const list = ... | arrays.js:126:11:126:27 | [VariableDeclarator] list = ["source"] | semmle.order | 1 |
+| arrays.js:126:11:126:27 | [VariableDeclarator] list = ["source"] | arrays.js:126:11:126:14 | [VarDecl] list | semmle.label | 1 |
+| arrays.js:126:11:126:27 | [VariableDeclarator] list = ["source"] | arrays.js:126:11:126:14 | [VarDecl] list | semmle.order | 1 |
+| arrays.js:126:11:126:27 | [VariableDeclarator] list = ["source"] | arrays.js:126:18:126:27 | [ArrayExpr] ["source"] | semmle.label | 2 |
+| arrays.js:126:11:126:27 | [VariableDeclarator] list = ["source"] | arrays.js:126:18:126:27 | [ArrayExpr] ["source"] | semmle.order | 2 |
+| arrays.js:126:18:126:27 | [ArrayExpr] ["source"] | arrays.js:126:19:126:26 | [Literal] "source" | semmle.label | 1 |
+| arrays.js:126:18:126:27 | [ArrayExpr] ["source"] | arrays.js:126:19:126:26 | [Literal] "source" | semmle.order | 1 |
+| arrays.js:127:5:127:61 | [DeclStmt] const element = ... | arrays.js:127:11:127:60 | [VariableDeclarator] element ... (item)) | semmle.label | 1 |
+| arrays.js:127:5:127:61 | [DeclStmt] const element = ... | arrays.js:127:11:127:60 | [VariableDeclarator] element ... (item)) | semmle.order | 1 |
+| arrays.js:127:11:127:60 | [VariableDeclarator] element ... (item)) | arrays.js:127:11:127:17 | [VarDecl] element | semmle.label | 1 |
+| arrays.js:127:11:127:60 | [VariableDeclarator] element ... (item)) | arrays.js:127:11:127:17 | [VarDecl] element | semmle.order | 1 |
+| arrays.js:127:11:127:60 | [VariableDeclarator] element ... (item)) | arrays.js:127:21:127:60 | [MethodCallExpr] list.fi ... (item)) | semmle.label | 2 |
+| arrays.js:127:11:127:60 | [VariableDeclarator] element ... (item)) | arrays.js:127:21:127:60 | [MethodCallExpr] list.fi ... (item)) | semmle.order | 2 |
+| arrays.js:127:21:127:38 | [DotExpr] list.findLastIndex | arrays.js:127:21:127:24 | [VarRef] list | semmle.label | 1 |
+| arrays.js:127:21:127:38 | [DotExpr] list.findLastIndex | arrays.js:127:21:127:24 | [VarRef] list | semmle.order | 1 |
+| arrays.js:127:21:127:38 | [DotExpr] list.findLastIndex | arrays.js:127:26:127:38 | [Label] findLastIndex | semmle.label | 2 |
+| arrays.js:127:21:127:38 | [DotExpr] list.findLastIndex | arrays.js:127:26:127:38 | [Label] findLastIndex | semmle.order | 2 |
+| arrays.js:127:21:127:60 | [MethodCallExpr] list.fi ... (item)) | arrays.js:127:21:127:38 | [DotExpr] list.findLastIndex | semmle.label | 0 |
+| arrays.js:127:21:127:60 | [MethodCallExpr] list.fi ... (item)) | arrays.js:127:21:127:38 | [DotExpr] list.findLastIndex | semmle.order | 0 |
+| arrays.js:127:21:127:60 | [MethodCallExpr] list.fi ... (item)) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:127:21:127:60 | [MethodCallExpr] list.fi ... (item)) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:127:40:127:59 | [ArrowFunctionExpr] (item) => sink(item) | arrays.js:127:50:127:59 | [CallExpr] sink(item) | semmle.label | 5 |
+| arrays.js:127:40:127:59 | [ArrowFunctionExpr] (item) => sink(item) | arrays.js:127:50:127:59 | [CallExpr] sink(item) | semmle.order | 5 |
+| arrays.js:127:40:127:59 | [ArrowFunctionExpr] (item) => sink(item) | file://:0:0:0:0 | (Parameters) | semmle.label | 1 |
+| arrays.js:127:40:127:59 | [ArrowFunctionExpr] (item) => sink(item) | file://:0:0:0:0 | (Parameters) | semmle.order | 1 |
+| arrays.js:127:50:127:59 | [CallExpr] sink(item) | arrays.js:127:50:127:53 | [VarRef] sink | semmle.label | 0 |
+| arrays.js:127:50:127:59 | [CallExpr] sink(item) | arrays.js:127:50:127:53 | [VarRef] sink | semmle.order | 0 |
+| arrays.js:127:50:127:59 | [CallExpr] sink(item) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:127:50:127:59 | [CallExpr] sink(item) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:128:5:128:17 | [CallExpr] sink(element) | arrays.js:128:5:128:8 | [VarRef] sink | semmle.label | 0 |
+| arrays.js:128:5:128:17 | [CallExpr] sink(element) | arrays.js:128:5:128:8 | [VarRef] sink | semmle.order | 0 |
+| arrays.js:128:5:128:17 | [CallExpr] sink(element) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:128:5:128:17 | [CallExpr] sink(element) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:128:5:128:18 | [ExprStmt] sink(element); | arrays.js:128:5:128:17 | [CallExpr] sink(element) | semmle.label | 1 |
+| arrays.js:128:5:128:18 | [ExprStmt] sink(element); | arrays.js:128:5:128:17 | [CallExpr] sink(element) | semmle.order | 1 |
+| arrays.js:130:3:134:3 | [BlockStmt] { c ... OK } | arrays.js:131:5:131:25 | [DeclStmt] const arr = ... | semmle.label | 1 |
+| arrays.js:130:3:134:3 | [BlockStmt] { c ... OK } | arrays.js:131:5:131:25 | [DeclStmt] const arr = ... | semmle.order | 1 |
+| arrays.js:130:3:134:3 | [BlockStmt] { c ... OK } | arrays.js:132:5:132:52 | [DeclStmt] const element1 = ... | semmle.label | 2 |
+| arrays.js:130:3:134:3 | [BlockStmt] { c ... OK } | arrays.js:132:5:132:52 | [DeclStmt] const element1 = ... | semmle.order | 2 |
+| arrays.js:130:3:134:3 | [BlockStmt] { c ... OK } | arrays.js:133:5:133:19 | [ExprStmt] sink(element1); | semmle.label | 3 |
+| arrays.js:130:3:134:3 | [BlockStmt] { c ... OK } | arrays.js:133:5:133:19 | [ExprStmt] sink(element1); | semmle.order | 3 |
+| arrays.js:131:5:131:25 | [DeclStmt] const arr = ... | arrays.js:131:11:131:24 | [VariableDeclarator] arr = source() | semmle.label | 1 |
+| arrays.js:131:5:131:25 | [DeclStmt] const arr = ... | arrays.js:131:11:131:24 | [VariableDeclarator] arr = source() | semmle.order | 1 |
+| arrays.js:131:11:131:24 | [VariableDeclarator] arr = source() | arrays.js:131:11:131:13 | [VarDecl] arr | semmle.label | 1 |
+| arrays.js:131:11:131:24 | [VariableDeclarator] arr = source() | arrays.js:131:11:131:13 | [VarDecl] arr | semmle.order | 1 |
+| arrays.js:131:11:131:24 | [VariableDeclarator] arr = source() | arrays.js:131:17:131:24 | [CallExpr] source() | semmle.label | 2 |
+| arrays.js:131:11:131:24 | [VariableDeclarator] arr = source() | arrays.js:131:17:131:24 | [CallExpr] source() | semmle.order | 2 |
+| arrays.js:131:17:131:24 | [CallExpr] source() | arrays.js:131:17:131:22 | [VarRef] source | semmle.label | 0 |
+| arrays.js:131:17:131:24 | [CallExpr] source() | arrays.js:131:17:131:22 | [VarRef] source | semmle.order | 0 |
+| arrays.js:132:5:132:52 | [DeclStmt] const element1 = ... | arrays.js:132:11:132:51 | [VariableDeclarator] element ... (item)) | semmle.label | 1 |
+| arrays.js:132:5:132:52 | [DeclStmt] const element1 = ... | arrays.js:132:11:132:51 | [VariableDeclarator] element ... (item)) | semmle.order | 1 |
+| arrays.js:132:11:132:51 | [VariableDeclarator] element ... (item)) | arrays.js:132:11:132:18 | [VarDecl] element1 | semmle.label | 1 |
+| arrays.js:132:11:132:51 | [VariableDeclarator] element ... (item)) | arrays.js:132:11:132:18 | [VarDecl] element1 | semmle.order | 1 |
+| arrays.js:132:11:132:51 | [VariableDeclarator] element ... (item)) | arrays.js:132:22:132:51 | [MethodCallExpr] arr.fin ... (item)) | semmle.label | 2 |
+| arrays.js:132:11:132:51 | [VariableDeclarator] element ... (item)) | arrays.js:132:22:132:51 | [MethodCallExpr] arr.fin ... (item)) | semmle.order | 2 |
+| arrays.js:132:22:132:29 | [DotExpr] arr.find | arrays.js:132:22:132:24 | [VarRef] arr | semmle.label | 1 |
+| arrays.js:132:22:132:29 | [DotExpr] arr.find | arrays.js:132:22:132:24 | [VarRef] arr | semmle.order | 1 |
+| arrays.js:132:22:132:29 | [DotExpr] arr.find | arrays.js:132:26:132:29 | [Label] find | semmle.label | 2 |
+| arrays.js:132:22:132:29 | [DotExpr] arr.find | arrays.js:132:26:132:29 | [Label] find | semmle.order | 2 |
+| arrays.js:132:22:132:51 | [MethodCallExpr] arr.fin ... (item)) | arrays.js:132:22:132:29 | [DotExpr] arr.find | semmle.label | 0 |
+| arrays.js:132:22:132:51 | [MethodCallExpr] arr.fin ... (item)) | arrays.js:132:22:132:29 | [DotExpr] arr.find | semmle.order | 0 |
+| arrays.js:132:22:132:51 | [MethodCallExpr] arr.fin ... (item)) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:132:22:132:51 | [MethodCallExpr] arr.fin ... (item)) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:132:31:132:50 | [ArrowFunctionExpr] (item) => sink(item) | arrays.js:132:41:132:50 | [CallExpr] sink(item) | semmle.label | 5 |
+| arrays.js:132:31:132:50 | [ArrowFunctionExpr] (item) => sink(item) | arrays.js:132:41:132:50 | [CallExpr] sink(item) | semmle.order | 5 |
+| arrays.js:132:31:132:50 | [ArrowFunctionExpr] (item) => sink(item) | file://:0:0:0:0 | (Parameters) | semmle.label | 1 |
+| arrays.js:132:31:132:50 | [ArrowFunctionExpr] (item) => sink(item) | file://:0:0:0:0 | (Parameters) | semmle.order | 1 |
+| arrays.js:132:41:132:50 | [CallExpr] sink(item) | arrays.js:132:41:132:44 | [VarRef] sink | semmle.label | 0 |
+| arrays.js:132:41:132:50 | [CallExpr] sink(item) | arrays.js:132:41:132:44 | [VarRef] sink | semmle.order | 0 |
+| arrays.js:132:41:132:50 | [CallExpr] sink(item) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:132:41:132:50 | [CallExpr] sink(item) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:133:5:133:18 | [CallExpr] sink(element1) | arrays.js:133:5:133:8 | [VarRef] sink | semmle.label | 0 |
+| arrays.js:133:5:133:18 | [CallExpr] sink(element1) | arrays.js:133:5:133:8 | [VarRef] sink | semmle.order | 0 |
+| arrays.js:133:5:133:18 | [CallExpr] sink(element1) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:133:5:133:18 | [CallExpr] sink(element1) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:133:5:133:19 | [ExprStmt] sink(element1); | arrays.js:133:5:133:18 | [CallExpr] sink(element1) | semmle.label | 1 |
+| arrays.js:133:5:133:19 | [ExprStmt] sink(element1); | arrays.js:133:5:133:18 | [CallExpr] sink(element1) | semmle.order | 1 |
+| arrays.js:136:3:140:3 | [BlockStmt] { c ... OK } | arrays.js:137:5:137:25 | [DeclStmt] const arr = ... | semmle.label | 1 |
+| arrays.js:136:3:140:3 | [BlockStmt] { c ... OK } | arrays.js:137:5:137:25 | [DeclStmt] const arr = ... | semmle.order | 1 |
+| arrays.js:136:3:140:3 | [BlockStmt] { c ... OK } | arrays.js:138:5:138:56 | [DeclStmt] const element1 = ... | semmle.label | 2 |
+| arrays.js:136:3:140:3 | [BlockStmt] { c ... OK } | arrays.js:138:5:138:56 | [DeclStmt] const element1 = ... | semmle.order | 2 |
+| arrays.js:136:3:140:3 | [BlockStmt] { c ... OK } | arrays.js:139:5:139:19 | [ExprStmt] sink(element1); | semmle.label | 3 |
+| arrays.js:136:3:140:3 | [BlockStmt] { c ... OK } | arrays.js:139:5:139:19 | [ExprStmt] sink(element1); | semmle.order | 3 |
+| arrays.js:137:5:137:25 | [DeclStmt] const arr = ... | arrays.js:137:11:137:24 | [VariableDeclarator] arr = source() | semmle.label | 1 |
+| arrays.js:137:5:137:25 | [DeclStmt] const arr = ... | arrays.js:137:11:137:24 | [VariableDeclarator] arr = source() | semmle.order | 1 |
+| arrays.js:137:11:137:24 | [VariableDeclarator] arr = source() | arrays.js:137:11:137:13 | [VarDecl] arr | semmle.label | 1 |
+| arrays.js:137:11:137:24 | [VariableDeclarator] arr = source() | arrays.js:137:11:137:13 | [VarDecl] arr | semmle.order | 1 |
+| arrays.js:137:11:137:24 | [VariableDeclarator] arr = source() | arrays.js:137:17:137:24 | [CallExpr] source() | semmle.label | 2 |
+| arrays.js:137:11:137:24 | [VariableDeclarator] arr = source() | arrays.js:137:17:137:24 | [CallExpr] source() | semmle.order | 2 |
+| arrays.js:137:17:137:24 | [CallExpr] source() | arrays.js:137:17:137:22 | [VarRef] source | semmle.label | 0 |
+| arrays.js:137:17:137:24 | [CallExpr] source() | arrays.js:137:17:137:22 | [VarRef] source | semmle.order | 0 |
+| arrays.js:138:5:138:56 | [DeclStmt] const element1 = ... | arrays.js:138:11:138:55 | [VariableDeclarator] element ... (item)) | semmle.label | 1 |
+| arrays.js:138:5:138:56 | [DeclStmt] const element1 = ... | arrays.js:138:11:138:55 | [VariableDeclarator] element ... (item)) | semmle.order | 1 |
+| arrays.js:138:11:138:55 | [VariableDeclarator] element ... (item)) | arrays.js:138:11:138:18 | [VarDecl] element1 | semmle.label | 1 |
+| arrays.js:138:11:138:55 | [VariableDeclarator] element ... (item)) | arrays.js:138:11:138:18 | [VarDecl] element1 | semmle.order | 1 |
+| arrays.js:138:11:138:55 | [VariableDeclarator] element ... (item)) | arrays.js:138:22:138:55 | [MethodCallExpr] arr.fin ... (item)) | semmle.label | 2 |
+| arrays.js:138:11:138:55 | [VariableDeclarator] element ... (item)) | arrays.js:138:22:138:55 | [MethodCallExpr] arr.fin ... (item)) | semmle.order | 2 |
+| arrays.js:138:22:138:33 | [DotExpr] arr.findLast | arrays.js:138:22:138:24 | [VarRef] arr | semmle.label | 1 |
+| arrays.js:138:22:138:33 | [DotExpr] arr.findLast | arrays.js:138:22:138:24 | [VarRef] arr | semmle.order | 1 |
+| arrays.js:138:22:138:33 | [DotExpr] arr.findLast | arrays.js:138:26:138:33 | [Label] findLast | semmle.label | 2 |
+| arrays.js:138:22:138:33 | [DotExpr] arr.findLast | arrays.js:138:26:138:33 | [Label] findLast | semmle.order | 2 |
+| arrays.js:138:22:138:55 | [MethodCallExpr] arr.fin ... (item)) | arrays.js:138:22:138:33 | [DotExpr] arr.findLast | semmle.label | 0 |
+| arrays.js:138:22:138:55 | [MethodCallExpr] arr.fin ... (item)) | arrays.js:138:22:138:33 | [DotExpr] arr.findLast | semmle.order | 0 |
+| arrays.js:138:22:138:55 | [MethodCallExpr] arr.fin ... (item)) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:138:22:138:55 | [MethodCallExpr] arr.fin ... (item)) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:138:35:138:54 | [ArrowFunctionExpr] (item) => sink(item) | arrays.js:138:45:138:54 | [CallExpr] sink(item) | semmle.label | 5 |
+| arrays.js:138:35:138:54 | [ArrowFunctionExpr] (item) => sink(item) | arrays.js:138:45:138:54 | [CallExpr] sink(item) | semmle.order | 5 |
+| arrays.js:138:35:138:54 | [ArrowFunctionExpr] (item) => sink(item) | file://:0:0:0:0 | (Parameters) | semmle.label | 1 |
+| arrays.js:138:35:138:54 | [ArrowFunctionExpr] (item) => sink(item) | file://:0:0:0:0 | (Parameters) | semmle.order | 1 |
+| arrays.js:138:45:138:54 | [CallExpr] sink(item) | arrays.js:138:45:138:48 | [VarRef] sink | semmle.label | 0 |
+| arrays.js:138:45:138:54 | [CallExpr] sink(item) | arrays.js:138:45:138:48 | [VarRef] sink | semmle.order | 0 |
+| arrays.js:138:45:138:54 | [CallExpr] sink(item) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:138:45:138:54 | [CallExpr] sink(item) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:139:5:139:18 | [CallExpr] sink(element1) | arrays.js:139:5:139:8 | [VarRef] sink | semmle.label | 0 |
+| arrays.js:139:5:139:18 | [CallExpr] sink(element1) | arrays.js:139:5:139:8 | [VarRef] sink | semmle.order | 0 |
+| arrays.js:139:5:139:18 | [CallExpr] sink(element1) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:139:5:139:18 | [CallExpr] sink(element1) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:139:5:139:19 | [ExprStmt] sink(element1); | arrays.js:139:5:139:18 | [CallExpr] sink(element1) | semmle.label | 1 |
+| arrays.js:139:5:139:19 | [ExprStmt] sink(element1); | arrays.js:139:5:139:18 | [CallExpr] sink(element1) | semmle.order | 1 |
+| arrays.js:142:3:146:3 | [BlockStmt] { c ... OK } | arrays.js:143:5:143:25 | [DeclStmt] const arr = ... | semmle.label | 1 |
+| arrays.js:142:3:146:3 | [BlockStmt] { c ... OK } | arrays.js:143:5:143:25 | [DeclStmt] const arr = ... | semmle.order | 1 |
+| arrays.js:142:3:146:3 | [BlockStmt] { c ... OK } | arrays.js:144:5:144:61 | [DeclStmt] const element1 = ... | semmle.label | 2 |
+| arrays.js:142:3:146:3 | [BlockStmt] { c ... OK } | arrays.js:144:5:144:61 | [DeclStmt] const element1 = ... | semmle.order | 2 |
+| arrays.js:142:3:146:3 | [BlockStmt] { c ... OK } | arrays.js:145:5:145:19 | [ExprStmt] sink(element1); | semmle.label | 3 |
+| arrays.js:142:3:146:3 | [BlockStmt] { c ... OK } | arrays.js:145:5:145:19 | [ExprStmt] sink(element1); | semmle.order | 3 |
+| arrays.js:143:5:143:25 | [DeclStmt] const arr = ... | arrays.js:143:11:143:24 | [VariableDeclarator] arr = source() | semmle.label | 1 |
+| arrays.js:143:5:143:25 | [DeclStmt] const arr = ... | arrays.js:143:11:143:24 | [VariableDeclarator] arr = source() | semmle.order | 1 |
+| arrays.js:143:11:143:24 | [VariableDeclarator] arr = source() | arrays.js:143:11:143:13 | [VarDecl] arr | semmle.label | 1 |
+| arrays.js:143:11:143:24 | [VariableDeclarator] arr = source() | arrays.js:143:11:143:13 | [VarDecl] arr | semmle.order | 1 |
+| arrays.js:143:11:143:24 | [VariableDeclarator] arr = source() | arrays.js:143:17:143:24 | [CallExpr] source() | semmle.label | 2 |
+| arrays.js:143:11:143:24 | [VariableDeclarator] arr = source() | arrays.js:143:17:143:24 | [CallExpr] source() | semmle.order | 2 |
+| arrays.js:143:17:143:24 | [CallExpr] source() | arrays.js:143:17:143:22 | [VarRef] source | semmle.label | 0 |
+| arrays.js:143:17:143:24 | [CallExpr] source() | arrays.js:143:17:143:22 | [VarRef] source | semmle.order | 0 |
+| arrays.js:144:5:144:61 | [DeclStmt] const element1 = ... | arrays.js:144:11:144:60 | [VariableDeclarator] element ... (item)) | semmle.label | 1 |
+| arrays.js:144:5:144:61 | [DeclStmt] const element1 = ... | arrays.js:144:11:144:60 | [VariableDeclarator] element ... (item)) | semmle.order | 1 |
+| arrays.js:144:11:144:60 | [VariableDeclarator] element ... (item)) | arrays.js:144:11:144:18 | [VarDecl] element1 | semmle.label | 1 |
+| arrays.js:144:11:144:60 | [VariableDeclarator] element ... (item)) | arrays.js:144:11:144:18 | [VarDecl] element1 | semmle.order | 1 |
+| arrays.js:144:11:144:60 | [VariableDeclarator] element ... (item)) | arrays.js:144:22:144:60 | [MethodCallExpr] arr.fin ... (item)) | semmle.label | 2 |
+| arrays.js:144:11:144:60 | [VariableDeclarator] element ... (item)) | arrays.js:144:22:144:60 | [MethodCallExpr] arr.fin ... (item)) | semmle.order | 2 |
+| arrays.js:144:22:144:38 | [DotExpr] arr.findLastIndex | arrays.js:144:22:144:24 | [VarRef] arr | semmle.label | 1 |
+| arrays.js:144:22:144:38 | [DotExpr] arr.findLastIndex | arrays.js:144:22:144:24 | [VarRef] arr | semmle.order | 1 |
+| arrays.js:144:22:144:38 | [DotExpr] arr.findLastIndex | arrays.js:144:26:144:38 | [Label] findLastIndex | semmle.label | 2 |
+| arrays.js:144:22:144:38 | [DotExpr] arr.findLastIndex | arrays.js:144:26:144:38 | [Label] findLastIndex | semmle.order | 2 |
+| arrays.js:144:22:144:60 | [MethodCallExpr] arr.fin ... (item)) | arrays.js:144:22:144:38 | [DotExpr] arr.findLastIndex | semmle.label | 0 |
+| arrays.js:144:22:144:60 | [MethodCallExpr] arr.fin ... (item)) | arrays.js:144:22:144:38 | [DotExpr] arr.findLastIndex | semmle.order | 0 |
+| arrays.js:144:22:144:60 | [MethodCallExpr] arr.fin ... (item)) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:144:22:144:60 | [MethodCallExpr] arr.fin ... (item)) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:144:40:144:59 | [ArrowFunctionExpr] (item) => sink(item) | arrays.js:144:50:144:59 | [CallExpr] sink(item) | semmle.label | 5 |
+| arrays.js:144:40:144:59 | [ArrowFunctionExpr] (item) => sink(item) | arrays.js:144:50:144:59 | [CallExpr] sink(item) | semmle.order | 5 |
+| arrays.js:144:40:144:59 | [ArrowFunctionExpr] (item) => sink(item) | file://:0:0:0:0 | (Parameters) | semmle.label | 1 |
+| arrays.js:144:40:144:59 | [ArrowFunctionExpr] (item) => sink(item) | file://:0:0:0:0 | (Parameters) | semmle.order | 1 |
+| arrays.js:144:50:144:59 | [CallExpr] sink(item) | arrays.js:144:50:144:53 | [VarRef] sink | semmle.label | 0 |
+| arrays.js:144:50:144:59 | [CallExpr] sink(item) | arrays.js:144:50:144:53 | [VarRef] sink | semmle.order | 0 |
+| arrays.js:144:50:144:59 | [CallExpr] sink(item) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:144:50:144:59 | [CallExpr] sink(item) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:145:5:145:18 | [CallExpr] sink(element1) | arrays.js:145:5:145:8 | [VarRef] sink | semmle.label | 0 |
+| arrays.js:145:5:145:18 | [CallExpr] sink(element1) | arrays.js:145:5:145:8 | [VarRef] sink | semmle.order | 0 |
+| arrays.js:145:5:145:18 | [CallExpr] sink(element1) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
+| arrays.js:145:5:145:18 | [CallExpr] sink(element1) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
+| arrays.js:145:5:145:19 | [ExprStmt] sink(element1); | arrays.js:145:5:145:18 | [CallExpr] sink(element1) | semmle.label | 1 |
+| arrays.js:145:5:145:19 | [ExprStmt] sink(element1); | arrays.js:145:5:145:18 | [CallExpr] sink(element1) | semmle.order | 1 |
| file://:0:0:0:0 | (Arguments) | arrays.js:5:8:5:14 | [DotExpr] obj.foo | semmle.label | 0 |
| file://:0:0:0:0 | (Arguments) | arrays.js:5:8:5:14 | [DotExpr] obj.foo | semmle.order | 0 |
| file://:0:0:0:0 | (Arguments) | arrays.js:8:12:8:17 | [VarRef] source | semmle.label | 0 |
@@ -1634,6 +2080,46 @@ edges
| file://:0:0:0:0 | (Arguments) | arrays.js:108:45:108:50 | [SpreadElement] ...arr | semmle.order | 2 |
| file://:0:0:0:0 | (Arguments) | arrays.js:109:8:109:24 | [MethodCallExpr] arr8_spread.pop() | semmle.label | 0 |
| file://:0:0:0:0 | (Arguments) | arrays.js:109:8:109:24 | [MethodCallExpr] arr8_spread.pop() | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:111:8:111:33 | [MethodCallExpr] arr.fin ... llback) | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:111:8:111:33 | [MethodCallExpr] arr.fin ... llback) | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:111:21:111:32 | [VarRef] someCallback | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:111:21:111:32 | [VarRef] someCallback | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:115:35:115:54 | [ArrowFunctionExpr] (item) => sink(item) | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:115:35:115:54 | [ArrowFunctionExpr] (item) => sink(item) | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:115:50:115:53 | [VarRef] item | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:115:50:115:53 | [VarRef] item | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:116:10:116:16 | [VarRef] element | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:116:10:116:16 | [VarRef] element | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:121:31:121:50 | [ArrowFunctionExpr] (item) => sink(item) | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:121:31:121:50 | [ArrowFunctionExpr] (item) => sink(item) | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:121:46:121:49 | [VarRef] item | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:121:46:121:49 | [VarRef] item | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:122:10:122:16 | [VarRef] element | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:122:10:122:16 | [VarRef] element | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:127:40:127:59 | [ArrowFunctionExpr] (item) => sink(item) | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:127:40:127:59 | [ArrowFunctionExpr] (item) => sink(item) | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:127:55:127:58 | [VarRef] item | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:127:55:127:58 | [VarRef] item | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:128:10:128:16 | [VarRef] element | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:128:10:128:16 | [VarRef] element | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:132:31:132:50 | [ArrowFunctionExpr] (item) => sink(item) | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:132:31:132:50 | [ArrowFunctionExpr] (item) => sink(item) | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:132:46:132:49 | [VarRef] item | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:132:46:132:49 | [VarRef] item | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:133:10:133:17 | [VarRef] element1 | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:133:10:133:17 | [VarRef] element1 | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:138:35:138:54 | [ArrowFunctionExpr] (item) => sink(item) | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:138:35:138:54 | [ArrowFunctionExpr] (item) => sink(item) | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:138:50:138:53 | [VarRef] item | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:138:50:138:53 | [VarRef] item | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:139:10:139:17 | [VarRef] element1 | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:139:10:139:17 | [VarRef] element1 | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:144:40:144:59 | [ArrowFunctionExpr] (item) => sink(item) | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:144:40:144:59 | [ArrowFunctionExpr] (item) => sink(item) | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:144:55:144:58 | [VarRef] item | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:144:55:144:58 | [VarRef] item | semmle.order | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:145:10:145:17 | [VarRef] element1 | semmle.label | 0 |
+| file://:0:0:0:0 | (Arguments) | arrays.js:145:10:145:17 | [VarRef] element1 | semmle.order | 0 |
| file://:0:0:0:0 | (Parameters) | arrays.js:15:16:15:16 | [SimpleParameter] e | semmle.label | 0 |
| file://:0:0:0:0 | (Parameters) | arrays.js:15:16:15:16 | [SimpleParameter] e | semmle.order | 0 |
| file://:0:0:0:0 | (Parameters) | arrays.js:16:12:16:12 | [SimpleParameter] e | semmle.label | 0 |
@@ -1652,5 +2138,17 @@ edges
| file://:0:0:0:0 | (Parameters) | arrays.js:95:27:95:27 | [SimpleParameter] x | semmle.order | 0 |
| file://:0:0:0:0 | (Parameters) | arrays.js:96:27:96:27 | [SimpleParameter] x | semmle.label | 0 |
| file://:0:0:0:0 | (Parameters) | arrays.js:96:27:96:27 | [SimpleParameter] x | semmle.order | 0 |
+| file://:0:0:0:0 | (Parameters) | arrays.js:115:36:115:39 | [SimpleParameter] item | semmle.label | 0 |
+| file://:0:0:0:0 | (Parameters) | arrays.js:115:36:115:39 | [SimpleParameter] item | semmle.order | 0 |
+| file://:0:0:0:0 | (Parameters) | arrays.js:121:32:121:35 | [SimpleParameter] item | semmle.label | 0 |
+| file://:0:0:0:0 | (Parameters) | arrays.js:121:32:121:35 | [SimpleParameter] item | semmle.order | 0 |
+| file://:0:0:0:0 | (Parameters) | arrays.js:127:41:127:44 | [SimpleParameter] item | semmle.label | 0 |
+| file://:0:0:0:0 | (Parameters) | arrays.js:127:41:127:44 | [SimpleParameter] item | semmle.order | 0 |
+| file://:0:0:0:0 | (Parameters) | arrays.js:132:32:132:35 | [SimpleParameter] item | semmle.label | 0 |
+| file://:0:0:0:0 | (Parameters) | arrays.js:132:32:132:35 | [SimpleParameter] item | semmle.order | 0 |
+| file://:0:0:0:0 | (Parameters) | arrays.js:138:36:138:39 | [SimpleParameter] item | semmle.label | 0 |
+| file://:0:0:0:0 | (Parameters) | arrays.js:138:36:138:39 | [SimpleParameter] item | semmle.order | 0 |
+| file://:0:0:0:0 | (Parameters) | arrays.js:144:41:144:44 | [SimpleParameter] item | semmle.label | 0 |
+| file://:0:0:0:0 | (Parameters) | arrays.js:144:41:144:44 | [SimpleParameter] item | semmle.order | 0 |
graphProperties
| semmle.graphKind | tree |
diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected
index f81405a32a2f..b6d5ab1e435c 100644
--- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected
+++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected
@@ -209,6 +209,10 @@ typeInferenceMismatch
| static-capture-groups.js:2:17:2:24 | source() | static-capture-groups.js:27:14:27:22 | RegExp.$1 |
| static-capture-groups.js:32:17:32:24 | source() | static-capture-groups.js:38:10:38:18 | RegExp.$1 |
| static-capture-groups.js:42:12:42:19 | source() | static-capture-groups.js:43:14:43:22 | RegExp.$1 |
+| string-immutable-operations.js:2:13:2:20 | source() | string-immutable-operations.js:3:10:3:25 | x.toWellFormed() |
+| string-immutable-operations.js:2:13:2:20 | source() | string-immutable-operations.js:6:10:6:20 | wellFormedX |
+| string-immutable-operations.js:2:13:2:20 | source() | string-immutable-operations.js:9:10:9:26 | concatWellFormedX |
+| string-immutable-operations.js:11:10:11:17 | source() | string-immutable-operations.js:11:10:11:32 | source( ... ormed() |
| string-replace.js:3:13:3:20 | source() | string-replace.js:14:10:14:13 | data |
| string-replace.js:3:13:3:20 | source() | string-replace.js:18:10:18:13 | data |
| string-replace.js:3:13:3:20 | source() | string-replace.js:21:6:21:41 | safe(). ... taint) |
@@ -244,8 +248,19 @@ typeInferenceMismatch
| tst.js:2:13:2:20 | source() | tst.js:66:10:66:16 | xSorted |
| tst.js:2:13:2:20 | source() | tst.js:68:10:68:23 | x.toReversed() |
| tst.js:2:13:2:20 | source() | tst.js:70:10:70:18 | xReversed |
-| tst.js:2:13:2:20 | source() | tst.js:72:10:72:17 | x.with() |
-| tst.js:2:13:2:20 | source() | tst.js:74:10:74:14 | xWith |
+| tst.js:2:13:2:20 | source() | tst.js:72:10:72:31 | Map.gro ... z => z) |
+| tst.js:2:13:2:20 | source() | tst.js:74:10:74:34 | Object. ... z => z) |
+| tst.js:2:13:2:20 | source() | tst.js:78:55:78:58 | item |
+| tst.js:2:13:2:20 | source() | tst.js:79:14:79:20 | grouped |
+| tst.js:2:13:2:20 | source() | tst.js:100:10:100:17 | x.with() |
+| tst.js:2:13:2:20 | source() | tst.js:102:10:102:14 | xWith |
+| tst.js:75:22:75:29 | source() | tst.js:75:10:75:52 | Map.gro ... (item)) |
+| tst.js:75:22:75:29 | source() | tst.js:75:47:75:50 | item |
+| tst.js:82:23:82:30 | source() | tst.js:83:58:83:61 | item |
+| tst.js:82:23:82:30 | source() | tst.js:84:14:84:20 | grouped |
+| tst.js:87:22:87:29 | source() | tst.js:90:14:90:25 | taintedValue |
+| tst.js:93:22:93:29 | source() | tst.js:96:14:96:25 | taintedValue |
+| tst.js:93:22:93:29 | source() | tst.js:97:14:97:26 | map.get(true) |
| xml.js:5:18:5:25 | source() | xml.js:8:14:8:17 | text |
| xml.js:12:17:12:24 | source() | xml.js:13:14:13:19 | result |
| xml.js:23:18:23:25 | source() | xml.js:20:14:20:17 | attr |
diff --git a/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected b/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected
index 33a27661ecd1..3b89229b2d7f 100644
--- a/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected
+++ b/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected
@@ -112,3 +112,5 @@
| thisAssignments.js:7:19:7:26 | source() | thisAssignments.js:8:10:8:20 | this.field2 |
| tst.js:2:13:2:20 | source() | tst.js:4:10:4:10 | x |
| tst.js:2:13:2:20 | source() | tst.js:54:14:54:19 | unsafe |
+| tst.js:93:22:93:29 | source() | tst.js:96:14:96:25 | taintedValue |
+| tst.js:93:22:93:29 | source() | tst.js:97:14:97:26 | map.get(true) |
diff --git a/javascript/ql/test/library-tests/TaintTracking/string-immutable-operations.js b/javascript/ql/test/library-tests/TaintTracking/string-immutable-operations.js
new file mode 100644
index 000000000000..79e93fab0025
--- /dev/null
+++ b/javascript/ql/test/library-tests/TaintTracking/string-immutable-operations.js
@@ -0,0 +1,12 @@
+function test() {
+ let x = source();
+ sink(x.toWellFormed()); // NOT OK
+
+ const wellFormedX = x.toWellFormed();
+ sink(wellFormedX); // NOT OK
+
+ const concatWellFormedX = "/" + wellFormedX + "!";
+ sink(concatWellFormedX); // NOT OK
+
+ sink(source().toWellFormed()); // NOT OK
+}
diff --git a/javascript/ql/test/library-tests/TaintTracking/tst.js b/javascript/ql/test/library-tests/TaintTracking/tst.js
index 13b4ea48d8dd..8fbc5e525bd7 100644
--- a/javascript/ql/test/library-tests/TaintTracking/tst.js
+++ b/javascript/ql/test/library-tests/TaintTracking/tst.js
@@ -69,6 +69,34 @@ function test() {
const xReversed = x.toReversed();
sink(xReversed) // NOT OK
+ sink(Map.groupBy(x, z => z)); // NOT OK
+ sink(Custom.groupBy(x, z => z)); // OK
+ sink(Object.groupBy(x, z => z)); // NOT OK
+ sink(Map.groupBy(source(), (item) => sink(item))); // NOT OK
+
+ {
+ const grouped = Map.groupBy(x, (item) => sink(item)); // NOT OK
+ sink(grouped); // NOT OK
+ }
+ {
+ const list = [source()];
+ const grouped = Map.groupBy(list, (item) => sink(item)); // NOT OK
+ sink(grouped); // NOT OK
+ }
+ {
+ const data = source();
+ const result = Object.groupBy(data, item => item);
+ const taintedValue = result[notDefined()];
+ sink(taintedValue); // NOT OK
+ }
+ {
+ const data = source();
+ const map = Map.groupBy(data, item => item);
+ const taintedValue = map.get(true);
+ sink(taintedValue); // NOT OK
+ sink(map.get(true)); // NOT OK
+ }
+
sink(x.with()) // NOT OK
const xWith = x.with();
sink(xWith) // NOT OK
diff --git a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected
index 3061a6ba1f04..df304b899bb0 100644
--- a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected
+++ b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected
@@ -1877,8 +1877,35 @@ nodes
| tst.ts:508:17:508:38 | [MethodCallExpr] obj[key ... rCase() | semmle.label | [MethodCallExpr] obj[key ... rCase() |
| tst.ts:508:21:508:23 | [VarRef] key | semmle.label | [VarRef] key |
| tst.ts:508:26:508:36 | [Label] toUpperCase | semmle.label | [Label] toUpperCase |
+| tst.ts:513:1:520:1 | [NamespaceDeclaration] namespa ... type. } | semmle.label | [NamespaceDeclaration] namespa ... type. } |
+| tst.ts:513:1:520:1 | [NamespaceDeclaration] namespa ... type. } | semmle.order | 92 |
+| tst.ts:513:11:513:14 | [VarDecl] TS57 | semmle.label | [VarDecl] TS57 |
+| tst.ts:514:3:514:26 | [DeclStmt] const a = ... | semmle.label | [DeclStmt] const a = ... |
+| tst.ts:514:17:514:17 | [VarDecl] a | semmle.label | [VarDecl] a |
+| tst.ts:514:17:514:25 | [VariableDeclarator] a: symbol | semmle.label | [VariableDeclarator] a: symbol |
+| tst.ts:514:20:514:25 | [KeywordTypeExpr] symbol | semmle.label | [KeywordTypeExpr] symbol |
+| tst.ts:515:3:517:3 | [ExportDeclaration] export ... }; } | semmle.label | [ExportDeclaration] export ... }; } |
+| tst.ts:515:10:517:3 | [ClassDefinition,TypeDefinition] class A ... }; } | semmle.label | [ClassDefinition,TypeDefinition] class A ... }; } |
+| tst.ts:515:16:515:16 | [VarDecl] A | semmle.label | [VarDecl] A |
+| tst.ts:515:18:515:17 | [BlockStmt] {} | semmle.label | [BlockStmt] {} |
+| tst.ts:515:18:515:17 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | [ClassInitializedMember,ConstructorDefinition] constructor() {} |
+| tst.ts:515:18:515:17 | [FunctionExpr] () {} | semmle.label | [FunctionExpr] () {} |
+| tst.ts:515:18:515:17 | [Label] constructor | semmle.label | [Label] constructor |
+| tst.ts:516:7:516:24 | [ClassInitializedMember,MethodDefinition] [a]() { return 1 } | semmle.label | [ClassInitializedMember,MethodDefinition] [a]() { return 1 } |
+| tst.ts:516:7:516:24 | [FunctionExpr] [a]() { return 1 } | semmle.label | [FunctionExpr] [a]() { return 1 } |
+| tst.ts:516:8:516:8 | [VarRef] a | semmle.label | [VarRef] a |
+| tst.ts:516:13:516:24 | [BlockStmt] { return 1 } | semmle.label | [BlockStmt] { return 1 } |
+| tst.ts:516:15:516:22 | [ReturnStmt] return 1 | semmle.label | [ReturnStmt] return 1 |
+| tst.ts:516:22:516:22 | [Literal] 1 | semmle.label | [Literal] 1 |
+| tst.ts:519:3:519:32 | [DeclStmt] const e1 = ... | semmle.label | [DeclStmt] const e1 = ... |
+| tst.ts:519:17:519:18 | [VarDecl] e1 | semmle.label | [VarDecl] e1 |
+| tst.ts:519:17:519:31 | [VariableDeclarator] e1: A[typeof a] | semmle.label | [VariableDeclarator] e1: A[typeof a] |
+| tst.ts:519:21:519:21 | [LocalTypeAccess] A | semmle.label | [LocalTypeAccess] A |
+| tst.ts:519:21:519:31 | [IndexedAccessTypeExpr] A[typeof a] | semmle.label | [IndexedAccessTypeExpr] A[typeof a] |
+| tst.ts:519:23:519:30 | [TypeofTypeExpr] typeof a | semmle.label | [TypeofTypeExpr] typeof a |
+| tst.ts:519:30:519:30 | [LocalVarTypeAccess] a | semmle.label | [LocalVarTypeAccess] a |
| tstModuleCJS.cts:1:1:3:1 | [ExportDeclaration] export ... 'b'; } | semmle.label | [ExportDeclaration] export ... 'b'; } |
-| tstModuleCJS.cts:1:1:3:1 | [ExportDeclaration] export ... 'b'; } | semmle.order | 92 |
+| tstModuleCJS.cts:1:1:3:1 | [ExportDeclaration] export ... 'b'; } | semmle.order | 93 |
| tstModuleCJS.cts:1:8:3:1 | [FunctionDeclStmt] functio ... 'b'; } | semmle.label | [FunctionDeclStmt] functio ... 'b'; } |
| tstModuleCJS.cts:1:17:1:28 | [VarDecl] tstModuleCJS | semmle.label | [VarDecl] tstModuleCJS |
| tstModuleCJS.cts:1:33:1:35 | [LiteralTypeExpr] 'a' | semmle.label | [LiteralTypeExpr] 'a' |
@@ -1896,7 +1923,7 @@ nodes
| tstModuleCJS.cts:2:34:2:36 | [Literal] 'a' | semmle.label | [Literal] 'a' |
| tstModuleCJS.cts:2:40:2:42 | [Literal] 'b' | semmle.label | [Literal] 'b' |
| tstModuleES.mts:1:1:3:1 | [ExportDeclaration] export ... 'b'; } | semmle.label | [ExportDeclaration] export ... 'b'; } |
-| tstModuleES.mts:1:1:3:1 | [ExportDeclaration] export ... 'b'; } | semmle.order | 93 |
+| tstModuleES.mts:1:1:3:1 | [ExportDeclaration] export ... 'b'; } | semmle.order | 94 |
| tstModuleES.mts:1:16:3:1 | [FunctionDeclStmt] functio ... 'b'; } | semmle.label | [FunctionDeclStmt] functio ... 'b'; } |
| tstModuleES.mts:1:25:1:35 | [VarDecl] tstModuleES | semmle.label | [VarDecl] tstModuleES |
| tstModuleES.mts:1:40:1:42 | [LiteralTypeExpr] 'a' | semmle.label | [LiteralTypeExpr] 'a' |
@@ -1914,7 +1941,7 @@ nodes
| tstModuleES.mts:2:34:2:36 | [Literal] 'a' | semmle.label | [Literal] 'a' |
| tstModuleES.mts:2:40:2:42 | [Literal] 'b' | semmle.label | [Literal] 'b' |
| tstSuffixA.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | semmle.label | [ExportDeclaration] export ... .ts'; } |
-| tstSuffixA.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | semmle.order | 94 |
+| tstSuffixA.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | semmle.order | 95 |
| tstSuffixA.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | semmle.label | [FunctionDeclStmt] functio ... .ts'; } |
| tstSuffixA.ts:1:17:1:28 | [VarDecl] resolvedFile | semmle.label | [VarDecl] resolvedFile |
| tstSuffixA.ts:1:33:1:47 | [LiteralTypeExpr] 'tstSuffixA.ts' | semmle.label | [LiteralTypeExpr] 'tstSuffixA.ts' |
@@ -1922,7 +1949,7 @@ nodes
| tstSuffixA.ts:2:5:2:27 | [ReturnStmt] return ... xA.ts'; | semmle.label | [ReturnStmt] return ... xA.ts'; |
| tstSuffixA.ts:2:12:2:26 | [Literal] 'tstSuffixA.ts' | semmle.label | [Literal] 'tstSuffixA.ts' |
| tstSuffixB.ios.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | semmle.label | [ExportDeclaration] export ... .ts'; } |
-| tstSuffixB.ios.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | semmle.order | 95 |
+| tstSuffixB.ios.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | semmle.order | 96 |
| tstSuffixB.ios.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | semmle.label | [FunctionDeclStmt] functio ... .ts'; } |
| tstSuffixB.ios.ts:1:17:1:28 | [VarDecl] resolvedFile | semmle.label | [VarDecl] resolvedFile |
| tstSuffixB.ios.ts:1:33:1:51 | [LiteralTypeExpr] 'tstSuffixB.ios.ts' | semmle.label | [LiteralTypeExpr] 'tstSuffixB.ios.ts' |
@@ -1930,7 +1957,7 @@ nodes
| tstSuffixB.ios.ts:2:5:2:31 | [ReturnStmt] return ... os.ts'; | semmle.label | [ReturnStmt] return ... os.ts'; |
| tstSuffixB.ios.ts:2:12:2:30 | [Literal] 'tstSuffixB.ios.ts' | semmle.label | [Literal] 'tstSuffixB.ios.ts' |
| tstSuffixB.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | semmle.label | [ExportDeclaration] export ... .ts'; } |
-| tstSuffixB.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | semmle.order | 96 |
+| tstSuffixB.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | semmle.order | 97 |
| tstSuffixB.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | semmle.label | [FunctionDeclStmt] functio ... .ts'; } |
| tstSuffixB.ts:1:17:1:28 | [VarDecl] resolvedFile | semmle.label | [VarDecl] resolvedFile |
| tstSuffixB.ts:1:33:1:47 | [LiteralTypeExpr] 'tstSuffixB.ts' | semmle.label | [LiteralTypeExpr] 'tstSuffixB.ts' |
@@ -1938,16 +1965,16 @@ nodes
| tstSuffixB.ts:2:5:2:27 | [ReturnStmt] return ... xB.ts'; | semmle.label | [ReturnStmt] return ... xB.ts'; |
| tstSuffixB.ts:2:12:2:26 | [Literal] 'tstSuffixB.ts' | semmle.label | [Literal] 'tstSuffixB.ts' |
| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type B = boolean; |
-| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.order | 97 |
+| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.order | 98 |
| type_alias.ts:1:6:1:6 | [Identifier] B | semmle.label | [Identifier] B |
| type_alias.ts:1:10:1:16 | [KeywordTypeExpr] boolean | semmle.label | [KeywordTypeExpr] boolean |
| type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.label | [DeclStmt] var b = ... |
-| type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.order | 98 |
+| type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.order | 99 |
| type_alias.ts:3:5:3:5 | [VarDecl] b | semmle.label | [VarDecl] b |
| type_alias.ts:3:5:3:8 | [VariableDeclarator] b: B | semmle.label | [VariableDeclarator] b: B |
| type_alias.ts:3:8:3:8 | [LocalTypeAccess] B | semmle.label | [LocalTypeAccess] B |
| type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; |
-| type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.order | 99 |
+| type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.order | 100 |
| type_alias.ts:5:6:5:17 | [Identifier] ValueOrArray | semmle.label | [Identifier] ValueOrArray |
| type_alias.ts:5:19:5:19 | [Identifier] T | semmle.label | [Identifier] T |
| type_alias.ts:5:19:5:19 | [TypeParameter] T | semmle.label | [TypeParameter] T |
@@ -1959,14 +1986,14 @@ nodes
| type_alias.ts:5:34:5:48 | [GenericTypeExpr] ValueOrArray | semmle.label | [GenericTypeExpr] ValueOrArray |
| type_alias.ts:5:47:5:47 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T |
| type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.label | [DeclStmt] var c = ... |
-| type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.order | 100 |
+| type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.order | 101 |
| type_alias.ts:7:5:7:5 | [VarDecl] c | semmle.label | [VarDecl] c |
| type_alias.ts:7:5:7:27 | [VariableDeclarator] c: Valu ... number> | semmle.label | [VariableDeclarator] c: Valu ... number> |
| type_alias.ts:7:8:7:19 | [LocalTypeAccess] ValueOrArray | semmle.label | [LocalTypeAccess] ValueOrArray |
| type_alias.ts:7:8:7:27 | [GenericTypeExpr] ValueOrArray | semmle.label | [GenericTypeExpr] ValueOrArray |
| type_alias.ts:7:21:7:26 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number |
| type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; |
-| type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.order | 101 |
+| type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.order | 102 |
| type_alias.ts:9:6:9:9 | [Identifier] Json | semmle.label | [Identifier] Json |
| type_alias.ts:10:5:15:12 | [UnionTypeExpr] \| strin ... Json[] | semmle.label | [UnionTypeExpr] \| strin ... Json[] |
| type_alias.ts:10:7:10:12 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string |
@@ -1982,12 +2009,12 @@ nodes
| type_alias.ts:15:7:15:10 | [LocalTypeAccess] Json | semmle.label | [LocalTypeAccess] Json |
| type_alias.ts:15:7:15:12 | [ArrayTypeExpr] Json[] | semmle.label | [ArrayTypeExpr] Json[] |
| type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.label | [DeclStmt] var json = ... |
-| type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.order | 102 |
+| type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.order | 103 |
| type_alias.ts:17:5:17:8 | [VarDecl] json | semmle.label | [VarDecl] json |
| type_alias.ts:17:5:17:14 | [VariableDeclarator] json: Json | semmle.label | [VariableDeclarator] json: Json |
| type_alias.ts:17:11:17:14 | [LocalTypeAccess] Json | semmle.label | [LocalTypeAccess] Json |
| type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; |
-| type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.order | 103 |
+| type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.order | 104 |
| type_alias.ts:19:6:19:16 | [Identifier] VirtualNode | semmle.label | [Identifier] VirtualNode |
| type_alias.ts:20:5:21:56 | [UnionTypeExpr] \| strin ... Node[]] | semmle.label | [UnionTypeExpr] \| strin ... Node[]] |
| type_alias.ts:20:7:20:12 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string |
@@ -2003,7 +2030,7 @@ nodes
| type_alias.ts:21:43:21:53 | [LocalTypeAccess] VirtualNode | semmle.label | [LocalTypeAccess] VirtualNode |
| type_alias.ts:21:43:21:55 | [ArrayTypeExpr] VirtualNode[] | semmle.label | [ArrayTypeExpr] VirtualNode[] |
| type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.label | [DeclStmt] const myNode = ... |
-| type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.order | 104 |
+| type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.order | 105 |
| type_alias.ts:23:7:23:12 | [VarDecl] myNode | semmle.label | [VarDecl] myNode |
| type_alias.ts:23:7:27:5 | [VariableDeclarator] myNode: ... ] ] | semmle.label | [VariableDeclarator] myNode: ... ] ] |
| type_alias.ts:23:15:23:25 | [LocalTypeAccess] VirtualNode | semmle.label | [LocalTypeAccess] VirtualNode |
@@ -2028,12 +2055,12 @@ nodes
| type_alias.ts:26:23:26:36 | [Literal] "second-child" | semmle.label | [Literal] "second-child" |
| type_alias.ts:26:41:26:62 | [Literal] "I'm the second child" | semmle.label | [Literal] "I'm the second child" |
| type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.label | [ImportDeclaration] import ... dummy"; |
-| type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 105 |
+| type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 106 |
| type_definition_objects.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.label | [ImportSpecifier] * as dummy |
| type_definition_objects.ts:1:13:1:17 | [VarDecl] dummy | semmle.label | [VarDecl] dummy |
| type_definition_objects.ts:1:24:1:32 | [Literal] "./dummy" | semmle.label | [Literal] "./dummy" |
| type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.label | [ExportDeclaration] export class C {} |
-| type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.order | 106 |
+| type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.order | 107 |
| type_definition_objects.ts:3:8:3:17 | [ClassDefinition,TypeDefinition] class C {} | semmle.label | [ClassDefinition,TypeDefinition] class C {} |
| type_definition_objects.ts:3:14:3:14 | [VarDecl] C | semmle.label | [VarDecl] C |
| type_definition_objects.ts:3:16:3:15 | [BlockStmt] {} | semmle.label | [BlockStmt] {} |
@@ -2041,36 +2068,36 @@ nodes
| type_definition_objects.ts:3:16:3:15 | [FunctionExpr] () {} | semmle.label | [FunctionExpr] () {} |
| type_definition_objects.ts:3:16:3:15 | [Label] constructor | semmle.label | [Label] constructor |
| type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.label | [DeclStmt] let classObj = ... |
-| type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.order | 107 |
+| type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.order | 108 |
| type_definition_objects.ts:4:5:4:12 | [VarDecl] classObj | semmle.label | [VarDecl] classObj |
| type_definition_objects.ts:4:5:4:16 | [VariableDeclarator] classObj = C | semmle.label | [VariableDeclarator] classObj = C |
| type_definition_objects.ts:4:16:4:16 | [VarRef] C | semmle.label | [VarRef] C |
| type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.label | [ExportDeclaration] export enum E {} |
-| type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.order | 108 |
+| type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.order | 109 |
| type_definition_objects.ts:6:8:6:16 | [EnumDeclaration,TypeDefinition] enum E {} | semmle.label | [EnumDeclaration,TypeDefinition] enum E {} |
| type_definition_objects.ts:6:13:6:13 | [VarDecl] E | semmle.label | [VarDecl] E |
| type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.label | [DeclStmt] let enumObj = ... |
-| type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.order | 109 |
+| type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.order | 110 |
| type_definition_objects.ts:7:5:7:11 | [VarDecl] enumObj | semmle.label | [VarDecl] enumObj |
| type_definition_objects.ts:7:5:7:15 | [VariableDeclarator] enumObj = E | semmle.label | [VariableDeclarator] enumObj = E |
| type_definition_objects.ts:7:15:7:15 | [VarRef] E | semmle.label | [VarRef] E |
| type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.label | [ExportDeclaration] export ... e N {;} |
-| type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.order | 110 |
+| type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.order | 111 |
| type_definition_objects.ts:9:8:9:22 | [NamespaceDeclaration] namespace N {;} | semmle.label | [NamespaceDeclaration] namespace N {;} |
| type_definition_objects.ts:9:18:9:18 | [VarDecl] N | semmle.label | [VarDecl] N |
| type_definition_objects.ts:9:21:9:21 | [EmptyStmt] ; | semmle.label | [EmptyStmt] ; |
| type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.label | [DeclStmt] let namespaceObj = ... |
-| type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.order | 111 |
+| type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.order | 112 |
| type_definition_objects.ts:10:5:10:16 | [VarDecl] namespaceObj | semmle.label | [VarDecl] namespaceObj |
| type_definition_objects.ts:10:5:10:20 | [VariableDeclarator] namespaceObj = N | semmle.label | [VariableDeclarator] namespaceObj = N |
| type_definition_objects.ts:10:20:10:20 | [VarRef] N | semmle.label | [VarRef] N |
| type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.label | [ImportDeclaration] import ... dummy"; |
-| type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 112 |
+| type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 113 |
| type_definitions.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.label | [ImportSpecifier] * as dummy |
| type_definitions.ts:1:13:1:17 | [VarDecl] dummy | semmle.label | [VarDecl] dummy |
| type_definitions.ts:1:24:1:32 | [Literal] "./dummy" | semmle.label | [Literal] "./dummy" |
| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } |
-| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.order | 113 |
+| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.order | 114 |
| type_definitions.ts:3:11:3:11 | [Identifier] I | semmle.label | [Identifier] I |
| type_definitions.ts:3:13:3:13 | [Identifier] S | semmle.label | [Identifier] S |
| type_definitions.ts:3:13:3:13 | [TypeParameter] S | semmle.label | [TypeParameter] S |
@@ -2078,14 +2105,14 @@ nodes
| type_definitions.ts:4:3:4:7 | [FieldDeclaration] x: S; | semmle.label | [FieldDeclaration] x: S; |
| type_definitions.ts:4:6:4:6 | [LocalTypeAccess] S | semmle.label | [LocalTypeAccess] S |
| type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.label | [DeclStmt] let i = ... |
-| type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.order | 114 |
+| type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.order | 115 |
| type_definitions.ts:6:5:6:5 | [VarDecl] i | semmle.label | [VarDecl] i |
| type_definitions.ts:6:5:6:16 | [VariableDeclarator] i: I | semmle.label | [VariableDeclarator] i: I |
| type_definitions.ts:6:8:6:8 | [LocalTypeAccess] I | semmle.label | [LocalTypeAccess] I |
| type_definitions.ts:6:8:6:16 | [GenericTypeExpr] I | semmle.label | [GenericTypeExpr] I |
| type_definitions.ts:6:10:6:15 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number |
| type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.label | [ClassDefinition,TypeDefinition] class C ... x: T } |
-| type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.order | 115 |
+| type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.order | 116 |
| type_definitions.ts:8:7:8:7 | [VarDecl] C | semmle.label | [VarDecl] C |
| type_definitions.ts:8:8:8:7 | [BlockStmt] {} | semmle.label | [BlockStmt] {} |
| type_definitions.ts:8:8:8:7 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | [ClassInitializedMember,ConstructorDefinition] constructor() {} |
@@ -2097,14 +2124,14 @@ nodes
| type_definitions.ts:9:3:9:6 | [FieldDeclaration] x: T | semmle.label | [FieldDeclaration] x: T |
| type_definitions.ts:9:6:9:6 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T |
| type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.label | [DeclStmt] let c = ... |
-| type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.order | 116 |
+| type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.order | 117 |
| type_definitions.ts:11:5:11:5 | [VarDecl] c | semmle.label | [VarDecl] c |
| type_definitions.ts:11:5:11:16 | [VariableDeclarator] c: C | semmle.label | [VariableDeclarator] c: C |
| type_definitions.ts:11:8:11:8 | [LocalTypeAccess] C | semmle.label | [LocalTypeAccess] C |
| type_definitions.ts:11:8:11:16 | [GenericTypeExpr] C | semmle.label | [GenericTypeExpr] C |
| type_definitions.ts:11:10:11:15 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number |
| type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.label | [EnumDeclaration,TypeDefinition] enum Co ... blue } |
-| type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.order | 117 |
+| type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.order | 118 |
| type_definitions.ts:13:6:13:10 | [VarDecl] Color | semmle.label | [VarDecl] Color |
| type_definitions.ts:14:3:14:5 | [EnumMember,TypeDefinition] red | semmle.label | [EnumMember,TypeDefinition] red |
| type_definitions.ts:14:3:14:5 | [VarDecl] red | semmle.label | [VarDecl] red |
@@ -2113,29 +2140,29 @@ nodes
| type_definitions.ts:14:15:14:18 | [EnumMember,TypeDefinition] blue | semmle.label | [EnumMember,TypeDefinition] blue |
| type_definitions.ts:14:15:14:18 | [VarDecl] blue | semmle.label | [VarDecl] blue |
| type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.label | [DeclStmt] let color = ... |
-| type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.order | 118 |
+| type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.order | 119 |
| type_definitions.ts:16:5:16:9 | [VarDecl] color | semmle.label | [VarDecl] color |
| type_definitions.ts:16:5:16:16 | [VariableDeclarator] color: Color | semmle.label | [VariableDeclarator] color: Color |
| type_definitions.ts:16:12:16:16 | [LocalTypeAccess] Color | semmle.label | [LocalTypeAccess] Color |
| type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.label | [EnumDeclaration,TypeDefinition] enum En ... ember } |
-| type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.order | 119 |
+| type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.order | 120 |
| type_definitions.ts:18:6:18:22 | [VarDecl] EnumWithOneMember | semmle.label | [VarDecl] EnumWithOneMember |
| type_definitions.ts:18:26:18:31 | [EnumMember,TypeDefinition] member | semmle.label | [EnumMember,TypeDefinition] member |
| type_definitions.ts:18:26:18:31 | [VarDecl] member | semmle.label | [VarDecl] member |
| type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.label | [DeclStmt] let e = ... |
-| type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.order | 120 |
+| type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.order | 121 |
| type_definitions.ts:19:5:19:5 | [VarDecl] e | semmle.label | [VarDecl] e |
| type_definitions.ts:19:5:19:24 | [VariableDeclarator] e: EnumWithOneMember | semmle.label | [VariableDeclarator] e: EnumWithOneMember |
| type_definitions.ts:19:8:19:24 | [LocalTypeAccess] EnumWithOneMember | semmle.label | [LocalTypeAccess] EnumWithOneMember |
| type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; |
-| type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.order | 121 |
+| type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.order | 122 |
| type_definitions.ts:21:6:21:10 | [Identifier] Alias | semmle.label | [Identifier] Alias |
| type_definitions.ts:21:12:21:12 | [Identifier] T | semmle.label | [Identifier] T |
| type_definitions.ts:21:12:21:12 | [TypeParameter] T | semmle.label | [TypeParameter] T |
| type_definitions.ts:21:17:21:17 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T |
| type_definitions.ts:21:17:21:19 | [ArrayTypeExpr] T[] | semmle.label | [ArrayTypeExpr] T[] |
| type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.label | [DeclStmt] let aliasForNumberArray = ... |
-| type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.order | 122 |
+| type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.order | 123 |
| type_definitions.ts:22:5:22:23 | [VarDecl] aliasForNumberArray | semmle.label | [VarDecl] aliasForNumberArray |
| type_definitions.ts:22:5:22:38 | [VariableDeclarator] aliasFo ... number> | semmle.label | [VariableDeclarator] aliasFo ... number> |
| type_definitions.ts:22:26:22:30 | [LocalTypeAccess] Alias | semmle.label | [LocalTypeAccess] Alias |
@@ -5534,6 +5561,56 @@ edges
| tst.ts:508:17:508:36 | [DotExpr] obj[key].toUpperCase | tst.ts:508:26:508:36 | [Label] toUpperCase | semmle.order | 2 |
| tst.ts:508:17:508:38 | [MethodCallExpr] obj[key ... rCase() | tst.ts:508:17:508:36 | [DotExpr] obj[key].toUpperCase | semmle.label | 0 |
| tst.ts:508:17:508:38 | [MethodCallExpr] obj[key ... rCase() | tst.ts:508:17:508:36 | [DotExpr] obj[key].toUpperCase | semmle.order | 0 |
+| tst.ts:513:1:520:1 | [NamespaceDeclaration] namespa ... type. } | tst.ts:513:11:513:14 | [VarDecl] TS57 | semmle.label | 1 |
+| tst.ts:513:1:520:1 | [NamespaceDeclaration] namespa ... type. } | tst.ts:513:11:513:14 | [VarDecl] TS57 | semmle.order | 1 |
+| tst.ts:513:1:520:1 | [NamespaceDeclaration] namespa ... type. } | tst.ts:514:3:514:26 | [DeclStmt] const a = ... | semmle.label | 2 |
+| tst.ts:513:1:520:1 | [NamespaceDeclaration] namespa ... type. } | tst.ts:514:3:514:26 | [DeclStmt] const a = ... | semmle.order | 2 |
+| tst.ts:513:1:520:1 | [NamespaceDeclaration] namespa ... type. } | tst.ts:515:3:517:3 | [ExportDeclaration] export ... }; } | semmle.label | 3 |
+| tst.ts:513:1:520:1 | [NamespaceDeclaration] namespa ... type. } | tst.ts:515:3:517:3 | [ExportDeclaration] export ... }; } | semmle.order | 3 |
+| tst.ts:513:1:520:1 | [NamespaceDeclaration] namespa ... type. } | tst.ts:519:3:519:32 | [DeclStmt] const e1 = ... | semmle.label | 4 |
+| tst.ts:513:1:520:1 | [NamespaceDeclaration] namespa ... type. } | tst.ts:519:3:519:32 | [DeclStmt] const e1 = ... | semmle.order | 4 |
+| tst.ts:514:3:514:26 | [DeclStmt] const a = ... | tst.ts:514:17:514:25 | [VariableDeclarator] a: symbol | semmle.label | 1 |
+| tst.ts:514:3:514:26 | [DeclStmt] const a = ... | tst.ts:514:17:514:25 | [VariableDeclarator] a: symbol | semmle.order | 1 |
+| tst.ts:514:17:514:25 | [VariableDeclarator] a: symbol | tst.ts:514:17:514:17 | [VarDecl] a | semmle.label | 1 |
+| tst.ts:514:17:514:25 | [VariableDeclarator] a: symbol | tst.ts:514:17:514:17 | [VarDecl] a | semmle.order | 1 |
+| tst.ts:514:17:514:25 | [VariableDeclarator] a: symbol | tst.ts:514:20:514:25 | [KeywordTypeExpr] symbol | semmle.label | 2 |
+| tst.ts:514:17:514:25 | [VariableDeclarator] a: symbol | tst.ts:514:20:514:25 | [KeywordTypeExpr] symbol | semmle.order | 2 |
+| tst.ts:515:3:517:3 | [ExportDeclaration] export ... }; } | tst.ts:515:10:517:3 | [ClassDefinition,TypeDefinition] class A ... }; } | semmle.label | 1 |
+| tst.ts:515:3:517:3 | [ExportDeclaration] export ... }; } | tst.ts:515:10:517:3 | [ClassDefinition,TypeDefinition] class A ... }; } | semmle.order | 1 |
+| tst.ts:515:10:517:3 | [ClassDefinition,TypeDefinition] class A ... }; } | tst.ts:515:16:515:16 | [VarDecl] A | semmle.label | 1 |
+| tst.ts:515:10:517:3 | [ClassDefinition,TypeDefinition] class A ... }; } | tst.ts:515:16:515:16 | [VarDecl] A | semmle.order | 1 |
+| tst.ts:515:10:517:3 | [ClassDefinition,TypeDefinition] class A ... }; } | tst.ts:515:18:515:17 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | 2 |
+| tst.ts:515:10:517:3 | [ClassDefinition,TypeDefinition] class A ... }; } | tst.ts:515:18:515:17 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.order | 2 |
+| tst.ts:515:10:517:3 | [ClassDefinition,TypeDefinition] class A ... }; } | tst.ts:516:7:516:24 | [ClassInitializedMember,MethodDefinition] [a]() { return 1 } | semmle.label | 3 |
+| tst.ts:515:10:517:3 | [ClassDefinition,TypeDefinition] class A ... }; } | tst.ts:516:7:516:24 | [ClassInitializedMember,MethodDefinition] [a]() { return 1 } | semmle.order | 3 |
+| tst.ts:515:18:515:17 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:515:18:515:17 | [FunctionExpr] () {} | semmle.label | 2 |
+| tst.ts:515:18:515:17 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:515:18:515:17 | [FunctionExpr] () {} | semmle.order | 2 |
+| tst.ts:515:18:515:17 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:515:18:515:17 | [Label] constructor | semmle.label | 1 |
+| tst.ts:515:18:515:17 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:515:18:515:17 | [Label] constructor | semmle.order | 1 |
+| tst.ts:515:18:515:17 | [FunctionExpr] () {} | tst.ts:515:18:515:17 | [BlockStmt] {} | semmle.label | 5 |
+| tst.ts:515:18:515:17 | [FunctionExpr] () {} | tst.ts:515:18:515:17 | [BlockStmt] {} | semmle.order | 5 |
+| tst.ts:516:7:516:24 | [ClassInitializedMember,MethodDefinition] [a]() { return 1 } | tst.ts:516:7:516:24 | [FunctionExpr] [a]() { return 1 } | semmle.label | 1 |
+| tst.ts:516:7:516:24 | [ClassInitializedMember,MethodDefinition] [a]() { return 1 } | tst.ts:516:7:516:24 | [FunctionExpr] [a]() { return 1 } | semmle.order | 1 |
+| tst.ts:516:7:516:24 | [ClassInitializedMember,MethodDefinition] [a]() { return 1 } | tst.ts:516:8:516:8 | [VarRef] a | semmle.label | 2 |
+| tst.ts:516:7:516:24 | [ClassInitializedMember,MethodDefinition] [a]() { return 1 } | tst.ts:516:8:516:8 | [VarRef] a | semmle.order | 2 |
+| tst.ts:516:7:516:24 | [FunctionExpr] [a]() { return 1 } | tst.ts:516:13:516:24 | [BlockStmt] { return 1 } | semmle.label | 5 |
+| tst.ts:516:7:516:24 | [FunctionExpr] [a]() { return 1 } | tst.ts:516:13:516:24 | [BlockStmt] { return 1 } | semmle.order | 5 |
+| tst.ts:516:13:516:24 | [BlockStmt] { return 1 } | tst.ts:516:15:516:22 | [ReturnStmt] return 1 | semmle.label | 1 |
+| tst.ts:516:13:516:24 | [BlockStmt] { return 1 } | tst.ts:516:15:516:22 | [ReturnStmt] return 1 | semmle.order | 1 |
+| tst.ts:516:15:516:22 | [ReturnStmt] return 1 | tst.ts:516:22:516:22 | [Literal] 1 | semmle.label | 1 |
+| tst.ts:516:15:516:22 | [ReturnStmt] return 1 | tst.ts:516:22:516:22 | [Literal] 1 | semmle.order | 1 |
+| tst.ts:519:3:519:32 | [DeclStmt] const e1 = ... | tst.ts:519:17:519:31 | [VariableDeclarator] e1: A[typeof a] | semmle.label | 1 |
+| tst.ts:519:3:519:32 | [DeclStmt] const e1 = ... | tst.ts:519:17:519:31 | [VariableDeclarator] e1: A[typeof a] | semmle.order | 1 |
+| tst.ts:519:17:519:31 | [VariableDeclarator] e1: A[typeof a] | tst.ts:519:17:519:18 | [VarDecl] e1 | semmle.label | 1 |
+| tst.ts:519:17:519:31 | [VariableDeclarator] e1: A[typeof a] | tst.ts:519:17:519:18 | [VarDecl] e1 | semmle.order | 1 |
+| tst.ts:519:17:519:31 | [VariableDeclarator] e1: A[typeof a] | tst.ts:519:21:519:31 | [IndexedAccessTypeExpr] A[typeof a] | semmle.label | 2 |
+| tst.ts:519:17:519:31 | [VariableDeclarator] e1: A[typeof a] | tst.ts:519:21:519:31 | [IndexedAccessTypeExpr] A[typeof a] | semmle.order | 2 |
+| tst.ts:519:21:519:31 | [IndexedAccessTypeExpr] A[typeof a] | tst.ts:519:21:519:21 | [LocalTypeAccess] A | semmle.label | 1 |
+| tst.ts:519:21:519:31 | [IndexedAccessTypeExpr] A[typeof a] | tst.ts:519:21:519:21 | [LocalTypeAccess] A | semmle.order | 1 |
+| tst.ts:519:21:519:31 | [IndexedAccessTypeExpr] A[typeof a] | tst.ts:519:23:519:30 | [TypeofTypeExpr] typeof a | semmle.label | 2 |
+| tst.ts:519:21:519:31 | [IndexedAccessTypeExpr] A[typeof a] | tst.ts:519:23:519:30 | [TypeofTypeExpr] typeof a | semmle.order | 2 |
+| tst.ts:519:23:519:30 | [TypeofTypeExpr] typeof a | tst.ts:519:30:519:30 | [LocalVarTypeAccess] a | semmle.label | 1 |
+| tst.ts:519:23:519:30 | [TypeofTypeExpr] typeof a | tst.ts:519:30:519:30 | [LocalVarTypeAccess] a | semmle.order | 1 |
| tstModuleCJS.cts:1:1:3:1 | [ExportDeclaration] export ... 'b'; } | tstModuleCJS.cts:1:8:3:1 | [FunctionDeclStmt] functio ... 'b'; } | semmle.label | 1 |
| tstModuleCJS.cts:1:1:3:1 | [ExportDeclaration] export ... 'b'; } | tstModuleCJS.cts:1:8:3:1 | [FunctionDeclStmt] functio ... 'b'; } | semmle.order | 1 |
| tstModuleCJS.cts:1:8:3:1 | [FunctionDeclStmt] functio ... 'b'; } | tstModuleCJS.cts:1:17:1:28 | [VarDecl] tstModuleCJS | semmle.label | 0 |
diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tests.expected b/javascript/ql/test/library-tests/TypeScript/Types/tests.expected
index b786fae3713e..150ce1867248 100644
--- a/javascript/ql/test/library-tests/TypeScript/Types/tests.expected
+++ b/javascript/ql/test/library-tests/TypeScript/Types/tests.expected
@@ -728,6 +728,13 @@ getExprType
| tst.ts:508:17:508:38 | obj[key ... rCase() | string |
| tst.ts:508:21:508:23 | key | string |
| tst.ts:508:26:508:36 | toUpperCase | () => string |
+| tst.ts:513:11:513:14 | TS57 | typeof TS57 in tst.ts |
+| tst.ts:514:17:514:17 | a | symbol |
+| tst.ts:515:16:515:16 | A | A |
+| tst.ts:516:7:516:24 | [a]() { return 1 } | () => number |
+| tst.ts:516:8:516:8 | a | symbol |
+| tst.ts:516:22:516:22 | 1 | 1 |
+| tst.ts:519:17:519:18 | e1 | () => number |
| tstModuleCJS.cts:1:17:1:28 | tstModuleCJS | () => "a" \| "b" |
| tstModuleCJS.cts:2:12:2:15 | Math | Math |
| tstModuleCJS.cts:2:12:2:22 | Math.random | () => number |
@@ -843,6 +850,7 @@ getTypeDefinitionType
| tst.ts:447:5:458:5 | class P ... }\\n } | Person |
| tst.ts:473:5:476:5 | class S ... ;\\n } | SomeClass |
| tst.ts:481:5:481:34 | type Pa ... T, T]; | Pair3 |
+| tst.ts:515:10:517:3 | class A ... };\\n } | A |
| type_alias.ts:1:1:1:17 | type B = boolean; | boolean |
| type_alias.ts:5:1:5:50 | type Va ... ay>; | ValueOrArray |
| type_alias.ts:9:1:15:13 | type Js ... Json[]; | Json |
@@ -1219,6 +1227,11 @@ getTypeExprType
| tst.ts:506:27:506:32 | string | string |
| tst.ts:506:35:506:41 | unknown | unknown |
| tst.ts:506:50:506:55 | string | string |
+| tst.ts:514:20:514:25 | symbol | symbol |
+| tst.ts:519:21:519:21 | A | A |
+| tst.ts:519:21:519:31 | A[typeof a] | () => number |
+| tst.ts:519:23:519:30 | typeof a | symbol |
+| tst.ts:519:30:519:30 | a | symbol |
| tstModuleCJS.cts:1:33:1:35 | 'a' | "a" |
| tstModuleCJS.cts:1:33:1:41 | 'a' \| 'b' | "a" \| "b" |
| tstModuleCJS.cts:1:39:1:41 | 'b' | "b" |
@@ -1290,6 +1303,7 @@ getTypeExprType
missingToString
referenceDefinition
| A | badTypes.ts:5:1:5:29 | interfa ... is.B {} |
+| A | tst.ts:515:10:517:3 | class A ... };\\n } |
| Action | tst.ts:252:3:254:50 | type Ac ... ring }; |
| Alias | type_definitions.ts:21:1:21:20 | type Alias = T[]; |
| Alias | type_definitions.ts:21:1:21:20 | type Alias = T[]; |
diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tst.ts b/javascript/ql/test/library-tests/TypeScript/Types/tst.ts
index 4c29465b5645..87f876be9d0f 100644
--- a/javascript/ql/test/library-tests/TypeScript/Types/tst.ts
+++ b/javascript/ql/test/library-tests/TypeScript/Types/tst.ts
@@ -508,4 +508,13 @@ module TS55 {
var str = obj[key].toUpperCase(); // Now okay, previously was error
}
}
-}
\ No newline at end of file
+}
+
+namespace TS57{
+ declare const a: symbol;
+ export class A {
+ [a]() { return 1 };
+ }
+
+ declare const e1: A[typeof a]; // Now okay, previously was compilation error TS2538: Type 'symbol' cannot be used as an index type.
+}
diff --git a/javascript/ql/test/query-tests/Statements/UseOfReturnlessFunction/UseOfReturnlessFunction.expected b/javascript/ql/test/query-tests/Statements/UseOfReturnlessFunction/UseOfReturnlessFunction.expected
index 1cbbd9faa5a3..dec3eea044d1 100644
--- a/javascript/ql/test/query-tests/Statements/UseOfReturnlessFunction/UseOfReturnlessFunction.expected
+++ b/javascript/ql/test/query-tests/Statements/UseOfReturnlessFunction/UseOfReturnlessFunction.expected
@@ -7,3 +7,5 @@
| tst.js:53:10:53:34 | bothOnl ... fects() | the $@ does not return anything, yet the return value is used. | tst.js:48:2:50:5 | functio ... )\\n } | function onlySideEffects2 |
| tst.js:76:12:76:46 | [1,2,3] ... n, 3)}) | the $@ does not return anything, yet the return value from the call to filter is used. | tst.js:76:27:76:45 | n => {equals(n, 3)} | callback function |
| tst.js:80:12:80:50 | filter( ... 3) } ) | the $@ does not return anything, yet the return value from the call to filter is used. | tst.js:80:28:80:48 | x => { ... x, 3) } | callback function |
+| tst.js:114:12:114:56 | [1,2,3] ... 3); }) | the $@ does not return anything, yet the return value from the call to findLastIndex is used. | tst.js:114:34:114:55 | n => { ... , 3); } | callback function |
+| tst.js:117:12:117:51 | [1,2,3] ... 3); }) | the $@ does not return anything, yet the return value from the call to findLast is used. | tst.js:117:29:117:50 | n => { ... , 3); } | callback function |
diff --git a/javascript/ql/test/query-tests/Statements/UseOfReturnlessFunction/tst.js b/javascript/ql/test/query-tests/Statements/UseOfReturnlessFunction/tst.js
index dbedb3dee3cc..7b9968115f5e 100644
--- a/javascript/ql/test/query-tests/Statements/UseOfReturnlessFunction/tst.js
+++ b/javascript/ql/test/query-tests/Statements/UseOfReturnlessFunction/tst.js
@@ -107,3 +107,13 @@ class Bar extends Foo {
console.log(super()); // OK.
}
}
+
+() => {
+ let equals = (x, y) => { return x === y; };
+
+ var foo = [1,2,3].findLastIndex(n => { equals(n, 3); }) // NOT OK
+ console.log(foo);
+
+ var foo = [1,2,3].findLast(n => { equals(n, 3); }) // NOT OK
+ console.log(foo);
+}
diff --git a/misc/codegen/codegen.py b/misc/codegen/codegen.py
index acc8e59bba5f..ae3a67d3fba6 100755
--- a/misc/codegen/codegen.py
+++ b/misc/codegen/codegen.py
@@ -54,6 +54,8 @@ def _parse_args() -> argparse.Namespace:
"generated qll file importing every class file"),
p.add_argument("--ql-test-output",
help="output directory for QL generated extractor test files"),
+ p.add_argument("--ql-cfg-output",
+ help="output directory for QL CFG layer (optional)."),
p.add_argument("--cpp-output",
help="output directory for generated C++ files, required if trap or cpp is provided to "
"--generate"),
diff --git a/misc/codegen/generators/qlgen.py b/misc/codegen/generators/qlgen.py
index c5e7153489b6..e42c9d015522 100755
--- a/misc/codegen/generators/qlgen.py
+++ b/misc/codegen/generators/qlgen.py
@@ -118,17 +118,18 @@ def get_ql_property(cls: schema.Class, prop: schema.Property, lookup: typing.Dic
type_is_hideable="ql_hideable" in lookup[prop.type].pragmas if prop.type in lookup else False,
internal="ql_internal" in prop.pragmas,
)
+ ql_name = prop.pragmas.get("ql_name", prop.name)
if prop.is_single:
args.update(
- singular=inflection.camelize(prop.name),
+ singular=inflection.camelize(ql_name),
tablename=inflection.tableize(cls.name),
tableparams=["this"] + ["result" if p is prop else "_" for p in cls.properties if p.is_single],
doc=_get_doc(cls, prop),
)
elif prop.is_repeated:
args.update(
- singular=inflection.singularize(inflection.camelize(prop.name)),
- plural=inflection.pluralize(inflection.camelize(prop.name)),
+ singular=inflection.singularize(inflection.camelize(ql_name)),
+ plural=inflection.pluralize(inflection.camelize(ql_name)),
tablename=inflection.tableize(f"{cls.name}_{prop.name}"),
tableparams=["this", "index", "result"] if not prop.is_unordered else ["this", "result"],
doc=_get_doc(cls, prop, plural=False),
@@ -136,14 +137,14 @@ def get_ql_property(cls: schema.Class, prop: schema.Property, lookup: typing.Dic
)
elif prop.is_optional:
args.update(
- singular=inflection.camelize(prop.name),
+ singular=inflection.camelize(ql_name),
tablename=inflection.tableize(f"{cls.name}_{prop.name}"),
tableparams=["this", "result"],
doc=_get_doc(cls, prop),
)
elif prop.is_predicate:
args.update(
- singular=inflection.camelize(prop.name, uppercase_first_letter=False),
+ singular=inflection.camelize(ql_name, uppercase_first_letter=False),
tablename=inflection.underscore(f"{cls.name}_{prop.name}"),
tableparams=["this"],
doc=_get_doc(cls, prop),
@@ -154,12 +155,16 @@ def get_ql_property(cls: schema.Class, prop: schema.Property, lookup: typing.Dic
def get_ql_class(cls: schema.Class, lookup: typing.Dict[str, schema.Class]) -> ql.Class:
+ if "ql_name" in cls.pragmas:
+ raise Error("ql_name is not supported yet for classes, only for properties")
prev_child = ""
properties = []
for p in cls.properties:
prop = get_ql_property(cls, p, lookup, prev_child)
if prop.is_child:
prev_child = prop.singular
+ if prop.type in lookup and lookup[prop.type].cfg:
+ prop.cfg = True
properties.append(prop)
return ql.Class(
name=cls.name,
@@ -171,6 +176,16 @@ def get_ql_class(cls: schema.Class, lookup: typing.Dict[str, schema.Class]) -> q
doc=cls.doc,
hideable="ql_hideable" in cls.pragmas,
internal="ql_internal" in cls.pragmas,
+ cfg=cls.cfg,
+ )
+
+
+def get_ql_cfg_class(cls: schema.Class, lookup: typing.Dict[str, ql.Class]) -> ql.CfgClass:
+ return ql.CfgClass(
+ name=cls.name,
+ bases=[base for base in cls.bases if lookup[base.base].cfg],
+ properties=cls.properties,
+ doc=cls.doc
)
@@ -361,6 +376,7 @@ def generate(opts, renderer):
input = opts.schema
out = opts.ql_output
stub_out = opts.ql_stub_output
+ cfg_out = opts.ql_cfg_output
test_out = opts.ql_test_output
missing_test_source_filename = "MISSING_SOURCE.txt"
include_file = stub_out.with_suffix(".qll")
@@ -385,6 +401,7 @@ def generate(opts, renderer):
imports = {}
imports_impl = {}
classes_used_by = {}
+ cfg_classes = []
generated_import_prefix = get_import(out, opts.root_dir)
registry = opts.generated_registry or pathlib.Path(
os.path.commonpath((out, stub_out, test_out)), ".generated.list")
@@ -402,6 +419,8 @@ def generate(opts, renderer):
imports[c.name] = path
path_impl = get_import(stub_out / c.dir / "internal" / c.name, opts.root_dir)
imports_impl[c.name + "Impl"] = path_impl + "Impl"
+ if c.cfg:
+ cfg_classes.append(get_ql_cfg_class(c, classes))
for c in classes.values():
qll = out / c.path.with_suffix(".qll")
@@ -411,6 +430,14 @@ def generate(opts, renderer):
c.import_prefix = generated_import_prefix
renderer.render(c, qll)
+ if cfg_out:
+ cfg_classes_val = ql.CfgClasses(
+ include_file_import=get_import(include_file, opts.root_dir),
+ classes=cfg_classes
+ )
+ cfg_qll = cfg_out / "CfgNodes.qll"
+ renderer.render(cfg_classes_val, cfg_qll)
+
for c in data.classes.values():
path = _get_path(c)
path_impl = _get_path_impl(c)
diff --git a/misc/codegen/lib/ql.py b/misc/codegen/lib/ql.py
index 1920a813e20d..1182c7f5dc16 100644
--- a/misc/codegen/lib/ql.py
+++ b/misc/codegen/lib/ql.py
@@ -45,6 +45,7 @@ class Property:
synth: bool = False
type_is_hideable: bool = False
internal: bool = False
+ cfg: bool = False
def __post_init__(self):
if self.tableparams:
@@ -110,6 +111,7 @@ class Class:
internal: bool = False
doc: List[str] = field(default_factory=list)
hideable: bool = False
+ cfg: bool = False
def __post_init__(self):
def get_bases(bases): return [Base(str(b), str(prev)) for b, prev in zip(bases, itertools.chain([""], bases))]
@@ -333,3 +335,18 @@ class ConstructorStub:
cls: "Synth.FinalClass"
import_prefix: str
+
+
+@dataclass
+class CfgClass:
+ name: str
+ bases: List[Base] = field(default_factory=list)
+ properties: List[Property] = field(default_factory=list)
+ doc: List[str] = field(default_factory=list)
+
+
+@dataclass
+class CfgClasses:
+ template: ClassVar = 'ql_cfg_nodes'
+ include_file_import: Optional[str] = None
+ classes: List[CfgClass] = field(default_factory=list)
diff --git a/misc/codegen/lib/schema.py b/misc/codegen/lib/schema.py
index c5c3dc810b68..23f1aea2ba42 100644
--- a/misc/codegen/lib/schema.py
+++ b/misc/codegen/lib/schema.py
@@ -94,6 +94,7 @@ class Class:
properties: List[Property] = field(default_factory=list)
pragmas: List[str] | Dict[str, object] = field(default_factory=dict)
doc: List[str] = field(default_factory=list)
+ cfg: bool = False
def __post_init__(self):
if not isinstance(self.pragmas, dict):
diff --git a/misc/codegen/lib/schemadefs.py b/misc/codegen/lib/schemadefs.py
index 997b85b4ca6a..8651240c1a3d 100644
--- a/misc/codegen/lib/schemadefs.py
+++ b/misc/codegen/lib/schemadefs.py
@@ -72,11 +72,11 @@ def include(source: str):
@_dataclass
class _Namespace:
""" simple namespacing mechanism """
- name: str
+ _name: str
def add(self, pragma: "_PragmaBase", key: str | None = None):
self.__dict__[pragma.pragma] = pragma
- pragma.pragma = key or f"{self.name}_{pragma.pragma}"
+ pragma.pragma = key or f"{self._name}_{pragma.pragma}"
@_dataclass
@@ -87,7 +87,7 @@ def modify(self, prop: _schema.Property):
prop.synth = self.synth
def negate(self) -> _schema.PropertyModifier:
- return _SynthModifier(self.name, False)
+ return _SynthModifier(self._name, False)
qltest = _Namespace("qltest")
@@ -239,6 +239,7 @@ def __getitem__(self, item):
ql.add(_ParametrizedClassPragma("default_doc_name", factory=lambda doc: doc))
ql.add(_ClassPragma("hideable", inherited=True))
ql.add(_Pragma("internal"))
+ql.add(_ParametrizedPragma("name", factory=lambda name: name))
cpp.add(_Pragma("skip"))
@@ -256,30 +257,24 @@ def __getitem__(self, item):
_schema.SynthInfo(on_arguments={k: _schema.get_type_name(t) for k, t in kwargs.items()})), key="synth")
+@_dataclass(frozen=True)
class _PropertyModifierList(_schema.PropertyModifier):
- def __init__(self):
- self._mods = []
+ _mods: tuple[_schema.PropertyModifier, ...]
def __or__(self, other: _schema.PropertyModifier):
- self._mods.append(other)
- return self
+ return _PropertyModifierList(self._mods + (other,))
def modify(self, prop: Property):
for m in self._mods:
m.modify(prop)
-class _PropertyAnnotation:
- def __or__(self, other: _schema.PropertyModifier):
- return _PropertyModifierList() | other
-
-
-_ = _PropertyAnnotation()
+_ = _PropertyModifierList(())
drop = object()
-def annotate(annotated_cls: type, add_bases: _Iterable[type] | None = None, replace_bases: _Dict[type, type] | None = None) -> _Callable[[type], _PropertyAnnotation]:
+def annotate(annotated_cls: type, add_bases: _Iterable[type] | None = None, replace_bases: _Dict[type, type] | None = None, cfg: bool = False) -> _Callable[[type], _PropertyModifierList]:
"""
Add or modify schema annotations after a class has been defined previously.
@@ -287,7 +282,7 @@ def annotate(annotated_cls: type, add_bases: _Iterable[type] | None = None, repl
`replace_bases` can be used to replace bases on the annotated class.
"""
- def decorator(cls: type) -> _PropertyAnnotation:
+ def decorator(cls: type) -> _PropertyModifierList:
if cls.__name__ != "_":
raise _schema.Error("Annotation classes must be named _")
if cls.__doc__ is not None:
@@ -298,6 +293,7 @@ def decorator(cls: type) -> _PropertyAnnotation:
annotated_cls.__bases__ = tuple(replace_bases.get(b, b) for b in annotated_cls.__bases__)
if add_bases:
annotated_cls.__bases__ += tuple(add_bases)
+ annotated_cls.__cfg__ = cfg
for a in dir(cls):
if a.startswith(_schema.inheritable_pragma_prefix):
setattr(annotated_cls, a, getattr(cls, a))
@@ -306,7 +302,7 @@ def decorator(cls: type) -> _PropertyAnnotation:
del annotated_cls.__annotations__[p]
elif p in annotated_cls.__annotations__:
annotated_cls.__annotations__[p] |= a
- elif isinstance(a, (_PropertyAnnotation, _PropertyModifierList)):
+ elif isinstance(a, (_PropertyModifierList, _PropertyModifierList)):
raise _schema.Error(f"annotated property {p} not present in annotated class "
f"{annotated_cls.__name__}")
else:
diff --git a/misc/codegen/loaders/schemaloader.py b/misc/codegen/loaders/schemaloader.py
index 069e3b654740..dd1edee1de09 100644
--- a/misc/codegen/loaders/schemaloader.py
+++ b/misc/codegen/loaders/schemaloader.py
@@ -53,6 +53,7 @@ def _get_class(cls: type) -> schema.Class:
bases=[b.__name__ for b in cls.__bases__ if b is not object],
derived=derived,
pragmas=pragmas,
+ cfg=cls.__cfg__ if hasattr(cls, "__cfg__") else False,
# in the following we don't use `getattr` to avoid inheriting
properties=[
a | _PropertyNamer(n)
diff --git a/misc/codegen/templates/ql_cfg_nodes.mustache b/misc/codegen/templates/ql_cfg_nodes.mustache
new file mode 100644
index 000000000000..a2edf4410be0
--- /dev/null
+++ b/misc/codegen/templates/ql_cfg_nodes.mustache
@@ -0,0 +1,199 @@
+// generated by {{generator}}, do not edit
+/**
+ * This module provides generated wrappers around the `CfgNode` type.
+ *
+ * INTERNAL: Do not import directly.
+ */
+
+private import codeql.util.Location
+private import codeql.util.Unit
+private import {{include_file_import}}
+
+/** Provides the input to `MakeCfgNodes` */
+signature module InputSig {
+ class CfgNode {
+ AstNode getAstNode();
+
+ string toString();
+
+ Loc getLocation();
+ }
+
+ AstNode getDesugared(AstNode n);
+}
+
+/**
+ * Given a `CfgNode` implementation, provides the module `Nodes` that
+ * contains wrappers around `CfgNode` for relevant classes.
+ */
+module MakeCfgNodes Input> {
+ private import Input
+
+ final private class AstNodeFinal = AstNode;
+
+ final private class CfgNodeFinal = CfgNode;
+
+ /**
+ * INTERNAL: Do not expose.
+ */
+ abstract class ParentAstNode extends AstNodeFinal {
+ /**
+ * Holds if `child` is a (possibly nested) child of this AST node
+ * for which we would like to find a matching CFG child.
+ */
+ abstract predicate relevantChild(AstNode child);
+ }
+
+ /**
+ * INTERNAL: Do not expose.
+ */
+ abstract class ChildMapping extends Unit {
+ /**
+ * Holds if `child` is a (possibly nested) child of AST node `parent`
+ * for which we would like to find a matching CFG child.
+ */
+ final predicate relevantChild(AstNode parent, AstNode child) {
+ parent.(ParentAstNode).relevantChild(child)
+ }
+
+ /**
+ * Holds if there is a control flow path from `cfn` to `cfnChild`, where `cfn`
+ * is a control flow node for this AST node, and `cfnChild` is a control flow
+ * node for `child`.
+ *
+ * This predicate should be implemented at the place where `MakeCfgNodes` is
+ * invoked. Ideally, `MakeCfgNodes` should be a higher-order parameterized
+ * module, but since that is currently not supported, we achieve the "callback"
+ * effect using this `abstract` class instead.
+ */
+ cached
+ abstract predicate hasCfgChild(AstNode parent, AstNode child, CfgNode cfn, CfgNode cfnChild);
+ }
+
+ /** Provides sub classes of `CfgNode`. */
+ module Nodes {
+ {{#classes}}
+ private final class Parent{{name}} extends ParentAstNode, {{name}} {
+ override predicate relevantChild(AstNode child) {
+ none()
+ {{#properties}}
+ {{#cfg}}
+ or
+ child = this.{{getter}}({{#is_indexed}}_{{/is_indexed}})
+ {{/cfg}}
+ {{/properties}}
+ }
+ }
+
+ /**
+ {{#doc}}
+ * {{.}}
+ {{/doc}}
+ */
+ final class {{name}}CfgNode extends CfgNodeFinal{{#bases}}, {{.}}CfgNode{{/bases}} {
+ private {{name}} node;
+
+ {{name}}CfgNode() {
+ node = this.getAstNode()
+ }
+
+ /** Gets the underlying `{{name}}`. */
+ {{name}} get{{name}}() { result = node }
+
+ {{#properties}}
+ /**
+ * {{>ql_property_doc}} *
+ {{#description}}
+ * {{.}}
+ {{/description}}
+ {{#internal}}
+ * INTERNAL: Do not use.
+ {{/internal}}
+ */
+ {{type}}{{#cfg}}CfgNode{{/cfg}} {{getter}}({{#is_indexed}}int index{{/is_indexed}}) {
+ {{#cfg}}
+ any(ChildMapping mapping).hasCfgChild(node, node.{{getter}}({{#is_indexed}}index{{/is_indexed}}), this, result)
+ {{/cfg}}
+ {{^cfg}}
+ {{^is_predicate}}result = {{/is_predicate}}node.{{getter}}({{#is_indexed}}index{{/is_indexed}})
+ {{/cfg}}
+ }
+
+ {{#is_optional}}
+ /**
+ * Holds if `{{getter}}({{#is_repeated}}index{{/is_repeated}})` exists.
+ {{#internal}}
+ * INTERNAL: Do not use.
+ {{/internal}}
+ */
+ predicate has{{singular}}({{#is_repeated}}int index{{/is_repeated}}) {
+ exists(this.{{getter}}({{#is_repeated}}index{{/is_repeated}}))
+ }
+ {{/is_optional}}
+ {{#is_indexed}}
+
+ /**
+ * Gets any of the {{doc_plural}}.
+ {{#internal}}
+ * INTERNAL: Do not use.
+ {{/internal}}
+ */
+ {{type}}{{#cfg}}CfgNode{{/cfg}} {{indefinite_getter}}() {
+ result = this.{{getter}}(_)
+ }
+ {{^is_optional}}
+
+ /**
+ * Gets the number of {{doc_plural}}.
+ {{#internal}}
+ * INTERNAL: Do not use.
+ {{/internal}}
+ */
+ int getNumberOf{{plural}}() {
+ result = count(int i | exists(this.{{getter}}(i)))
+ }
+ {{/is_optional}}
+ {{/is_indexed}}
+ {{#is_unordered}}
+ /**
+ * Gets the number of {{doc_plural}}.
+ {{#internal}}
+ * INTERNAL: Do not use.
+ {{/internal}}
+ */
+ int getNumberOf{{plural}}() {
+ result = count(this.{{getter}}())
+ }
+ {{/is_unordered}}
+ {{/properties}}
+ }
+ {{/classes}}
+ }
+
+ module Consistency {
+ private predicate hasCfgNode(AstNode astNode) {
+ astNode = any(CfgNode cfgNode).getAstNode()
+ }
+
+ query predicate missingCfgChild(CfgNode parent, string pred, int i, AstNode child) {
+ none()
+ {{#classes}}
+ {{#properties}}
+ {{#cfg}}
+ or
+ pred = "{{getter}}" and
+ parent = any(Nodes::{{name}}CfgNode cfgNode, {{name}} astNode |
+ astNode = cfgNode.get{{name}}() and
+ child = getDesugared(astNode.{{getter}}({{#is_indexed}}i{{/is_indexed}}))
+ {{^is_indexed}}and i = -1{{/is_indexed}} and
+ hasCfgNode(child) and
+ not child = cfgNode.{{getter}}({{#is_indexed}}i{{/is_indexed}}).getAstNode()
+ |
+ cfgNode
+ )
+ {{/cfg}}
+ {{/properties}}
+ {{/classes}}
+ }
+ }
+}
\ No newline at end of file
diff --git a/misc/scripts/create-change-note.py b/misc/scripts/create-change-note.py
new file mode 100644
index 000000000000..548fa4e87fb0
--- /dev/null
+++ b/misc/scripts/create-change-note.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+
+# Creates a change note and opens it in VSCode for editing.
+
+# Expects to receive the following arguments:
+# - What language the change note is for
+# - Whether it's a query or library change (the string `src` or `lib`)
+# - The name of the change note (in kebab-case)
+# - The category of the change.
+
+# The change note will be created in the `{language}/ql/{subdir}/change-notes` directory, where `subdir` is either `src` or `lib`.
+
+# The format of the change note filename is `{current_date}-{change_note_name}.md` with the date in
+# the format `YYYY-MM-DD`.
+
+import sys
+import os
+
+# Read the given arguments
+language = sys.argv[1]
+subdir = sys.argv[2]
+change_note_name = sys.argv[3]
+change_category = sys.argv[4]
+
+# Find the root of the repository. The current script should be located in `misc/scripts`.
+root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+# Go to the repo root
+os.chdir(root)
+
+output_dir = f"{language}/ql/{subdir}/change-notes"
+
+# Abort if the output directory doesn't exist
+if not os.path.exists(output_dir):
+ print(f"Output directory {output_dir} does not exist")
+ sys.exit(1)
+
+# Get the current date
+import datetime
+current_date = datetime.datetime.now().strftime("%Y-%m-%d")
+
+# Create the change note file
+change_note_file = f"{output_dir}/{current_date}-{change_note_name}.md"
+
+change_note = f"""
+---
+category: {change_category}
+---
+* """.lstrip()
+
+with open(change_note_file, "w") as f:
+ f.write(change_note)
+
+# Open the change note file in VSCode, reusing the existing window if possible
+os.system(f"code -r {change_note_file}")
diff --git a/python/extractor/semmle/python/passes/pruner.py b/python/extractor/semmle/python/passes/pruner.py
index d6363e529f18..fe3b03d453cd 100644
--- a/python/extractor/semmle/python/passes/pruner.py
+++ b/python/extractor/semmle/python/passes/pruner.py
@@ -196,6 +196,25 @@ def visit_Attribute(self, node):
if isinstance(node.value, ast.Name):
self.nodes.add(node.value)
+class NotBooleanTestVisitor(ASTVisitor):
+ """Visitor that checks if a test is not a boolean test."""
+
+ def __init__(self):
+ self.nodes = set()
+
+ def visit_MatchLiteralPattern(self, node):
+ # MatchLiteralPatterns _look_ like boolean tests, but are not.
+ # Thus, without this check, we would interpret
+ #
+ # match x:
+ # case False:
+ # pass
+ #
+ # (and similarly for True) as if it was a boolean test. This would cause the true edge
+ # (leading to pass) to be pruned later on.
+ if isinstance(node.literal, ast.Name) and node.literal.id in ('True', 'False'):
+ self.nodes.add(node.literal)
+
class NonlocalVisitor(ASTVisitor):
def __init__(self):
self.names = set()
@@ -306,6 +325,8 @@ def effective_constants_definitions(bool_const_defns, graph, branching_edges):
def do_pruning(tree, graph):
v = BoolConstVisitor()
v.visit(tree)
+ not_boolean_test = NotBooleanTestVisitor()
+ not_boolean_test.visit(tree)
nonlocals = NonlocalVisitor()
nonlocals.visit(tree)
global_vars = GlobalVisitor()
@@ -353,6 +374,8 @@ def do_pruning(tree, graph):
b = const_value(pred.node)
if b is None:
continue
+ if pred.node in not_boolean_test.nodes:
+ continue
if b.contradicts(val):
to_be_removed.add((pred, succ))
if not to_be_removed:
diff --git a/python/extractor/semmle/util.py b/python/extractor/semmle/util.py
index 1763d24e99e7..e0720a86312b 100644
--- a/python/extractor/semmle/util.py
+++ b/python/extractor/semmle/util.py
@@ -10,7 +10,7 @@
#Semantic version of extractor.
#Update this if any changes are made
-VERSION = "7.1.1"
+VERSION = "7.1.2"
PY_EXTENSIONS = ".py", ".pyw"
diff --git a/python/ql/lib/change-notes/2024-11-26-fix-match-cfg-pruning.md b/python/ql/lib/change-notes/2024-11-26-fix-match-cfg-pruning.md
new file mode 100644
index 000000000000..3ee1094c13b7
--- /dev/null
+++ b/python/ql/lib/change-notes/2024-11-26-fix-match-cfg-pruning.md
@@ -0,0 +1,5 @@
+---
+category: fix
+---
+
+- Fixed a problem with the control-flow graph construction, where writing `case True:` or `case False:` would cause parts of the graph to be pruned by mistake.
diff --git a/python/ql/test/library-tests/dataflow/global-or-captured-vars/test.expected b/python/ql/test/library-tests/dataflow/global-or-captured-vars/test.expected
new file mode 100644
index 000000000000..366de37b8677
--- /dev/null
+++ b/python/ql/test/library-tests/dataflow/global-or-captured-vars/test.expected
@@ -0,0 +1,4 @@
+argumentToEnsureNotTaintedNotMarkedAsSpurious
+untaintedArgumentToEnsureTaintedNotMarkedAsMissing
+testFailures
+failures
diff --git a/python/ql/test/library-tests/dataflow/global-or-captured-vars/test.py b/python/ql/test/library-tests/dataflow/global-or-captured-vars/test.py
new file mode 100644
index 000000000000..7719021890f8
--- /dev/null
+++ b/python/ql/test/library-tests/dataflow/global-or-captured-vars/test.py
@@ -0,0 +1,118 @@
+import threading
+import time
+
+# Test 1
+# TP - Flow is tracked through a global variable
+foo1 = None
+
+def bar1():
+ time.sleep(1)
+ ensure_tainted(foo1) # $tainted
+
+# The intent of these tests is to test how dataflow is handled through shared state accessed by different threads;
+# but the presense or absense of the actual call to start a thread does not affect the results (there is no special modelling for Thread)
+# threading.Thread(target=bar).start()
+
+foo1 = TAINTED_STRING
+
+# Test 2
+# FN - Flow is *not* tracked through an access path on a global variable
+foo2 = []
+
+def bar2():
+ time.sleep(1)
+ ensure_tainted(foo2[0]) # $MISSING:tainted
+
+threading.Thread(target=bar2).start()
+
+foo2.append(TAINTED_STRING)
+
+# Test 3
+# FN - Flow is not found even when there is a direct call
+foo3 = []
+
+def bar3():
+ time.sleep(1)
+ ensure_tainted(foo2[0]) # $MISSING:tainted
+
+foo3.append(TAINTED_STRING)
+bar3()
+
+# Tast 4
+# TP - Sanity check: Flow is found through a ListElement directly without a call
+foo4 = []
+foo4.append(TAINTED_STRING)
+ensure_tainted(foo4[0]) # $tainted
+
+# Test 5
+# FN - Flow is *not* tracked through a shared captured but non-global variable
+def test5():
+ foo5 = None
+
+ def bar5():
+ time.sleep(1)
+ ensure_tainted(foo5) # $MISSING:tainted
+
+ threading.Thread(target=bar5).start() # Only the presense of this thread call makes this an FN rather than a TN
+
+ foo5 = TAINTED_STRING
+
+ # Test 6
+ # TP - Flow is tracked through a shared captured but non-global variable with a direct call
+def test6():
+ foo6 = []
+
+ def bar6():
+ time.sleep(1)
+ ensure_tainted(foo6[0]) # $tainted
+
+ foo6.append(TAINTED_STRING)
+ bar6()
+
+
+# Test 7
+# FN - Flow is *not* found through an access path on a global variable that's also used as a parameter
+# We'd like to cover this case in order to be able to cover this CVE: https://github.com/github/codeql-python-CVE-coverage/issues/3176
+
+foo7 = []
+
+def bar7():
+ time.sleep(1)
+ ensure_tainted(foo7[0]) # $MISSING: tainted
+
+def baz7(loc_foo):
+ loc_foo.append(TAINTED_STRING)
+
+threading.Thread(target=bar7).start()
+
+baz7(foo7)
+
+# Test 8
+# FN - Flow is also *not* found in the above case through a direct call
+
+foo8 = []
+
+def bar8():
+ time.sleep(1)
+ ensure_tainted(foo8[0]) # $MISSING: tainted
+
+def baz8(loc_foo):
+ loc_foo.append(TAINTED_STRING)
+
+baz8(foo8)
+bar8()
+
+# Test 9
+# TP - Flow is found in the above case when the variable is captured rather than global
+
+def test9():
+ foo9 = []
+ def bar9():
+ time.sleep(1)
+ ensure_tainted(foo9[0]) # $tainted
+
+ def baz9(loc_foo):
+ loc_foo.append(TAINTED_STRING)
+
+ baz9(foo9)
+ bar9()
\ No newline at end of file
diff --git a/python/ql/test/library-tests/dataflow/global-or-captured-vars/test.ql b/python/ql/test/library-tests/dataflow/global-or-captured-vars/test.ql
new file mode 100644
index 000000000000..dd01b5d3e341
--- /dev/null
+++ b/python/ql/test/library-tests/dataflow/global-or-captured-vars/test.ql
@@ -0,0 +1,3 @@
+import python
+import experimental.meta.InlineTaintTest
+import MakeInlineTaintTest
diff --git a/python/ql/test/query-tests/Statements/unreachable/UnreachableCode.expected b/python/ql/test/query-tests/Statements/unreachable/UnreachableCode.expected
index 013d7a0fe68d..2417041f472d 100644
--- a/python/ql/test/query-tests/Statements/unreachable/UnreachableCode.expected
+++ b/python/ql/test/query-tests/Statements/unreachable/UnreachableCode.expected
@@ -4,5 +4,3 @@
| test.py:21:5:21:38 | For | This statement is unreachable. |
| test.py:28:9:28:21 | ExprStmt | This statement is unreachable. |
| test.py:84:5:84:21 | ExceptStmt | This statement is unreachable. |
-| test.py:144:13:144:16 | Pass | This statement is unreachable. |
-| test.py:147:9:148:16 | Case | This statement is unreachable. |
diff --git a/rust/ast-generator/src/main.rs b/rust/ast-generator/src/main.rs
index 665ffbac6c75..dbc9686a90f6 100644
--- a/rust/ast-generator/src/main.rs
+++ b/rust/ast-generator/src/main.rs
@@ -14,24 +14,29 @@ fn project_root() -> PathBuf {
PathBuf::from(dir).parent().unwrap().to_owned()
}
-fn class_name(type_name: &String) -> String {
- match type_name.as_str() {
- "BinExpr" => "BinaryExpr".to_owned(),
- "ElseBranch" => "Expr".to_owned(),
- "Fn" => "Function".to_owned(),
- "Literal" => "LiteralExpr".to_owned(),
- "Type" => "TypeRef".to_owned(),
- _ => type_name.to_owned(),
- }
+fn class_name(type_name: &str) -> String {
+ let name = match type_name {
+ "BinExpr" => "BinaryExpr",
+ "ElseBranch" => "Expr",
+ "Fn" => "Function",
+ "Literal" => "LiteralExpr",
+ "Type" => "TypeRef",
+ _ => type_name,
+ };
+ name.to_owned()
}
-fn property_name(type_name: &String, field_name: &String) -> String {
- match (type_name.as_str(), field_name.as_str()) {
- ("Path", "segment") => "part".to_owned(),
- (_, "then_branch") => "then".to_owned(),
- (_, "else_branch") => "else_".to_owned(),
- _ => field_name.to_owned(),
- }
+fn property_name(type_name: &str, field_name: &str) -> String {
+ let name = match (type_name, field_name) {
+ ("CallExpr", "expr") => "function",
+ ("LetExpr", "expr") => "scrutinee",
+ ("MatchExpr", "expr") => "scrutinee",
+ ("Path", "segment") => "part",
+ (_, "then_branch") => "then",
+ (_, "else_branch") => "else_",
+ _ => field_name,
+ };
+ name.to_owned()
}
fn to_lower_snake_case(s: &str) -> String {
@@ -61,7 +66,7 @@ fn write_schema(
for node in &grammar.enums {
let super_classses = if let Some(cls) = super_types.get(&node.name) {
- let super_classes: Vec