From 4195eef9ba0f455a65d22018010578aaa1799db3 Mon Sep 17 00:00:00 2001 From: haby0 Date: Tue, 15 Mar 2022 15:15:38 +0800 Subject: [PATCH 001/125] Add CSV injection model --- .../Security/CWE-1236/CsvInjection.py | 56 ++++++++++++++ .../Security/CWE-1236/CsvInjection.qhelp | 29 ++++++++ .../Security/CWE-1236/CsvInjection.ql | 20 +++++ .../experimental/semmle/python/Concepts.qll | 30 ++++++++ .../experimental/semmle/python/Frameworks.qll | 1 + .../semmle/python/frameworks/Csv.qll | 73 +++++++++++++++++++ .../security/injection/CsvInjection.qll | 24 ++++++ .../Security/CWE-1236/CsvInjection.expected | 19 +++++ .../Security/CWE-1236/CsvInjection.qlref | 1 + .../query-tests/Security/CWE-1236/csv_bad.py | 31 ++++++++ 10 files changed, 284 insertions(+) create mode 100644 python/ql/src/experimental/Security/CWE-1236/CsvInjection.py create mode 100644 python/ql/src/experimental/Security/CWE-1236/CsvInjection.qhelp create mode 100644 python/ql/src/experimental/Security/CWE-1236/CsvInjection.ql create mode 100644 python/ql/src/experimental/semmle/python/frameworks/Csv.qll create mode 100644 python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll create mode 100644 python/ql/test/experimental/query-tests/Security/CWE-1236/CsvInjection.expected create mode 100644 python/ql/test/experimental/query-tests/Security/CWE-1236/CsvInjection.qlref create mode 100644 python/ql/test/experimental/query-tests/Security/CWE-1236/csv_bad.py diff --git a/python/ql/src/experimental/Security/CWE-1236/CsvInjection.py b/python/ql/src/experimental/Security/CWE-1236/CsvInjection.py new file mode 100644 index 00000000000..4c436592bc9 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-1236/CsvInjection.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +""" +@Desc :csv injection +""" +import copy +import csv +from flask import Flask +from flask import request +from typing import List + +app = Flask(__name__) + +@app.route('/bad1') +def bad1(): + csv_data = request.args.get('csv') + csvWriter = csv.writer(open("test.csv", "wt")) + csvWriter.writerow(csv_data) + return "bad1" + +@app.route('/good1') +def good1(): + csv_data = request.args.get('csv') + csvWriter = csv.writer(open("test.csv", "wt")) + csvWriter.writerow(santize_for_csv(csv_data)) + return "good1" + +def santize_for_csv(data: str| List[str] | List[List[str]]): + def sanitize(item): + return "'" + item + + unsafe_prefixes = ("+", "=", "-", "@") + if isinstance(data, str): + if data.startswith(unsafe_prefixes): + return sanitize(data) + return data + elif isinstance(data, list) and isinstance(data[0], str): + sanitized_data = copy.deepcopy(data) + for index, item in enumerate(data): + if item.startswith(unsafe_prefixes): + sanitized_data[index] = sanitize(item) + return sanitized_data + elif isinstance(data[0], list) and isinstance(data[0][0], str): + sanitized_data = copy.deepcopy(data) + for outer_index, sublist in enumerate(data): + for inner_index, item in enumerate(sublist): + if item.startswith(unsafe_prefixes): + sanitized_data[outer_index][inner_index] = sanitize(item) + return sanitized_data + else: + raise ValueError("Unsupported data type: " + str(type(data))) + + +if __name__ == '__main__': + app.debug = True + app.run() \ No newline at end of file diff --git a/python/ql/src/experimental/Security/CWE-1236/CsvInjection.qhelp b/python/ql/src/experimental/Security/CWE-1236/CsvInjection.qhelp new file mode 100644 index 00000000000..237bf9493a8 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-1236/CsvInjection.qhelp @@ -0,0 +1,29 @@ + + + +

CSV Injection, also known as Formula Injection, occurs when websites embed untrusted input inside CSV files.

+

When a CSV format file is opened with a spreadsheet program such as Microsoft Excel or LibreOffice Calc. +this software interprets entries beginning with = as formulas. may attempt information exfiltration +or other malicious activity when automatically executed by the spreadsheet software.

+
+ + +

When generating CSV output, ensure that formula-sensitive metacharacters are effectively escaped or removed from all data before storage in the resultant CSV. +Risky characters include =(equal), +(plus), -(minus), and @(at).

+ +
+ + +

The following examples show the bad case and the good case respectively. +In bad1 method, the data provided by the user is directly stored in the CSV file, which may be attacked. +But in the good1 method,, the program will check the data provided by the user, and process the data starting with =(equal), +(plus), -(minus), and @(at) characters safely.

+ + + +
+ +
  • OWASP: CSV Injection.
  • +
    +
    diff --git a/python/ql/src/experimental/Security/CWE-1236/CsvInjection.ql b/python/ql/src/experimental/Security/CWE-1236/CsvInjection.ql new file mode 100644 index 00000000000..a570461add1 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-1236/CsvInjection.ql @@ -0,0 +1,20 @@ +/** + * @name Csv Injection + * @description From user-controlled data saved in CSV files, it is easy to attempt information disclosure + * or other malicious activities when automated by spreadsheet software + * @kind path-problem + * @problem.severity error + * @id py/csv-injection + * @tags security + * external/cwe/cwe-1236 + */ + +import python +import DataFlow::PathGraph +import semmle.python.dataflow.new.DataFlow +import experimental.semmle.python.security.injection.CsvInjection + +from CsvInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Csv injection might include code from $@.", source.getNode(), + "this user input" diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index bb83fbda583..d7fe929925d 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -267,6 +267,36 @@ class HeaderDeclaration extends DataFlow::Node { DataFlow::Node getValueArg() { result = range.getValueArg() } } +/** Provides classes for modeling Csv writer APIs. */ +module CsvWriter { + /** + * A data flow node for csv writer. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `CsvWriter` instead. + */ + abstract class Range extends DataFlow::Node { + /** + * Get the parameter value of the csv writer function. + */ + abstract DataFlow::Node getAnInput(); + } +} + +/** + * A data flow node for csv writer. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `CsvWriter::Range` instead. + */ +class CsvWriter extends DataFlow::Node { + CsvWriter::Range range; + + CsvWriter() { this = range } + + DataFlow::Node getAnInput() { result = range.getAnInput() } +} + /** Provides classes for modeling JWT encoding-related APIs. */ module JWTEncoding { /** diff --git a/python/ql/src/experimental/semmle/python/Frameworks.qll b/python/ql/src/experimental/semmle/python/Frameworks.qll index 81b2c1bee23..9322b9a622d 100644 --- a/python/ql/src/experimental/semmle/python/Frameworks.qll +++ b/python/ql/src/experimental/semmle/python/Frameworks.qll @@ -9,6 +9,7 @@ private import experimental.semmle.python.frameworks.Werkzeug private import experimental.semmle.python.frameworks.LDAP private import experimental.semmle.python.frameworks.NoSQL private import experimental.semmle.python.frameworks.JWT +private import experimental.semmle.python.frameworks.Csv private import experimental.semmle.python.libraries.PyJWT private import experimental.semmle.python.libraries.Python_JWT private import experimental.semmle.python.libraries.Authlib diff --git a/python/ql/src/experimental/semmle/python/frameworks/Csv.qll b/python/ql/src/experimental/semmle/python/frameworks/Csv.qll new file mode 100644 index 00000000000..7f92f85a8ed --- /dev/null +++ b/python/ql/src/experimental/semmle/python/frameworks/Csv.qll @@ -0,0 +1,73 @@ +/** + * Provides classes modeling security-relevant aspects of the `csv` PyPI package. + * See https://docs.python.org/3/library/csv.html + */ + +private import python +private import semmle.python.ApiGraphs +private import experimental.semmle.python.Concepts + +/** + * Provides models for the `csv` PyPI package. + * + * See + * - https://docs.python.org/3/library/csv.html + */ +private module Csv { + private module Writer { + abstract class InstanceSource extends DataFlow::LocalSourceNode { } + + /** A direct instantiation of `csv.writer` or `csv.DictWriter`. */ + private class ClassInstantiation extends InstanceSource, DataFlow::CfgNode { + ClassInstantiation() { + this = API::moduleImport("csv").getMember(["writer", "DictWriter"]).getACall() + } + } + + /** Gets a reference to an `csv.writer` or `csv.DictWriter` instance. */ + private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) { + t.start() and + result instanceof InstanceSource + or + exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t)) + } + + /** Gets a reference to an `csv.writer` or `csv.DictWriter` instance. */ + DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) } + + /** + * See: + * - https://docs.python.org/3/library/csv.html#csvwriter.writerow + * - https://docs.python.org/3/library/csv.html#csvwriter.writerows + */ + private class CsvWriteCall extends CsvWriter::Range, DataFlow::CallCfgNode { + string methodName; + + CsvWriteCall() { + methodName in ["writerow", "writerows"] and + this.(DataFlow::MethodCallNode).calls(Writer::instance(), methodName) + } + + override DataFlow::Node getAnInput() { + result = this.getArg(0) + or + methodName = "writerow" and + result = this.getArgByName("row") + or + methodName = "writerows" and + result = this.getArgByName("rows") + } + } + + /** + * See: https://docs.python.org/3/library/csv.html#csv.DictWriter + */ + private class DictWriterInstance extends CsvWriter::Range, DataFlow::CallCfgNode { + DictWriterInstance() { this = API::moduleImport("csv").getMember("DictWriter").getACall() } + + override DataFlow::Node getAnInput() { + result in [this.getArg(1), this.getArgByName("fieldnames")] + } + } + } +} diff --git a/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll new file mode 100644 index 00000000000..ac2f0e0a2d7 --- /dev/null +++ b/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll @@ -0,0 +1,24 @@ +import python +import experimental.semmle.python.Concepts +import semmle.python.dataflow.new.DataFlow +import semmle.python.dataflow.new.TaintTracking +import semmle.python.dataflow.new.RemoteFlowSources + +/** + * A taint-tracking configuration for tracking untrusted user input used in file read. + */ +class CsvInjectionFlowConfig extends TaintTracking::Configuration { + CsvInjectionFlowConfig() { this = "CsvInjectionFlowConfig" } + + override predicate isSource(DataFlow::Node source) { + source instanceof RemoteFlowSource + // exists(DataFlow::CallCfgNode ccn | + // ccn.getFunction().asCfgNode().(NameNode).getId() = "flag" and + // ccn.getArg(_) = source + // ) + } + + override predicate isSink(DataFlow::Node sink) { + exists(CsvWriter csvwriter | sink = csvwriter.getAnInput()) + } +} diff --git a/python/ql/test/experimental/query-tests/Security/CWE-1236/CsvInjection.expected b/python/ql/test/experimental/query-tests/Security/CWE-1236/CsvInjection.expected new file mode 100644 index 00000000000..2b6fb18f47b --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-1236/CsvInjection.expected @@ -0,0 +1,19 @@ +edges +| csv_bad.py:16:16:16:22 | ControlFlowNode for request | csv_bad.py:16:16:16:27 | ControlFlowNode for Attribute | +| csv_bad.py:16:16:16:27 | ControlFlowNode for Attribute | csv_bad.py:18:24:18:31 | ControlFlowNode for csv_data | +| csv_bad.py:16:16:16:27 | ControlFlowNode for Attribute | csv_bad.py:19:25:19:32 | ControlFlowNode for csv_data | +| csv_bad.py:24:16:24:22 | ControlFlowNode for request | csv_bad.py:24:16:24:27 | ControlFlowNode for Attribute | +| csv_bad.py:24:16:24:27 | ControlFlowNode for Attribute | csv_bad.py:25:46:25:53 | ControlFlowNode for csv_data | +nodes +| csv_bad.py:16:16:16:22 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| csv_bad.py:16:16:16:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| csv_bad.py:18:24:18:31 | ControlFlowNode for csv_data | semmle.label | ControlFlowNode for csv_data | +| csv_bad.py:19:25:19:32 | ControlFlowNode for csv_data | semmle.label | ControlFlowNode for csv_data | +| csv_bad.py:24:16:24:22 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| csv_bad.py:24:16:24:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| csv_bad.py:25:46:25:53 | ControlFlowNode for csv_data | semmle.label | ControlFlowNode for csv_data | +subpaths +#select +| csv_bad.py:18:24:18:31 | ControlFlowNode for csv_data | csv_bad.py:16:16:16:22 | ControlFlowNode for request | csv_bad.py:18:24:18:31 | ControlFlowNode for csv_data | Csv injection might include code from $@. | csv_bad.py:16:16:16:22 | ControlFlowNode for request | this user input | +| csv_bad.py:19:25:19:32 | ControlFlowNode for csv_data | csv_bad.py:16:16:16:22 | ControlFlowNode for request | csv_bad.py:19:25:19:32 | ControlFlowNode for csv_data | Csv injection might include code from $@. | csv_bad.py:16:16:16:22 | ControlFlowNode for request | this user input | +| csv_bad.py:25:46:25:53 | ControlFlowNode for csv_data | csv_bad.py:24:16:24:22 | ControlFlowNode for request | csv_bad.py:25:46:25:53 | ControlFlowNode for csv_data | Csv injection might include code from $@. | csv_bad.py:24:16:24:22 | ControlFlowNode for request | this user input | diff --git a/python/ql/test/experimental/query-tests/Security/CWE-1236/CsvInjection.qlref b/python/ql/test/experimental/query-tests/Security/CWE-1236/CsvInjection.qlref new file mode 100644 index 00000000000..d9cd7e9ca51 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-1236/CsvInjection.qlref @@ -0,0 +1 @@ +experimental/Security/CWE-1236/CsvInjection.ql \ No newline at end of file diff --git a/python/ql/test/experimental/query-tests/Security/CWE-1236/csv_bad.py b/python/ql/test/experimental/query-tests/Security/CWE-1236/csv_bad.py new file mode 100644 index 00000000000..6e204d1f3c5 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-1236/csv_bad.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +""" +@Desc :csv injection +""" +import copy +import csv +from flask import Flask +from flask import request +from typing import List + +app = Flask(__name__) + +@app.route('/bad1') +def bad1(): + csv_data = request.args.get('csv') + csvWriter = csv.writer(open("test.csv", "wt")) + csvWriter.writerow(csv_data) # bad + csvWriter.writerows(csv_data) # bad + return "bad1" + +@app.route('/bad2') +def bad2(): + csv_data = request.args.get('csv') + csvWriter = csv.DictWriter(f, fieldnames=csv_data) # bad + csvWriter.writeheader() + return "bad2" + +if __name__ == '__main__': + app.debug = True + app.run() \ No newline at end of file From e11c74c5804d7046491e9b333afa76fd354ac03a Mon Sep 17 00:00:00 2001 From: haby0 Date: Tue, 15 Mar 2022 15:25:08 +0800 Subject: [PATCH 002/125] Delete redundant comments --- .../semmle/python/security/injection/CsvInjection.qll | 4 ---- 1 file changed, 4 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll index ac2f0e0a2d7..6f8dae404fd 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll @@ -12,10 +12,6 @@ class CsvInjectionFlowConfig extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource - // exists(DataFlow::CallCfgNode ccn | - // ccn.getFunction().asCfgNode().(NameNode).getId() = "flag" and - // ccn.getArg(_) = source - // ) } override predicate isSink(DataFlow::Node sink) { From bf8c7a2ea79c1ad4f9b11cda9c8b4017d1cb1397 Mon Sep 17 00:00:00 2001 From: haby0 Date: Tue, 29 Mar 2022 14:29:33 +0800 Subject: [PATCH 003/125] Added Sanitizer Guard --- .../Security/CWE-1236/CsvInjection.qhelp | 4 ++-- .../experimental/semmle/python/Concepts.qll | 3 +++ .../security/injection/CsvInjection.qll | 21 ++++++++++++++----- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-1236/CsvInjection.qhelp b/python/ql/src/experimental/Security/CWE-1236/CsvInjection.qhelp index 237bf9493a8..785e8bdbfef 100644 --- a/python/ql/src/experimental/Security/CWE-1236/CsvInjection.qhelp +++ b/python/ql/src/experimental/Security/CWE-1236/CsvInjection.qhelp @@ -5,7 +5,7 @@

    CSV Injection, also known as Formula Injection, occurs when websites embed untrusted input inside CSV files.

    When a CSV format file is opened with a spreadsheet program such as Microsoft Excel or LibreOffice Calc. -this software interprets entries beginning with = as formulas. may attempt information exfiltration +this software interprets entries beginning with = as formulas, which may attempt information exfiltration or other malicious activity when automatically executed by the spreadsheet software.

    @@ -18,7 +18,7 @@ Risky characters include =(equal), +(plus), -The following examples show the bad case and the good case respectively. In bad1 method, the data provided by the user is directly stored in the CSV file, which may be attacked. -But in the good1 method,, the program will check the data provided by the user, and process the data starting with =(equal), +(plus), -(minus), and @(at) characters safely.

    +But in the good1 method, the program will check the data provided by the user, and process the data starting with =(equal), +(plus), -(minus), and @(at) characters safely.

    diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index d7fe929925d..4e9e5e48ce2 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -294,6 +294,9 @@ class CsvWriter extends DataFlow::Node { CsvWriter() { this = range } + /** + * Get the parameter value of the csv writer function. + */ DataFlow::Node getAnInput() { result = range.getAnInput() } } diff --git a/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll index 6f8dae404fd..afe58637545 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll @@ -2,6 +2,7 @@ import python import experimental.semmle.python.Concepts import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking +import semmle.python.dataflow.new.BarrierGuards import semmle.python.dataflow.new.RemoteFlowSources /** @@ -10,11 +11,21 @@ import semmle.python.dataflow.new.RemoteFlowSources class CsvInjectionFlowConfig extends TaintTracking::Configuration { CsvInjectionFlowConfig() { this = "CsvInjectionFlowConfig" } - override predicate isSource(DataFlow::Node source) { - source instanceof RemoteFlowSource - } + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { - exists(CsvWriter csvwriter | sink = csvwriter.getAnInput()) + override predicate isSink(DataFlow::Node sink) { sink = any(CsvWriter cw).getAnInput() } + + override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { + guard instanceof StartsWithCheck or + guard instanceof StringConstCompare + } +} + +class StartsWithCheck extends DataFlow::BarrierGuard { + StartsWithCheck() { this.(CallNode).getNode().getFunc().(Attribute).getName() = "startswith" } + + override predicate checks(ControlFlowNode node, boolean branch) { + node = this.(CallNode).getNode().getFunc().(Attribute).getObject().getAFlowNode() and + branch = true } } From 8f2013c32ebd962a78a53c019047553b71f76b45 Mon Sep 17 00:00:00 2001 From: haby0 Date: Wed, 30 Mar 2022 19:04:03 +0800 Subject: [PATCH 004/125] Simplify StartsWithCheck --- .../semmle/python/security/injection/CsvInjection.qll | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll index afe58637545..f0b24f147fd 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll @@ -21,11 +21,16 @@ class CsvInjectionFlowConfig extends TaintTracking::Configuration { } } -class StartsWithCheck extends DataFlow::BarrierGuard { - StartsWithCheck() { this.(CallNode).getNode().getFunc().(Attribute).getName() = "startswith" } +private class StartsWithCheck extends DataFlow::BarrierGuard { + Attribute attr; + + StartsWithCheck() { + this.(CallNode).getNode().getFunc() = attr and + attr.getName() = "startswith" + } override predicate checks(ControlFlowNode node, boolean branch) { - node = this.(CallNode).getNode().getFunc().(Attribute).getObject().getAFlowNode() and + node = attr.getObject().getAFlowNode() and branch = true } } From 1e6893e230001eeeef7e03623062c3c3827cc91a Mon Sep 17 00:00:00 2001 From: haby0 Date: Wed, 30 Mar 2022 22:54:30 +0800 Subject: [PATCH 005/125] Update python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll Co-authored-by: yoff --- .../semmle/python/security/injection/CsvInjection.qll | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll index f0b24f147fd..7da7b51a6df 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/CsvInjection.qll @@ -22,15 +22,15 @@ class CsvInjectionFlowConfig extends TaintTracking::Configuration { } private class StartsWithCheck extends DataFlow::BarrierGuard { - Attribute attr; + DataFlow::MethodCallNode mc; StartsWithCheck() { - this.(CallNode).getNode().getFunc() = attr and - attr.getName() = "startswith" + this = mc.asCfgNode() and + mc.calls(_, "startswith") } override predicate checks(ControlFlowNode node, boolean branch) { - node = attr.getObject().getAFlowNode() and + node = mc.getObject().asCfgNode() and branch = true } } From 1c2c9159a9053d49ae0ed491c502b6dd1c1d9deb Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 26 Apr 2022 12:43:34 +0200 Subject: [PATCH 006/125] initial MaD implementation for Python --- config/identical-files.json | 6 +- python/ql/lib/semmle/python/ApiGraphs.qll | 3 + python/ql/lib/semmle/python/Frameworks.qll | 1 + .../python/frameworks/data/ModelsAsData.qll | 46 ++ .../data/internal/AccessPathSyntax.qll | 175 ++++++ .../data/internal/ApiGraphModels.qll | 522 ++++++++++++++++++ .../data/internal/ApiGraphModelsSpecific.qll | 173 ++++++ .../frameworks/data/test.expected | 18 + .../library-tests/frameworks/data/test.py | 4 + .../library-tests/frameworks/data/test.ql | 85 +++ .../frameworks/data/warnings.expected | 12 + .../library-tests/frameworks/data/warnings.ql | 25 + 12 files changed, 1068 insertions(+), 2 deletions(-) create mode 100644 python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll create mode 100644 python/ql/lib/semmle/python/frameworks/data/internal/AccessPathSyntax.qll create mode 100644 python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll create mode 100644 python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll create mode 100644 python/ql/test/library-tests/frameworks/data/test.expected create mode 100644 python/ql/test/library-tests/frameworks/data/test.py create mode 100644 python/ql/test/library-tests/frameworks/data/test.ql create mode 100644 python/ql/test/library-tests/frameworks/data/warnings.expected create mode 100644 python/ql/test/library-tests/frameworks/data/warnings.ql diff --git a/config/identical-files.json b/config/identical-files.json index 2ff65c453f0..4ce332ca7f1 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -515,7 +515,8 @@ "csharp/ql/lib/semmle/code/csharp/dataflow/internal/AccessPathSyntax.qll", "java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll", "javascript/ql/lib/semmle/javascript/frameworks/data/internal/AccessPathSyntax.qll", - "ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll" + "ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll", + "python/ql/lib/semmle/python/frameworks/data/internal/AccessPathSyntax.qll" ], "IncompleteUrlSubstringSanitization": [ "javascript/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.qll", @@ -533,7 +534,8 @@ ], "ApiGraphModels": [ "javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll", - "ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll" + "ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll", + "python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll" ], "TaintedFormatStringQuery Ruby/JS": [ "javascript/ql/lib/semmle/javascript/security/dataflow/TaintedFormatStringQuery.qll", diff --git a/python/ql/lib/semmle/python/ApiGraphs.qll b/python/ql/lib/semmle/python/ApiGraphs.qll index 5c848fcb69e..53c78dd3c19 100644 --- a/python/ql/lib/semmle/python/ApiGraphs.qll +++ b/python/ql/lib/semmle/python/ApiGraphs.qll @@ -339,6 +339,9 @@ module API { result = callee.getReturn() and result.getAnImmediateUse() = this } + + /** Gets the number of arguments of this call. */ + int getNumArgument() { result = count(this.getArg(_)) } } /** diff --git a/python/ql/lib/semmle/python/Frameworks.qll b/python/ql/lib/semmle/python/Frameworks.qll index b94b8aee5d9..9bcbfe13576 100644 --- a/python/ql/lib/semmle/python/Frameworks.qll +++ b/python/ql/lib/semmle/python/Frameworks.qll @@ -12,6 +12,7 @@ private import semmle.python.frameworks.Asyncpg private import semmle.python.frameworks.ClickhouseDriver private import semmle.python.frameworks.Cryptodome private import semmle.python.frameworks.Cryptography +private import semmle.python.frameworks.data.ModelsAsData private import semmle.python.frameworks.Dill private import semmle.python.frameworks.Django private import semmle.python.frameworks.Fabric diff --git a/python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll b/python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll new file mode 100644 index 00000000000..6c45c9a4036 --- /dev/null +++ b/python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll @@ -0,0 +1,46 @@ +/** + * Provides classes for contributing a model, or using the interpreted results + * of a model represented as data. + * + * - Use the `ModelInput` module to contribute new models. + * - Use the `ModelOutput` module to access the model results in terms of API nodes. + * + * The package name refers to a Pip package name. + */ + +private import python +private import internal.ApiGraphModels as Shared +private import internal.ApiGraphModelsSpecific as Specific +import Shared::ModelInput as ModelInput +import Shared::ModelOutput as ModelOutput +private import semmle.python.dataflow.new.RemoteFlowSources +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.ApiGraphs +private import semmle.python.dataflow.new.TaintTracking + +/** + * A remote flow source originating from a CSV source row. + */ +private class RemoteFlowSourceFromCsv extends RemoteFlowSource { + RemoteFlowSourceFromCsv() { this = ModelOutput::getASourceNode("remote").getAnImmediateUse() } + + override string getSourceType() { result = "Remote flow" } +} + +/** + * Like `ModelOutput::summaryStep` but with API nodes mapped to data-flow nodes. + */ +private predicate summaryStepNodes(DataFlow::Node pred, DataFlow::Node succ, string kind) { + exists(API::Node predNode, API::Node succNode | + Specific::summaryStep(predNode, succNode, kind) and + pred = predNode.getARhs() and + succ = succNode.getAnImmediateUse() + ) +} + +/** Taint steps induced by summary models of kind `taint`. */ +private class TaintStepFromSummary extends TaintTracking::AdditionalTaintStep { + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + summaryStepNodes(pred, succ, "taint") + } +} diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/AccessPathSyntax.qll b/python/ql/lib/semmle/python/frameworks/data/internal/AccessPathSyntax.qll new file mode 100644 index 00000000000..3d40e04b815 --- /dev/null +++ b/python/ql/lib/semmle/python/frameworks/data/internal/AccessPathSyntax.qll @@ -0,0 +1,175 @@ +/** + * Module for parsing access paths from CSV models, both the identifying access path used + * by dynamic languages, and the input/output specifications for summary steps. + * + * This file is used by the shared data flow library and by the JavaScript libraries + * (which does not use the shared data flow libraries). + */ + +/** + * Convenience-predicate for extracting two capture groups at once. + */ +bindingset[input, regexp] +private predicate regexpCaptureTwo(string input, string regexp, string capture1, string capture2) { + capture1 = input.regexpCapture(regexp, 1) and + capture2 = input.regexpCapture(regexp, 2) +} + +/** Companion module to the `AccessPath` class. */ +module AccessPath { + /** A string that should be parsed as an access path. */ + abstract class Range extends string { + bindingset[this] + Range() { any() } + } + + /** + * Parses an integer constant `n` or interval `n1..n2` (inclusive) and gets the value + * of the constant or any value contained in the interval. + */ + bindingset[arg] + int parseInt(string arg) { + result = arg.toInt() + or + // Match "n1..n2" + exists(string lo, string hi | + regexpCaptureTwo(arg, "(-?\\d+)\\.\\.(-?\\d+)", lo, hi) and + result = [lo.toInt() .. hi.toInt()] + ) + } + + /** + * Parses a lower-bounded interval `n..` and gets the lower bound. + */ + bindingset[arg] + int parseLowerBound(string arg) { result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt() } + + /** + * Parses an integer constant or interval (bounded or unbounded) that explicitly + * references the arity, such as `N-1` or `N-3..N-1`. + * + * Note that expressions of form `N-x` will never resolve to a negative index, + * even if `N` is zero (it will have no result in that case). + */ + bindingset[arg, arity] + private int parseIntWithExplicitArity(string arg, int arity) { + result >= 0 and // do not allow N-1 to resolve to a negative index + exists(string lo | + // N-x + lo = arg.regexpCapture("N-(\\d+)", 1) and + result = arity - lo.toInt() + or + // N-x.. + lo = arg.regexpCapture("N-(\\d+)\\.\\.", 1) and + result = [arity - lo.toInt(), arity - 1] + ) + or + exists(string lo, string hi | + // x..N-y + regexpCaptureTwo(arg, "(-?\\d+)\\.\\.N-(\\d+)", lo, hi) and + result = [lo.toInt() .. arity - hi.toInt()] + or + // N-x..N-y + regexpCaptureTwo(arg, "N-(\\d+)\\.\\.N-(\\d+)", lo, hi) and + result = [arity - lo.toInt() .. arity - hi.toInt()] and + result >= 0 + or + // N-x..y + regexpCaptureTwo(arg, "N-(\\d+)\\.\\.(\\d+)", lo, hi) and + result = [arity - lo.toInt() .. hi.toInt()] and + result >= 0 + ) + } + + /** + * Parses an integer constant or interval (bounded or unbounded) and gets any + * of the integers contained within (of which there may be infinitely many). + * + * Has no result for arguments involving an explicit arity, such as `N-1`. + */ + bindingset[arg, result] + int parseIntUnbounded(string arg) { + result = parseInt(arg) + or + result >= parseLowerBound(arg) + } + + /** + * Parses an integer constant or interval (bounded or unbounded) that + * may reference the arity of a call, such as `N-1` or `N-3..N-1`. + * + * Note that expressions of form `N-x` will never resolve to a negative index, + * even if `N` is zero (it will have no result in that case). + */ + bindingset[arg, arity] + int parseIntWithArity(string arg, int arity) { + result = parseInt(arg) + or + result in [parseLowerBound(arg) .. arity - 1] + or + result = parseIntWithExplicitArity(arg, arity) + } +} + +/** Gets the `n`th token on the access path as a string. */ +private string getRawToken(AccessPath path, int n) { + // Avoid splitting by '.' since tokens may contain dots, e.g. `Field[foo.Bar.x]`. + // Instead use regexpFind to match valid tokens, and supplement with a final length + // check (in `AccessPath.hasSyntaxError`) to ensure all characters were included in a token. + result = path.regexpFind("\\w+(?:\\[[^\\]]*\\])?(?=\\.|$)", n, _) +} + +/** + * A string that occurs as an access path (either identifying or input/output spec) + * which might be relevant for this database. + */ +class AccessPath extends string instanceof AccessPath::Range { + /** Holds if this string is not a syntactically valid access path. */ + predicate hasSyntaxError() { + // If the lengths match, all characters must haven been included in a token + // or seen by the `.` lookahead pattern. + this != "" and + not this.length() = sum(int n | | getRawToken(this, n).length() + 1) - 1 + } + + /** Gets the `n`th token on the access path (if there are no syntax errors). */ + AccessPathToken getToken(int n) { + result = getRawToken(this, n) and + not this.hasSyntaxError() + } + + /** Gets the number of tokens on the path (if there are no syntax errors). */ + int getNumToken() { + result = count(int n | exists(getRawToken(this, n))) and + not this.hasSyntaxError() + } +} + +/** + * An access part token such as `Argument[1]` or `ReturnValue`, appearing in one or more access paths. + */ +class AccessPathToken extends string { + AccessPathToken() { this = getRawToken(_, _) } + + private string getPart(int part) { + result = this.regexpCapture("([^\\[]+)(?:\\[([^\\]]*)\\])?", part) + } + + /** Gets the name of the token, such as `Member` from `Member[x]` */ + string getName() { result = this.getPart(1) } + + /** + * Gets the argument list, such as `1,2` from `Member[1,2]`, + * or has no result if there are no arguments. + */ + string getArgumentList() { result = this.getPart(2) } + + /** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */ + string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() } + + /** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */ + string getAnArgument() { result = this.getArgument(_) } + + /** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */ + int getNumArgument() { result = count(int n | exists(this.getArgument(n))) } +} diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll new file mode 100644 index 00000000000..4681c2b91a5 --- /dev/null +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll @@ -0,0 +1,522 @@ +/** + * INTERNAL use only. This is an experimental API subject to change without notice. + * + * Provides classes and predicates for dealing with flow models specified in CSV format. + * + * The CSV specification has the following columns: + * - Sources: + * `package; type; path; kind` + * - Sinks: + * `package; type; path; kind` + * - Summaries: + * `package; type; path; input; output; kind` + * - Types: + * `package1; type1; package2; type2; path` + * + * The interpretation of a row is similar to API-graphs with a left-to-right + * reading. + * 1. The `package` column selects a package name, as it would be referenced in the source code, + * such as an NPM package, PIP package, or Ruby gem. (See `ModelsAsData.qll` for language-specific details). + * It may also be a synthetic package used for a type definition (see type definitions below). + * 2. The `type` column selects all instances of a named type originating from that package, + * or the empty string if referring to the package itself. + * It can also be a synthetic type name defined by a type definition (see type definitions below). + * 3. The `path` column is a `.`-separated list of "access path tokens" to resolve, starting at the node selected by `package` and `type`. + * + * Every language supports the following tokens: + * - Argument[n]: the n-th argument to a call. May be a range of form `x..y` (inclusive) and/or a comma-separated list. + * Additionally, `N-1` refers to the last argument, `N-2` refers to the second-last, and so on. + * - Parameter[n]: the n-th parameter of a callback. May be a range of form `x..y` (inclusive) and/or a comma-separated list. + * - ReturnValue: the value returned by a function call + * - WithArity[n]: match a call with the given arity. May be a range of form `x..y` (inclusive) and/or a comma-separated list. + * + * The following tokens are common and should be implemented for languages where it makes sense: + * - Member[x]: a member named `x`; exactly what a "member" is depends on the language. May be a comma-separated list of names. + * - Instance: an instance of a class + * - Subclass: a subclass of a class + * - ArrayElement: an element of array + * - Element: an element of a collection-like object + * - MapKey: a key in map-like object + * - MapValue: a value in a map-like object + * - Awaited: the value from a resolved promise/future-like object + * + * For the time being, please consult `ApiGraphModelsSpecific.qll` to see which language-specific tokens are currently supported. + * + * 4. The `input` and `output` columns specify how data enters and leaves the element selected by the + * first `(package, type, path)` tuple. Both strings are `.`-separated access paths + * of the same syntax as the `path` column. + * 5. The `kind` column is a tag that can be referenced from QL to determine to + * which classes the interpreted elements should be added. For example, for + * sources `"remote"` indicates a default remote flow source, and for summaries + * `"taint"` indicates a default additional taint step and `"value"` indicates a + * globally applicable value-preserving step. + * + * ### Types + * + * A type row of form `package1; type1; package2; type2; path` indicates that `package2; type2; path` + * should be seen as an instance of the type `package1; type1`. + * + * A `(package,type)` pair may refer to a static type or a synthetic type name used internally in the model. + * Synthetic type names can be used to reuse intermediate sub-paths, when there are multiple ways to access the same + * element. + * See `ModelsAsData.qll` for the langauge-specific interpretation of packages and static type names. + * + * By convention, if one wants to avoid clashes with static types from the package, the type name + * should be prefixed with a tilde character (`~`). For example, `(foo, ~Bar)` can be used to indicate that + * the type is related to the `foo` package but is not intended to match a static type. + */ + +private import ApiGraphModelsSpecific as Specific + +private class Unit = Specific::Unit; + +private module API = Specific::API; + +private import Specific::AccessPathSyntax + +/** Module containing hooks for providing input data to be interpreted as a model. */ +module ModelInput { + /** + * A unit class for adding additional source model rows. + * + * Extend this class to add additional source definitions. + */ + class SourceModelCsv extends Unit { + /** + * Holds if `row` specifies a source definition. + * + * A row of form + * ``` + * package;type;path;kind + * ``` + * indicates that the value at `(package, type, path)` should be seen as a flow + * source of the given `kind`. + * + * The kind `remote` represents a general remote flow source. + */ + abstract predicate row(string row); + } + + /** + * A unit class for adding additional sink model rows. + * + * Extend this class to add additional sink definitions. + */ + class SinkModelCsv extends Unit { + /** + * Holds if `row` specifies a sink definition. + * + * A row of form + * ``` + * package;type;path;kind + * ``` + * indicates that the value at `(package, type, path)` should be seen as a sink + * of the given `kind`. + */ + abstract predicate row(string row); + } + + /** + * A unit class for adding additional summary model rows. + * + * Extend this class to add additional flow summary definitions. + */ + class SummaryModelCsv extends Unit { + /** + * Holds if `row` specifies a summary definition. + * + * A row of form + * ``` + * package;type;path;input;output;kind + * ``` + * indicates that for each call to `(package, type, path)`, the value referred to by `input` + * can flow to the value referred to by `output`. + * + * `kind` should be either `value` or `taint`, for value-preserving or taint-preserving steps, + * respectively. + */ + abstract predicate row(string row); + } + + /** + * A unit class for adding additional type model rows. + * + * Extend this class to add additional type definitions. + */ + class TypeModelCsv extends Unit { + /** + * Holds if `row` specifies a type definition. + * + * A row of form, + * ``` + * package1;type1;package2;type2;path + * ``` + * indicates that `(package2, type2, path)` should be seen as an instance of `(package1, type1)`. + */ + abstract predicate row(string row); + } +} + +private import ModelInput + +/** + * An empty class, except in specific tests. + * + * If this is non-empty, all models are parsed even if the package is not + * considered relevant for the current database. + */ +abstract class TestAllModels extends Unit { } + +/** + * Append `;dummy` to the value of `s` to work around the fact that `string.split(delim,n)` + * does not preserve empty trailing substrings. + */ +bindingset[result] +private string inversePad(string s) { s = result + ";dummy" } + +private predicate sourceModel(string row) { any(SourceModelCsv s).row(inversePad(row)) } + +private predicate sinkModel(string row) { any(SinkModelCsv s).row(inversePad(row)) } + +private predicate summaryModel(string row) { any(SummaryModelCsv s).row(inversePad(row)) } + +private predicate typeModel(string row) { any(TypeModelCsv s).row(inversePad(row)) } + +/** Holds if a source model exists for the given parameters. */ +predicate sourceModel(string package, string type, string path, string kind) { + exists(string row | + sourceModel(row) and + row.splitAt(";", 0) = package and + row.splitAt(";", 1) = type and + row.splitAt(";", 2) = path and + row.splitAt(";", 3) = kind + ) +} + +/** Holds if a sink model exists for the given parameters. */ +private predicate sinkModel(string package, string type, string path, string kind) { + exists(string row | + sinkModel(row) and + row.splitAt(";", 0) = package and + row.splitAt(";", 1) = type and + row.splitAt(";", 2) = path and + row.splitAt(";", 3) = kind + ) +} + +/** Holds if a summary model `row` exists for the given parameters. */ +private predicate summaryModel( + string package, string type, string path, string input, string output, string kind +) { + exists(string row | + summaryModel(row) and + row.splitAt(";", 0) = package and + row.splitAt(";", 1) = type and + row.splitAt(";", 2) = path and + row.splitAt(";", 3) = input and + row.splitAt(";", 4) = output and + row.splitAt(";", 5) = kind + ) +} + +/** Holds if an type model exists for the given parameters. */ +private predicate typeModel( + string package1, string type1, string package2, string type2, string path +) { + exists(string row | + typeModel(row) and + row.splitAt(";", 0) = package1 and + row.splitAt(";", 1) = type1 and + row.splitAt(";", 2) = package2 and + row.splitAt(";", 3) = type2 and + row.splitAt(";", 4) = path + ) +} + +/** + * Gets a package that should be seen as an alias for the given other `package`, + * or the `package` itself. + */ +bindingset[package] +bindingset[result] +string getAPackageAlias(string package) { + typeModel(package, "", result, "", "") + or + result = package +} + +/** + * Holds if CSV rows involving `package` might be relevant for the analysis of this database. + */ +private predicate isRelevantPackage(string package) { + ( + sourceModel(package, _, _, _) or + sinkModel(package, _, _, _) or + summaryModel(package, _, _, _, _, _) or + typeModel(package, _, _, _, _) + ) and + ( + Specific::isPackageUsed(package) + or + exists(TestAllModels t) + ) + or + exists(string other | + isRelevantPackage(other) and + typeModel(package, _, other, _, _) + ) +} + +/** + * Holds if `package,type,path` is used in some CSV row. + */ +pragma[nomagic] +predicate isRelevantFullPath(string package, string type, string path) { + isRelevantPackage(package) and + ( + sourceModel(package, type, path, _) or + sinkModel(package, type, path, _) or + summaryModel(package, type, path, _, _, _) or + typeModel(_, _, package, type, path) + ) +} + +/** A string from a CSV row that should be parsed as an access path. */ +private class AccessPathRange extends AccessPath::Range { + AccessPathRange() { + isRelevantFullPath(_, _, this) + or + exists(string package | isRelevantPackage(package) | + summaryModel(package, _, _, this, _, _) or + summaryModel(package, _, _, _, this, _) + ) + } +} + +/** + * Gets a successor of `node` in the API graph. + */ +bindingset[token] +API::Node getSuccessorFromNode(API::Node node, AccessPathToken token) { + // API graphs use the same label for arguments and parameters. An edge originating from a + // use-node represents be an argument, and an edge originating from a def-node represents a parameter. + // We just map both to the same thing. + token.getName() = ["Argument", "Parameter"] and + result = node.getParameter(AccessPath::parseIntUnbounded(token.getAnArgument())) + or + token.getName() = "ReturnValue" and + result = node.getReturn() + or + // Language-specific tokens + result = Specific::getExtraSuccessorFromNode(node, token) +} + +/** + * Gets an API-graph successor for the given invocation. + */ +bindingset[token] +API::Node getSuccessorFromInvoke(Specific::InvokeNode invoke, AccessPathToken token) { + token.getName() = "Argument" and + result = + invoke + .getParameter(AccessPath::parseIntWithArity(token.getAnArgument(), invoke.getNumArgument())) + or + token.getName() = "ReturnValue" and + result = invoke.getReturn() + or + // Language-specific tokens + result = Specific::getExtraSuccessorFromInvoke(invoke, token) +} + +/** + * Holds if `invoke` invokes a call-site filter given by `token`. + */ +pragma[inline] +private predicate invocationMatchesCallSiteFilter(Specific::InvokeNode invoke, AccessPathToken token) { + token.getName() = "WithArity" and + invoke.getNumArgument() = AccessPath::parseIntUnbounded(token.getAnArgument()) + or + Specific::invocationMatchesExtraCallSiteFilter(invoke, token) +} + +/** + * Gets the API node identified by the first `n` tokens of `path` in the given `(package, type, path)` tuple. + */ +pragma[nomagic] +private API::Node getNodeFromPath(string package, string type, AccessPath path, int n) { + isRelevantFullPath(package, type, path) and + ( + n = 0 and + exists(string package2, string type2, AccessPath path2 | + typeModel(package, type, package2, type2, path2) and + result = getNodeFromPath(package2, type2, path2, path2.getNumToken()) + ) + or + // Language-specific cases, such as handling of global variables + result = Specific::getExtraNodeFromPath(package, type, path, n) + ) + or + result = getSuccessorFromNode(getNodeFromPath(package, type, path, n - 1), path.getToken(n - 1)) + or + // Similar to the other recursive case, but where the path may have stepped through one or more call-site filters + result = + getSuccessorFromInvoke(getInvocationFromPath(package, type, path, n - 1), path.getToken(n - 1)) +} + +/** Gets the node identified by the given `(package, type, path)` tuple. */ +API::Node getNodeFromPath(string package, string type, AccessPath path) { + result = getNodeFromPath(package, type, path, path.getNumToken()) +} + +/** + * Gets an invocation identified by the given `(package, type, path)` tuple. + * + * Unlike `getNodeFromPath`, the `path` may end with one or more call-site filters. + */ +Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPath path, int n) { + result = Specific::getAnInvocationOf(getNodeFromPath(package, type, path, n)) + or + result = getInvocationFromPath(package, type, path, n - 1) and + invocationMatchesCallSiteFilter(result, path.getToken(n - 1)) +} + +/** Gets an invocation identified by the given `(package, type, path)` tuple. */ +Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPath path) { + result = getInvocationFromPath(package, type, path, path.getNumToken()) +} + +/** + * Holds if `name` is a valid name for an access path token in the identifying access path. + */ +bindingset[name] +predicate isValidTokenNameInIdentifyingAccessPath(string name) { + name = ["Argument", "Parameter", "ReturnValue", "WithArity"] + or + Specific::isExtraValidTokenNameInIdentifyingAccessPath(name) +} + +/** + * Holds if `name` is a valid name for an access path token with no arguments, occuring + * in an identifying access path. + */ +bindingset[name] +predicate isValidNoArgumentTokenInIdentifyingAccessPath(string name) { + name = "ReturnValue" + or + Specific::isExtraValidNoArgumentTokenInIdentifyingAccessPath(name) +} + +/** + * Holds if `argument` is a valid argument to an access path token with the given `name`, occurring + * in an identifying access path. + */ +bindingset[name, argument] +predicate isValidTokenArgumentInIdentifyingAccessPath(string name, string argument) { + name = ["Argument", "Parameter"] and + argument.regexpMatch("(N-|-)?\\d+(\\.\\.((N-|-)?\\d+)?)?") + or + name = "WithArity" and + argument.regexpMatch("\\d+(\\.\\.(\\d+)?)?") + or + Specific::isExtraValidTokenArgumentInIdentifyingAccessPath(name, argument) +} + +/** + * Module providing access to the imported models in terms of API graph nodes. + */ +module ModelOutput { + /** + * Holds if a CSV source model contributed `source` with the given `kind`. + */ + API::Node getASourceNode(string kind) { + exists(string package, string type, string path | + sourceModel(package, type, path, kind) and + result = getNodeFromPath(package, type, path) + ) + } + + /** + * Holds if a CSV sink model contributed `sink` with the given `kind`. + */ + API::Node getASinkNode(string kind) { + exists(string package, string type, string path | + sinkModel(package, type, path, kind) and + result = getNodeFromPath(package, type, path) + ) + } + + /** + * Holds if a relevant CSV summary exists for these parameters. + */ + predicate relevantSummaryModel( + string package, string type, string path, string input, string output, string kind + ) { + isRelevantPackage(package) and + summaryModel(package, type, path, input, output, kind) + } + + /** + * Holds if a `baseNode` is an invocation identified by the `package,type,path` part of a summary row. + */ + predicate resolvedSummaryBase( + string package, string type, string path, Specific::InvokeNode baseNode + ) { + summaryModel(package, type, path, _, _, _) and + baseNode = getInvocationFromPath(package, type, path) + } + + /** + * Holds if `node` is seen as an instance of `(package,type)` due to a type definition + * contributed by a CSV model. + */ + API::Node getATypeNode(string package, string type) { + exists(string package2, string type2, AccessPath path | + typeModel(package, type, package2, type2, path) and + result = getNodeFromPath(package2, type2, path) + ) + } + + /** + * Gets an error message relating to an invalid CSV row in a model. + */ + string getAWarning() { + // Check number of columns + exists(string row, string kind, int expectedArity, int actualArity | + any(SourceModelCsv csv).row(row) and kind = "source" and expectedArity = 4 + or + any(SinkModelCsv csv).row(row) and kind = "sink" and expectedArity = 4 + or + any(SummaryModelCsv csv).row(row) and kind = "summary" and expectedArity = 6 + or + any(TypeModelCsv csv).row(row) and kind = "type" and expectedArity = 5 + | + actualArity = count(row.indexOf(";")) + 1 and + actualArity != expectedArity and + result = + "CSV " + kind + " row should have " + expectedArity + " columns but has " + actualArity + + ": " + row + ) + or + // Check names and arguments of access path tokens + exists(AccessPath path, AccessPathToken token | + isRelevantFullPath(_, _, path) and + token = path.getToken(_) + | + not isValidTokenNameInIdentifyingAccessPath(token.getName()) and + result = "Invalid token name '" + token.getName() + "' in access path: " + path + or + isValidTokenNameInIdentifyingAccessPath(token.getName()) and + exists(string argument | + argument = token.getAnArgument() and + not isValidTokenArgumentInIdentifyingAccessPath(token.getName(), argument) and + result = + "Invalid argument '" + argument + "' in token '" + token + "' in access path: " + path + ) + or + isValidTokenNameInIdentifyingAccessPath(token.getName()) and + token.getNumArgument() = 0 and + not isValidNoArgumentTokenInIdentifyingAccessPath(token.getName()) and + result = "Invalid token '" + token + "' is missing its arguments, in access path: " + path + ) + } +} diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll new file mode 100644 index 00000000000..fc6e69404ee --- /dev/null +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -0,0 +1,173 @@ +/** + * Contains the language-specific part of the models-as-data implementation found in `ApiGraphModels.qll`. + * + * It must export the following members: + * ```ql + * class Unit // a unit type + * module AccessPathSyntax // a re-export of the AccessPathSyntax module + * class InvokeNode // a type representing an invocation connected to the API graph + * module API // the API graph module + * predicate isPackageUsed(string package) + * API::Node getExtraNodeFromPath(string package, string type, string path, int n) + * API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) + * API::Node getExtraSuccessorFromInvoke(API::InvokeNode node, AccessPathToken token) + * predicate invocationMatchesExtraCallSiteFilter(API::InvokeNode invoke, AccessPathToken token) + * InvokeNode getAnInvocationOf(API::Node node) + * predicate isExtraValidTokenNameInIdentifyingAccessPath(string name) + * predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name) + * predicate isExtraValidTokenArgumentInIdentifyingAccessPath(string name, string argument) + * ``` + */ + +private import python as PY +import semmle.python.dataflow.new.DataFlow +private import ApiGraphModels +import semmle.python.ApiGraphs + +class Unit = PY::Unit; + +// Re-export libraries needed by ApiGraphModels.qll +import semmle.python.frameworks.data.internal.AccessPathSyntax as AccessPathSyntax +private import AccessPathSyntax + +/** + * Holds if models describing `package` may be relevant for the analysis of this database. + */ +predicate isPackageUsed(string package) { exists(API::moduleImport(package)) } + +/** Gets a Python-specific interpretation of the `(package, type, path)` tuple after resolving the first `n` access path tokens. */ +bindingset[package, type, path] +API::Node getExtraNodeFromPath(string package, string type, AccessPath path, int n) { + type = "" and + n = 0 and + result = API::moduleImport(package) and + exists(path) +} + +/** + * Gets a Python-specific API graph successor of `node` reachable by resolving `token`. + */ +bindingset[token] +API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) { + token.getName() = "Member" and + result = node.getMember(token.getAnArgument()) + or + token.getName() = "Instance" and + result = node.getReturn() // commonly used Token. In Python `Instance` is just an alias for `ReturnValue` + or + token.getName() = "Awaited" and + result = node.getAwaited() + // Some features don't have MaD tokens yet, they would need to be added to API-graphs first. + // - decorators ("DecoratedClass", "DecoratedMember", "DecoratedParameter") + // - Array/Map elements ("ArrayElement", "Element", "MapKey", "MapValue") +} + +/** + * Gets a Python-specific API graph successor of `node` reachable by resolving `token`. + */ +bindingset[token] +API::Node getExtraSuccessorFromInvoke(API::CallNode node, AccessPathToken token) { + token.getName() = "Instance" and + result = node.getReturn() + or + token.getName() = "Argument" and + token.getAnArgument() = "self" and + result.getARhs() = node.(DataFlow::MethodCallNode).getObject() // TODO: Get proper support for this in API-graphs? + or + token.getName() = "Argument" and + exists(string arg | arg + ":" = token.getAnArgument() | result = node.getKeywordParameter(arg)) +} + +/** + * Holds if `invoke` matches the PY-specific call site filter in `token`. + */ +bindingset[token] +predicate invocationMatchesExtraCallSiteFilter(API::CallNode invoke, AccessPathToken token) { + token.getName() = "Call" and exists(invoke) // there is only one kind of call in Python. +} + +/** + * Holds if `path` is an input or output spec for a summary with the given `base` node. + */ +pragma[nomagic] +private predicate relevantInputOutputPath(API::CallNode base, AccessPath inputOrOutput) { + exists(string package, string type, string input, string output, string path | + ModelOutput::relevantSummaryModel(package, type, path, input, output, _) and + ModelOutput::resolvedSummaryBase(package, type, path, base) and + inputOrOutput = [input, output] + ) +} + +/** + * Gets the API node for the first `n` tokens of the given input/output path, evaluated relative to `baseNode`. + */ +private API::Node getNodeFromInputOutputPath(API::CallNode baseNode, AccessPath path, int n) { + relevantInputOutputPath(baseNode, path) and + ( + n = 1 and + result = getSuccessorFromInvoke(baseNode, path.getToken(0)) + or + result = + getSuccessorFromNode(getNodeFromInputOutputPath(baseNode, path, n - 1), path.getToken(n - 1)) + ) +} + +/** + * Gets the API node for the given input/output path, evaluated relative to `baseNode`. + */ +private API::Node getNodeFromInputOutputPath(API::CallNode baseNode, AccessPath path) { + result = getNodeFromInputOutputPath(baseNode, path, path.getNumToken()) +} + +/** + * Holds if a CSV summary contributed the step `pred -> succ` of the given `kind`. + */ +predicate summaryStep(API::Node pred, API::Node succ, string kind) { + exists( + string package, string type, string path, API::CallNode base, AccessPath input, + AccessPath output + | + ModelOutput::relevantSummaryModel(package, type, path, input, output, kind) and + ModelOutput::resolvedSummaryBase(package, type, path, base) and + pred = getNodeFromInputOutputPath(base, input) and + succ = getNodeFromInputOutputPath(base, output) + ) +} + +class InvokeNode = API::CallNode; + +/** Gets an `InvokeNode` corresponding to an invocation of `node`. */ +InvokeNode getAnInvocationOf(API::Node node) { result = node.getACall() } + +/** + * Holds if `name` is a valid name for an access path token in the identifying access path. + */ +bindingset[name] +predicate isExtraValidTokenNameInIdentifyingAccessPath(string name) { + name = ["Member", "Instance", "Awaited", "Call", "Method", "Subclass"] +} + +/** + * Holds if `name` is a valid name for an access path token with no arguments, occuring + * in an identifying access path. + */ +predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name) { + name = ["Instance", "Awaited", "Call", "Subclass"] +} + +/** + * Holds if `argument` is a valid argument to an access path token with the given `name`, occurring + * in an identifying access path. + */ +bindingset[name, argument] +predicate isExtraValidTokenArgumentInIdentifyingAccessPath(string name, string argument) { + name = ["Member"] and + exists(argument) + or + name = ["Argument", "Parameter"] and + ( + argument = "self" + or + argument.regexpMatch("\\w+:") // keyword argument + ) +} diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected new file mode 100644 index 00000000000..afdd0b91a9d --- /dev/null +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -0,0 +1,18 @@ +taintFlow +| test.py:3:5:3:15 | ControlFlowNode for getSource() | test.py:4:8:4:8 | ControlFlowNode for x | +isSink +| test.py:4:8:4:8 | ControlFlowNode for x | test-sink | +isSource +| test.py:3:5:3:15 | ControlFlowNode for getSource() | test-source | +syntaxErrors +| Member[foo | +| Member[foo] .Member[bar] | +| Member[foo] Member[bar] | +| Member[foo], Member[bar] | +| Member[foo],Member[bar] | +| Member[foo]. Member[bar] | +| Member[foo]..Member[bar] | +| Member[foo]Member[bar] | +| Member[foo]] | +| Member[foo]].Member[bar] | +warning diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py new file mode 100644 index 00000000000..23da10f806d --- /dev/null +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -0,0 +1,4 @@ +from testlib import getSource, mySink + +x = getSource() +mySink(x) \ No newline at end of file diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql new file mode 100644 index 00000000000..da1d80bf276 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -0,0 +1,85 @@ +import python +import semmle.python.frameworks.data.internal.AccessPathSyntax as AccessPathSyntax +import semmle.python.frameworks.data.ModelsAsData +import semmle.python.dataflow.new.TaintTracking +import semmle.python.dataflow.new.DataFlow + +// TODO: +/* + * class Steps extends ModelInput::SummaryModelCsv { + * override predicate row(string row) { + * // package;type;path;input;output;kind + * row = + * [ + * "testlib;;Member[preserveTaint];Argument[0];ReturnValue;taint", + * "testlib;;Member[taintIntoCallback];Argument[0];Argument[1..2].Parameter[0];taint", + * "testlib;;Member[taintIntoCallbackThis];Argument[0];Argument[1..2].Parameter[this];taint", + * "testlib;;Member[preserveArgZeroAndTwo];Argument[0,2];ReturnValue;taint", + * "testlib;;Member[preserveAllButFirstArgument];Argument[1..];ReturnValue;taint", + * "testlib;;Member[preserveAllIfCall].Call;Argument[0..];ReturnValue;taint", + * "testlib;;Member[getSource].ReturnValue.Member[continue];Argument[this];ReturnValue;taint", + * ] + * } + * } + */ + +class Sinks extends ModelInput::SinkModelCsv { + override predicate row(string row) { + // package;type;path;kind + row = ["testlib;;Member[mySink].Argument[0];test-sink"] + } +} + +// TODO: Test taint steps (include that the base path may end with ".Call") +// TODO: Ctrl + f: TODO +// TODO: // There are no API-graph edges for: ArrayElement, Element, MapKey, MapValue (remove from valid tokens list) +// TODO: Verify that the list of valid tokens matches the implementation. +class Sources extends ModelInput::SourceModelCsv { + // package;type;path;kind + override predicate row(string row) { + row = ["testlib;;Member[getSource].ReturnValue;test-source"] + } +} + +class BasicTaintTracking extends TaintTracking::Configuration { + BasicTaintTracking() { this = "BasicTaintTracking" } + + override predicate isSource(DataFlow::Node source) { + source = ModelOutput::getASourceNode("test-source").getAnImmediateUse() + } + + override predicate isSink(DataFlow::Node sink) { + sink = ModelOutput::getASinkNode("test-sink").getARhs() + } +} + +query predicate taintFlow(DataFlow::Node source, DataFlow::Node sink) { + any(BasicTaintTracking tr).hasFlow(source, sink) +} + +query predicate isSink(DataFlow::Node node, string kind) { + node = ModelOutput::getASinkNode(kind).getARhs() +} + +query predicate isSource(DataFlow::Node node, string kind) { + node = ModelOutput::getASourceNode(kind).getAnImmediateUse() +} + +class SyntaxErrorTest extends ModelInput::SinkModelCsv { + override predicate row(string row) { + row = + [ + "testlib;;Member[foo],Member[bar];test-sink", "testlib;;Member[foo] Member[bar];test-sink", + "testlib;;Member[foo]. Member[bar];test-sink", + "testlib;;Member[foo], Member[bar];test-sink", + "testlib;;Member[foo]..Member[bar];test-sink", + "testlib;;Member[foo] .Member[bar];test-sink", "testlib;;Member[foo]Member[bar];test-sink", + "testlib;;Member[foo;test-sink", "testlib;;Member[foo]];test-sink", + "testlib;;Member[foo]].Member[bar];test-sink" + ] + } +} + +query predicate syntaxErrors(AccessPathSyntax::AccessPath path) { path.hasSyntaxError() } + +query predicate warning = ModelOutput::getAWarning/0; diff --git a/python/ql/test/library-tests/frameworks/data/warnings.expected b/python/ql/test/library-tests/frameworks/data/warnings.expected new file mode 100644 index 00000000000..b8242f31a85 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/data/warnings.expected @@ -0,0 +1,12 @@ +| CSV type row should have 5 columns but has 2: test;TooFewColumns | +| CSV type row should have 5 columns but has 8: test;TooManyColumns;;;Member[Foo].Instance;too;many;columns | +| Invalid argument '0-1' in token 'Argument[0-1]' in access path: Method[foo].Argument[0-1] | +| Invalid argument '*' in token 'Argument[*]' in access path: Method[foo].Argument[*] | +| Invalid argument 'foo' in token 'Method[foo]' in access path: Method[foo].Arg[0] | +| Invalid argument 'foo' in token 'Method[foo]' in access path: Method[foo].Argument | +| Invalid argument 'foo' in token 'Method[foo]' in access path: Method[foo].Argument[0-1] | +| Invalid argument 'foo' in token 'Method[foo]' in access path: Method[foo].Argument[*] | +| Invalid argument 'foo' in token 'Method[foo]' in access path: Method[foo].Member | +| Invalid token 'Argument' is missing its arguments, in access path: Method[foo].Argument | +| Invalid token 'Member' is missing its arguments, in access path: Method[foo].Member | +| Invalid token name 'Arg' in access path: Method[foo].Arg[0] | diff --git a/python/ql/test/library-tests/frameworks/data/warnings.ql b/python/ql/test/library-tests/frameworks/data/warnings.ql new file mode 100644 index 00000000000..3443233179e --- /dev/null +++ b/python/ql/test/library-tests/frameworks/data/warnings.ql @@ -0,0 +1,25 @@ +import python +import semmle.python.frameworks.data.internal.AccessPathSyntax as AccessPathSyntax +import semmle.python.frameworks.data.internal.ApiGraphModels as ApiGraphModels +import semmle.python.frameworks.data.ModelsAsData + +private class InvalidTypeModel extends ModelInput::TypeModelCsv { + override predicate row(string row) { + row = + [ + "test;TooManyColumns;;;Member[Foo].Instance;too;many;columns", // + "test;TooFewColumns", // + "test;X;test;Y;Method[foo].Arg[0]", // + "test;X;test;Y;Method[foo].Argument[0-1]", // + "test;X;test;Y;Method[foo].Argument[*]", // + "test;X;test;Y;Method[foo].Argument", // + "test;X;test;Y;Method[foo].Member", // + ] + } +} + +class IsTesting extends ApiGraphModels::TestAllModels { + IsTesting() { this = this } +} + +query predicate warning = ModelOutput::getAWarning/0; From d4b882519af5fa716c3794a638dc0d3af36a8edd Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 26 Apr 2022 14:47:50 +0200 Subject: [PATCH 007/125] convert most of the asyncpg model to MaD --- python/ql/lib/semmle/python/Concepts.qll | 16 +++ .../lib/semmle/python/frameworks/Asyncpg.qll | 120 ++++++------------ .../library-tests/frameworks/asyncpg/test.py | 12 +- 3 files changed, 60 insertions(+), 88 deletions(-) diff --git a/python/ql/lib/semmle/python/Concepts.qll b/python/ql/lib/semmle/python/Concepts.qll index dc461861ace..e734105cd2e 100644 --- a/python/ql/lib/semmle/python/Concepts.qll +++ b/python/ql/lib/semmle/python/Concepts.qll @@ -62,6 +62,14 @@ module FileSystemAccess { /** Gets an argument to this file system access that is interpreted as a path. */ abstract DataFlow::Node getAPathArgument(); } + + private import semmle.python.frameworks.data.ModelsAsData + + private class DataAsFileAccess extends Range { + DataAsFileAccess() { this = ModelOutput::getASinkNode("file-access").getARhs() } + + override DataFlow::Node getAPathArgument() { result = this } + } } /** @@ -364,6 +372,14 @@ module SqlExecution { /** Gets the argument that specifies the SQL statements to be executed. */ abstract DataFlow::Node getSql(); } + + private import semmle.python.frameworks.data.ModelsAsData + + private class DataAsSqlExecution extends Range { + DataAsSqlExecution() { this = ModelOutput::getASinkNode("sql-injection").getARhs() } + + override DataFlow::Node getSql() { result = this } + } } /** diff --git a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll index 45c274c64ba..2b2b9ed2e1b 100644 --- a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll +++ b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll @@ -7,91 +7,42 @@ private import python private import semmle.python.dataflow.new.DataFlow private import semmle.python.Concepts private import semmle.python.ApiGraphs +private import semmle.python.frameworks.data.ModelsAsData /** Provides models for the `asyncpg` PyPI package. */ private module Asyncpg { - private import semmle.python.internal.Awaited - - /** Gets a `ConnectionPool` that is created when the result of `asyncpg.create_pool()` is awaited. */ - API::Node connectionPool() { - result = API::moduleImport("asyncpg").getMember("create_pool").getReturn().getAwaited() - } - - /** - * Gets a `Connection` that is created when - * - the result of `asyncpg.connect()` is awaited. - * - the result of calling `aquire` on a `ConnectionPool` is awaited. - */ - API::Node connection() { - result = API::moduleImport("asyncpg").getMember("connect").getReturn().getAwaited() - or - result = connectionPool().getMember("acquire").getReturn().getAwaited() - } - - /** `Connection`s and `ConnectionPool`s provide some methods that execute SQL. */ - class SqlExecutionOnConnection extends SqlExecution::Range, DataFlow::MethodCallNode { - string methodName; - - SqlExecutionOnConnection() { - this = [connectionPool(), connection()].getMember(methodName).getACall() and - methodName in ["copy_from_query", "execute", "fetch", "fetchrow", "fetchval", "executemany"] - } - - override DataFlow::Node getSql() { - methodName in ["copy_from_query", "execute", "fetch", "fetchrow", "fetchval"] and - result in [this.getArg(0), this.getArgByName("query")] - or - methodName = "executemany" and - result in [this.getArg(0), this.getArgByName("command")] + class AsyncpgModel extends ModelInput::TypeModelCsv { + override predicate row(string row) { + // package1;type1;package2;type2;path + row = + [ + // a `ConnectionPool` that is created when the result of `asyncpg.create_pool()` is awaited. + "asyncpg;ConnectionPool;asyncpg;;Member[create_pool].ReturnValue.Awaited", + // a `Connection` that is created when + // * - the result of `asyncpg.connect()` is awaited. + // * - the result of calling `aquire` on a `ConnectionPool` is awaited. + "asyncpg;Connection;asyncpg;;Member[connect].ReturnValue.Awaited", + "asyncpg;Connection;asyncpg;ConnectionPool;Member[acquire].ReturnValue.Awaited", + // Creating an internal `~Connection` type that contains both `Connection` and `ConnectionPool`. + "asyncpg;~Connection;asyncpg;Connection;", "asyncpg;~Connection;asyncpg;ConnectionPool;" + ] } } - /** A model of `Connection` and `ConnectionPool`, which provide some methods that access the file system. */ - class FileAccessOnConnection extends FileSystemAccess::Range, DataFlow::MethodCallNode { - string methodName; - - FileAccessOnConnection() { - this = [connectionPool(), connection()].getMember(methodName).getACall() and - methodName in ["copy_from_query", "copy_from_table", "copy_to_table"] - } - - // The path argument is keyword only. - override DataFlow::Node getAPathArgument() { - methodName in ["copy_from_query", "copy_from_table"] and - result = this.getArgByName("output") - or - methodName = "copy_to_table" and - result = this.getArgByName("source") - } - } - - /** - * Provides models of the `PreparedStatement` class in `asyncpg`. - * `PreparedStatement`s are created when the result of calling `prepare(query)` on a connection is awaited. - * The result of calling `prepare(query)` is a `PreparedStatementFactory` and the argument, `query` needs to - * be tracked to the place where a `PreparedStatement` is created and then futher to any executing methods. - * Hence the two type trackers. - */ - module PreparedStatement { - class PreparedStatementConstruction extends SqlConstruction::Range, API::CallNode { - PreparedStatementConstruction() { this = connection().getMember("prepare").getACall() } - - override DataFlow::Node getSql() { result = this.getParameter(0, "query").getARhs() } - } - - class PreparedStatementExecution extends SqlExecution::Range, API::CallNode { - PreparedStatementConstruction prepareCall; - - PreparedStatementExecution() { - this = - prepareCall - .getReturn() - .getAwaited() - .getMember(["executemany", "fetch", "fetchrow", "fetchval"]) - .getACall() - } - - override DataFlow::Node getSql() { result = prepareCall.getSql() } + class AsyncpgSink extends ModelInput::SinkModelCsv { + // package;type;path;kind + override predicate row(string row) { + row = + [ + // `Connection`s and `ConnectionPool`s provide some methods that execute SQL. + "asyncpg;~Connection;Member[copy_from_query,execute,fetch,fetchrow,fetchval].Argument[0,query:];sql-injection", + "asyncpg;~Connection;Member[executemany].Argument[0,command:];sql-injection", + // A model of `Connection` and `ConnectionPool`, which provide some methods that access the file system. + "asyncpg;~Connection;Member[copy_from_query,copy_from_table].Argument[output:];file-access", + "asyncpg;~Connection;Member[copy_to_table].Argument[source:];file-access", + // the `PreparedStatement` class in `asyncpg`. + "asyncpg;Connection;Member[prepare].Argument[0,query:];sql-injection", + ] } } @@ -106,7 +57,9 @@ private module Asyncpg { */ module Cursor { class CursorConstruction extends SqlConstruction::Range, API::CallNode { - CursorConstruction() { this = connection().getMember("cursor").getACall() } + CursorConstruction() { + this = ModelOutput::getATypeNode("asyncpg", "Connection").getMember("cursor").getACall() + } override DataFlow::Node getSql() { result = this.getParameter(0, "query").getARhs() } } @@ -121,8 +74,11 @@ private module Asyncpg { this = c.getReturn().getAwaited().getAnImmediateUse() ) or - exists(PreparedStatement::PreparedStatementConstruction prepareCall | - sql = prepareCall.getSql() and + exists(API::CallNode prepareCall | + prepareCall = + ModelOutput::getATypeNode("asyncpg", "Connection").getMember("prepare").getACall() + | + sql = prepareCall.getParameter(0, "query").getARhs() and this = prepareCall .getReturn() diff --git a/python/ql/test/library-tests/frameworks/asyncpg/test.py b/python/ql/test/library-tests/frameworks/asyncpg/test.py index e4b3c895ece..572fc454bed 100644 --- a/python/ql/test/library-tests/frameworks/asyncpg/test.py +++ b/python/ql/test/library-tests/frameworks/asyncpg/test.py @@ -27,11 +27,11 @@ async def test_prepared_statement(): conn = await asyncpg.connect() try: - pstmt = await conn.prepare("psql") # $ constructedSql="psql" - pstmt.executemany() # $ getSql="psql" - pstmt.fetch() # $ getSql="psql" - pstmt.fetchrow() # $ getSql="psql" - pstmt.fetchval() # $ getSql="psql" + pstmt = await conn.prepare("psql") # $ getSql="psql" + pstmt.executemany() + pstmt.fetch() + pstmt.fetchrow() + pstmt.fetchval() finally: await conn.close() @@ -46,7 +46,7 @@ async def test_cursor(): cursor = await conn.cursor("sql") # $ getSql="sql" constructedSql="sql" await cursor.fetch() - pstmt = await conn.prepare("psql") # $ constructedSql="psql" + pstmt = await conn.prepare("psql") # $ getSql="psql" pcursor = await pstmt.cursor() # $ getSql="psql" await pcursor.fetch() From 86a9bc6aca9599c2b7b999d2cfe4fb6b7332564c Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 26 Apr 2022 15:02:56 +0200 Subject: [PATCH 008/125] add test for keyword arguments --- .../frameworks/data/test.expected | 17 ++++++++++++++++ .../library-tests/frameworks/data/test.py | 12 +++++++++-- .../library-tests/frameworks/data/test.ql | 20 +++++++++++++++++-- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index afdd0b91a9d..1181bde6021 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -1,9 +1,26 @@ taintFlow | test.py:3:5:3:15 | ControlFlowNode for getSource() | test.py:4:8:4:8 | ControlFlowNode for x | +| test.py:3:5:3:15 | ControlFlowNode for getSource() | test.py:7:17:7:17 | ControlFlowNode for x | +| test.py:9:8:9:14 | ControlFlowNode for alias() | test.py:9:8:9:14 | ControlFlowNode for alias() | +| test.py:10:8:10:22 | ControlFlowNode for Attribute() | test.py:10:8:10:22 | ControlFlowNode for Attribute() | +| test.py:11:8:11:30 | ControlFlowNode for Attribute() | test.py:11:8:11:30 | ControlFlowNode for Attribute() | isSink | test.py:4:8:4:8 | ControlFlowNode for x | test-sink | +| test.py:7:17:7:17 | ControlFlowNode for x | test-sink | +| test.py:9:8:9:14 | ControlFlowNode for alias() | test-sink | +| test.py:10:8:10:22 | ControlFlowNode for Attribute() | test-sink | +| test.py:11:8:11:30 | ControlFlowNode for Attribute() | test-sink | +| test.py:12:8:12:34 | ControlFlowNode for Attribute() | test-sink | isSource | test.py:3:5:3:15 | ControlFlowNode for getSource() | test-source | +| test.py:9:8:9:14 | ControlFlowNode for alias() | test-source | +| test.py:10:8:10:14 | ControlFlowNode for alias() | test-source | +| test.py:10:8:10:22 | ControlFlowNode for Attribute() | test-source | +| test.py:11:8:11:14 | ControlFlowNode for alias() | test-source | +| test.py:11:8:11:22 | ControlFlowNode for Attribute() | test-source | +| test.py:11:8:11:30 | ControlFlowNode for Attribute() | test-source | +| test.py:12:8:12:14 | ControlFlowNode for alias() | test-source | +| test.py:12:8:12:22 | ControlFlowNode for Attribute() | test-source | syntaxErrors | Member[foo | | Member[foo] .Member[bar] | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index 23da10f806d..fd1790ac6e8 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -1,4 +1,12 @@ -from testlib import getSource, mySink +from testlib import getSource, mySink, alias x = getSource() -mySink(x) \ No newline at end of file +mySink(x) + +mySink(foo=x) # OK +mySink(sinkName=x) # NOT OK + +mySink(alias()) # NOT OK +mySink(alias().chain()) # NOT OK +mySink(alias().chain().chain()) # NOT OK +mySink(alias().chain().safeThing()) # OK \ No newline at end of file diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index da1d80bf276..746fee47e22 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -3,6 +3,7 @@ import semmle.python.frameworks.data.internal.AccessPathSyntax as AccessPathSynt import semmle.python.frameworks.data.ModelsAsData import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.DataFlow +private import semmle.python.ApiGraphs // TODO: /* @@ -23,10 +24,21 @@ import semmle.python.dataflow.new.DataFlow * } */ +class Types extends ModelInput::TypeModelCsv { + override predicate row(string row) { + // package1;type1;package2;type2;path + row = + [ + "testlib;Alias;testlib;;Member[alias].ReturnValue", + "testlib;Alias;testlib;Alias;Member[chain].ReturnValue", + ] + } +} + class Sinks extends ModelInput::SinkModelCsv { override predicate row(string row) { // package;type;path;kind - row = ["testlib;;Member[mySink].Argument[0];test-sink"] + row = ["testlib;;Member[mySink].Argument[0,sinkName:];test-sink"] } } @@ -37,7 +49,11 @@ class Sinks extends ModelInput::SinkModelCsv { class Sources extends ModelInput::SourceModelCsv { // package;type;path;kind override predicate row(string row) { - row = ["testlib;;Member[getSource].ReturnValue;test-source"] + row = + [ + "testlib;;Member[getSource].ReturnValue;test-source", // + "testlib;Alias;;test-source" + ] } } From 35b143a1a5ae7989665d7199c46f1b940704f4d1 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 27 Apr 2022 09:26:04 +0200 Subject: [PATCH 009/125] add tests for argument syntax --- .../test/library-tests/frameworks/data/test.expected | 6 ++++++ python/ql/test/library-tests/frameworks/data/test.py | 9 ++++++++- python/ql/test/library-tests/frameworks/data/test.ql | 12 ++++++++++-- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 1181bde6021..045ef365012 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -11,6 +11,12 @@ isSink | test.py:10:8:10:22 | ControlFlowNode for Attribute() | test-sink | | test.py:11:8:11:30 | ControlFlowNode for Attribute() | test-sink | | test.py:12:8:12:34 | ControlFlowNode for Attribute() | test-sink | +| test.py:16:11:16:13 | ControlFlowNode for one | test-source | +| test.py:17:19:17:21 | ControlFlowNode for two | test-source | +| test.py:17:24:17:28 | ControlFlowNode for three | test-source | +| test.py:17:31:17:34 | ControlFlowNode for four | test-source | +| test.py:18:37:18:40 | ControlFlowNode for five | test-source | +| test.py:19:21:19:26 | ControlFlowNode for second | test-source | isSource | test.py:3:5:3:15 | ControlFlowNode for getSource() | test-source | | test.py:9:8:9:14 | ControlFlowNode for alias() | test-source | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index fd1790ac6e8..5108518ecf3 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -9,4 +9,11 @@ mySink(sinkName=x) # NOT OK mySink(alias()) # NOT OK mySink(alias().chain()) # NOT OK mySink(alias().chain().chain()) # NOT OK -mySink(alias().chain().safeThing()) # OK \ No newline at end of file +mySink(alias().chain().safeThing()) # OK + +from testlib import Args + +Args.arg0(one, two, three, four, five) +Args.arg1to3(one, two, three, four, five) +Args.lastarg(one, two, three, four, five) +Args.nonFist(first, second) \ No newline at end of file diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index 746fee47e22..4c8a04bcf19 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -38,7 +38,15 @@ class Types extends ModelInput::TypeModelCsv { class Sinks extends ModelInput::SinkModelCsv { override predicate row(string row) { // package;type;path;kind - row = ["testlib;;Member[mySink].Argument[0,sinkName:];test-sink"] + row = + [ + "testlib;;Member[mySink].Argument[0,sinkName:];test-sink", + // testing argument syntax + "testlib;;Member[Args].Member[arg0].Argument[0];test-source", // + "testlib;;Member[Args].Member[arg1to3].Argument[1..3];test-source", // + "testlib;;Member[Args].Member[lastarg].Argument[N-1];test-source", // + "testlib;;Member[Args].Member[nonFist].Argument[1..];test-source", // + ] } } @@ -52,7 +60,7 @@ class Sources extends ModelInput::SourceModelCsv { row = [ "testlib;;Member[getSource].ReturnValue;test-source", // - "testlib;Alias;;test-source" + "testlib;Alias;;test-source", ] } } From 20992af0377e20eb9d028b0844fa7c3a8e6c1988 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 27 Apr 2022 09:31:40 +0200 Subject: [PATCH 010/125] add test for parameter syntax --- .../ql/test/library-tests/frameworks/data/test.expected | 5 +++++ python/ql/test/library-tests/frameworks/data/test.py | 8 +++++++- python/ql/test/library-tests/frameworks/data/test.ql | 4 ++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 045ef365012..405efa858e3 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -27,6 +27,11 @@ isSource | test.py:11:8:11:30 | ControlFlowNode for Attribute() | test-source | | test.py:12:8:12:14 | ControlFlowNode for alias() | test-source | | test.py:12:8:12:22 | ControlFlowNode for Attribute() | test-source | +| test.py:23:24:23:26 | ControlFlowNode for one | test-source | +| test.py:24:33:24:35 | ControlFlowNode for two | test-source | +| test.py:24:38:24:42 | ControlFlowNode for three | test-source | +| test.py:24:45:24:48 | ControlFlowNode for four | test-source | +| test.py:25:34:25:39 | ControlFlowNode for second | test-source | syntaxErrors | Member[foo | | Member[foo] .Member[bar] | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index 5108518ecf3..da34b5ef56a 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -16,4 +16,10 @@ from testlib import Args Args.arg0(one, two, three, four, five) Args.arg1to3(one, two, three, four, five) Args.lastarg(one, two, three, four, five) -Args.nonFist(first, second) \ No newline at end of file +Args.nonFist(first, second) + +from testlib import Callbacks + +Callbacks.first(lambda one, two, three, four, five: 0) +Callbacks.param1to3(lambda one, two, three, four, five: 0) +Callbacks.nonFirst(lambda first, second: 0) \ No newline at end of file diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index 4c8a04bcf19..5574f3de199 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -61,6 +61,10 @@ class Sources extends ModelInput::SourceModelCsv { [ "testlib;;Member[getSource].ReturnValue;test-source", // "testlib;Alias;;test-source", + // testing parameter syntax + "testlib;;Member[Callbacks].Member[first].Argument[0].Parameter[0];test-source", // + "testlib;;Member[Callbacks].Member[param1to3].Argument[0].Parameter[1..3];test-source", // + "testlib;;Member[Callbacks].Member[nonFirst].Argument[0].Parameter[1..];test-source", // ] } } From 8d60336396f9c07301099fb7fa41ac55d68bc353 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 27 Apr 2022 09:41:13 +0200 Subject: [PATCH 011/125] add tests for callsite filters --- .../test/library-tests/frameworks/data/test.expected | 6 ++++++ python/ql/test/library-tests/frameworks/data/test.py | 10 +++++++++- python/ql/test/library-tests/frameworks/data/test.ql | 3 +++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 405efa858e3..c32b6f9a56f 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -17,6 +17,12 @@ isSink | test.py:17:31:17:34 | ControlFlowNode for four | test-source | | test.py:18:37:18:40 | ControlFlowNode for five | test-source | | test.py:19:21:19:26 | ControlFlowNode for second | test-source | +| test.py:30:21:30:23 | ControlFlowNode for one | test-source | +| test.py:32:22:32:24 | ControlFlowNode for one | test-source | +| test.py:32:27:32:29 | ControlFlowNode for two | test-source | +| test.py:33:22:33:24 | ControlFlowNode for one | test-source | +| test.py:33:27:33:29 | ControlFlowNode for two | test-source | +| test.py:33:32:33:36 | ControlFlowNode for three | test-source | isSource | test.py:3:5:3:15 | ControlFlowNode for getSource() | test-source | | test.py:9:8:9:14 | ControlFlowNode for alias() | test-source | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index da34b5ef56a..630621ec226 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -22,4 +22,12 @@ from testlib import Callbacks Callbacks.first(lambda one, two, three, four, five: 0) Callbacks.param1to3(lambda one, two, three, four, five: 0) -Callbacks.nonFirst(lambda first, second: 0) \ No newline at end of file +Callbacks.nonFirst(lambda first, second: 0) + +from testlib import CallFilter + +CallFilter.arityOne(one, two) # NO match +CallFilter.arityOne(one) # Match +CallFilter.twoOrMore(one) # NO match +CallFilter.twoOrMore(one, two) # Match +CallFilter.twoOrMore(one, two, three) # Match \ No newline at end of file diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index 5574f3de199..2aca54cd356 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -46,6 +46,9 @@ class Sinks extends ModelInput::SinkModelCsv { "testlib;;Member[Args].Member[arg1to3].Argument[1..3];test-source", // "testlib;;Member[Args].Member[lastarg].Argument[N-1];test-source", // "testlib;;Member[Args].Member[nonFist].Argument[1..];test-source", // + // callsite filter. + "testlib;;Member[CallFilter].Member[arityOne].WithArity[1].Argument[0..];test-source", // + "testlib;;Member[CallFilter].Member[twoOrMore].WithArity[2..].Argument[0..];test-source", // ] } } From 48408ca45d20ad7c0a6fa5a9351c5d9191b06676 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 27 Apr 2022 10:06:32 +0200 Subject: [PATCH 012/125] Add TODO list --- python/ql/test/library-tests/frameworks/data/test.ql | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index 2aca54cd356..35db27c557c 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -53,10 +53,12 @@ class Sinks extends ModelInput::SinkModelCsv { } } -// TODO: Test taint steps (include that the base path may end with ".Call") -// TODO: Ctrl + f: TODO -// TODO: // There are no API-graph edges for: ArrayElement, Element, MapKey, MapValue (remove from valid tokens list) -// TODO: Verify that the list of valid tokens matches the implementation. +// TODO: Named parameters? +// TODO: Commonly used tokens +// TODO: Uniform tokens for fields +// TODO: Non-positional arguments +// TODO: Any argument +// TODO: Test taint steps. class Sources extends ModelInput::SourceModelCsv { // package;type;path;kind override predicate row(string row) { From 682cab37378cd763238ed67c9e4f1b42910ee8a7 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 27 Apr 2022 11:01:10 +0200 Subject: [PATCH 013/125] add test for awaited --- .../ql/test/library-tests/frameworks/data/test.expected | 1 + python/ql/test/library-tests/frameworks/data/test.py | 8 +++++++- python/ql/test/library-tests/frameworks/data/test.ql | 7 +++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index c32b6f9a56f..b521a4a3018 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -38,6 +38,7 @@ isSource | test.py:24:38:24:42 | ControlFlowNode for three | test-source | | test.py:24:45:24:48 | ControlFlowNode for four | test-source | | test.py:25:34:25:39 | ControlFlowNode for second | test-source | +| test.py:39:11:39:20 | ControlFlowNode for Await | test-source | syntaxErrors | Member[foo | | Member[foo] .Member[bar] | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index 630621ec226..0dcbd633a2b 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -30,4 +30,10 @@ CallFilter.arityOne(one, two) # NO match CallFilter.arityOne(one) # Match CallFilter.twoOrMore(one) # NO match CallFilter.twoOrMore(one, two) # Match -CallFilter.twoOrMore(one, two, three) # Match \ No newline at end of file +CallFilter.twoOrMore(one, two, three) # Match + +from testlib import CommonTokens + +async def async_func(): + prom = CommonTokens.makePromise(1); + val = await prom \ No newline at end of file diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index 35db27c557c..96a4fe38cf2 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -53,12 +53,13 @@ class Sinks extends ModelInput::SinkModelCsv { } } -// TODO: Named parameters? // TODO: Commonly used tokens // TODO: Uniform tokens for fields -// TODO: Non-positional arguments +// TODO: Non-positional arguments (including Named parameters) // TODO: Any argument // TODO: Test taint steps. +// TODO: Should `instance()` be shorthand for `subClass*().getReturn()`? +// TODO: // There are no API-graph edges for: ArrayElement, Element, MapKey, MapValue (remove from valid tokens list) class Sources extends ModelInput::SourceModelCsv { // package;type;path;kind override predicate row(string row) { @@ -70,6 +71,8 @@ class Sources extends ModelInput::SourceModelCsv { "testlib;;Member[Callbacks].Member[first].Argument[0].Parameter[0];test-source", // "testlib;;Member[Callbacks].Member[param1to3].Argument[0].Parameter[1..3];test-source", // "testlib;;Member[Callbacks].Member[nonFirst].Argument[0].Parameter[1..];test-source", // + // Common tokens. + "testlib;;Member[CommonTokens].Member[makePromise].ReturnValue.Awaited;test-source", // ] } } From a02e812de8a05e491f868a372868eb51a0dcd0ba Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 27 Apr 2022 11:10:20 +0200 Subject: [PATCH 014/125] add test for the Instance token --- python/ql/test/library-tests/frameworks/data/test.expected | 1 + python/ql/test/library-tests/frameworks/data/test.py | 4 +++- python/ql/test/library-tests/frameworks/data/test.ql | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index b521a4a3018..c82438b93fc 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -39,6 +39,7 @@ isSource | test.py:24:45:24:48 | ControlFlowNode for four | test-source | | test.py:25:34:25:39 | ControlFlowNode for second | test-source | | test.py:39:11:39:20 | ControlFlowNode for Await | test-source | +| test.py:41:8:41:27 | ControlFlowNode for Attribute() | test-source | syntaxErrors | Member[foo | | Member[foo] .Member[bar] | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index 0dcbd633a2b..6860ef1211d 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -36,4 +36,6 @@ from testlib import CommonTokens async def async_func(): prom = CommonTokens.makePromise(1); - val = await prom \ No newline at end of file + val = await prom + +inst = CommonTokens.Class() \ No newline at end of file diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index 96a4fe38cf2..38b505cd768 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -73,6 +73,7 @@ class Sources extends ModelInput::SourceModelCsv { "testlib;;Member[Callbacks].Member[nonFirst].Argument[0].Parameter[1..];test-source", // // Common tokens. "testlib;;Member[CommonTokens].Member[makePromise].ReturnValue.Awaited;test-source", // + "testlib;;Member[CommonTokens].Member[Class].Instance;test-source", // ] } } From 46acce0ad42fc104911096f5f7d655eb627a251b Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 27 Apr 2022 11:14:50 +0200 Subject: [PATCH 015/125] add support for the Subclass token --- .../frameworks/data/internal/ApiGraphModelsSpecific.qll | 3 +++ python/ql/test/library-tests/frameworks/data/test.expected | 1 + python/ql/test/library-tests/frameworks/data/test.py | 7 ++++++- python/ql/test/library-tests/frameworks/data/test.ql | 2 ++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll index fc6e69404ee..d174f9416c7 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -57,6 +57,9 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) { or token.getName() = "Awaited" and result = node.getAwaited() + or + token.getName() = "Subclass" and + result = node.getASubclass*() // Some features don't have MaD tokens yet, they would need to be added to API-graphs first. // - decorators ("DecoratedClass", "DecoratedMember", "DecoratedParameter") // - Array/Map elements ("ArrayElement", "Element", "MapKey", "MapValue") diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index c82438b93fc..8f450186dd1 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -40,6 +40,7 @@ isSource | test.py:25:34:25:39 | ControlFlowNode for second | test-source | | test.py:39:11:39:20 | ControlFlowNode for Await | test-source | | test.py:41:8:41:27 | ControlFlowNode for Attribute() | test-source | +| test.py:46:7:46:16 | ControlFlowNode for SubClass() | test-source | syntaxErrors | Member[foo | | Member[foo] .Member[bar] | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index 6860ef1211d..707b7714dbc 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -38,4 +38,9 @@ async def async_func(): prom = CommonTokens.makePromise(1); val = await prom -inst = CommonTokens.Class() \ No newline at end of file +inst = CommonTokens.Class() + +class SubClass (CommonTokens.Super): + pass + +sub = SubClass() \ No newline at end of file diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index 38b505cd768..0d768404073 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -54,6 +54,7 @@ class Sinks extends ModelInput::SinkModelCsv { } // TODO: Commonly used tokens +// TODO: Should `instance()` be shorthand for `subClass*().getReturn()`? // TODO: Uniform tokens for fields // TODO: Non-positional arguments (including Named parameters) // TODO: Any argument @@ -74,6 +75,7 @@ class Sources extends ModelInput::SourceModelCsv { // Common tokens. "testlib;;Member[CommonTokens].Member[makePromise].ReturnValue.Awaited;test-source", // "testlib;;Member[CommonTokens].Member[Class].Instance;test-source", // + "testlib;;Member[CommonTokens].Member[Super].Subclass.Instance;test-source", // ] } } From ea01bcf5ec9055dbfb26672a30d87882ff36a2ef Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 27 Apr 2022 11:17:15 +0200 Subject: [PATCH 016/125] have the Instance token be an alias for Subclass.ReturnValue --- .../frameworks/data/internal/ApiGraphModelsSpecific.qll | 2 +- python/ql/test/library-tests/frameworks/data/test.expected | 1 + python/ql/test/library-tests/frameworks/data/test.py | 7 ++++++- python/ql/test/library-tests/frameworks/data/test.ql | 2 -- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll index d174f9416c7..113bcc71314 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -53,7 +53,7 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) { result = node.getMember(token.getAnArgument()) or token.getName() = "Instance" and - result = node.getReturn() // commonly used Token. In Python `Instance` is just an alias for `ReturnValue` + result = node.getASubclass*().getReturn() // In Python `Instance` is just an alias for `Subclass.ReturnValue` or token.getName() = "Awaited" and result = node.getAwaited() diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 8f450186dd1..93697631fb3 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -41,6 +41,7 @@ isSource | test.py:39:11:39:20 | ControlFlowNode for Await | test-source | | test.py:41:8:41:27 | ControlFlowNode for Attribute() | test-source | | test.py:46:7:46:16 | ControlFlowNode for SubClass() | test-source | +| test.py:51:8:51:18 | ControlFlowNode for Sub2Class() | test-source | syntaxErrors | Member[foo | | Member[foo] .Member[bar] | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index 707b7714dbc..aa0c9f2a6fc 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -43,4 +43,9 @@ inst = CommonTokens.Class() class SubClass (CommonTokens.Super): pass -sub = SubClass() \ No newline at end of file +sub = SubClass() + +class Sub2Class (CommonTokens.Class): + pass + +sub2 = Sub2Class() \ No newline at end of file diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index 0d768404073..bb19389bf9d 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -53,8 +53,6 @@ class Sinks extends ModelInput::SinkModelCsv { } } -// TODO: Commonly used tokens -// TODO: Should `instance()` be shorthand for `subClass*().getReturn()`? // TODO: Uniform tokens for fields // TODO: Non-positional arguments (including Named parameters) // TODO: Any argument From dc38aa8a96fc65097d5f44e2af27de8a161fc216 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 27 Apr 2022 11:35:17 +0200 Subject: [PATCH 017/125] add support for the Method[name] token --- .../frameworks/data/internal/ApiGraphModelsSpecific.qll | 5 ++++- python/ql/test/library-tests/frameworks/data/test.expected | 1 + python/ql/test/library-tests/frameworks/data/test.py | 4 +++- python/ql/test/library-tests/frameworks/data/test.ql | 3 ++- .../ql/test/library-tests/frameworks/data/warnings.expected | 5 ----- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll index 113bcc71314..d2de1f21724 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -60,6 +60,9 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) { or token.getName() = "Subclass" and result = node.getASubclass*() + or + token.getName() = "Method" and + result = node.getMember(token.getAnArgument()).getReturn() // Some features don't have MaD tokens yet, they would need to be added to API-graphs first. // - decorators ("DecoratedClass", "DecoratedMember", "DecoratedParameter") // - Array/Map elements ("ArrayElement", "Element", "MapKey", "MapValue") @@ -164,7 +167,7 @@ predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name) { */ bindingset[name, argument] predicate isExtraValidTokenArgumentInIdentifyingAccessPath(string name, string argument) { - name = ["Member"] and + name = ["Member", "Method"] and exists(argument) or name = ["Argument", "Parameter"] and diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 93697631fb3..72600cd8be5 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -42,6 +42,7 @@ isSource | test.py:41:8:41:27 | ControlFlowNode for Attribute() | test-source | | test.py:46:7:46:16 | ControlFlowNode for SubClass() | test-source | | test.py:51:8:51:18 | ControlFlowNode for Sub2Class() | test-source | +| test.py:53:7:53:16 | ControlFlowNode for Attribute() | test-source | syntaxErrors | Member[foo | | Member[foo] .Member[bar] | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index aa0c9f2a6fc..c53b6c55fbe 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -48,4 +48,6 @@ sub = SubClass() class Sub2Class (CommonTokens.Class): pass -sub2 = Sub2Class() \ No newline at end of file +sub2 = Sub2Class() + +val = inst.foo() \ No newline at end of file diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index bb19389bf9d..43549a36d8e 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -57,7 +57,6 @@ class Sinks extends ModelInput::SinkModelCsv { // TODO: Non-positional arguments (including Named parameters) // TODO: Any argument // TODO: Test taint steps. -// TODO: Should `instance()` be shorthand for `subClass*().getReturn()`? // TODO: // There are no API-graph edges for: ArrayElement, Element, MapKey, MapValue (remove from valid tokens list) class Sources extends ModelInput::SourceModelCsv { // package;type;path;kind @@ -74,6 +73,8 @@ class Sources extends ModelInput::SourceModelCsv { "testlib;;Member[CommonTokens].Member[makePromise].ReturnValue.Awaited;test-source", // "testlib;;Member[CommonTokens].Member[Class].Instance;test-source", // "testlib;;Member[CommonTokens].Member[Super].Subclass.Instance;test-source", // + // method + "testlib;;Member[CommonTokens].Member[Class].Instance.Method[foo];test-source", // ] } } diff --git a/python/ql/test/library-tests/frameworks/data/warnings.expected b/python/ql/test/library-tests/frameworks/data/warnings.expected index b8242f31a85..5cebb548358 100644 --- a/python/ql/test/library-tests/frameworks/data/warnings.expected +++ b/python/ql/test/library-tests/frameworks/data/warnings.expected @@ -2,11 +2,6 @@ | CSV type row should have 5 columns but has 8: test;TooManyColumns;;;Member[Foo].Instance;too;many;columns | | Invalid argument '0-1' in token 'Argument[0-1]' in access path: Method[foo].Argument[0-1] | | Invalid argument '*' in token 'Argument[*]' in access path: Method[foo].Argument[*] | -| Invalid argument 'foo' in token 'Method[foo]' in access path: Method[foo].Arg[0] | -| Invalid argument 'foo' in token 'Method[foo]' in access path: Method[foo].Argument | -| Invalid argument 'foo' in token 'Method[foo]' in access path: Method[foo].Argument[0-1] | -| Invalid argument 'foo' in token 'Method[foo]' in access path: Method[foo].Argument[*] | -| Invalid argument 'foo' in token 'Method[foo]' in access path: Method[foo].Member | | Invalid token 'Argument' is missing its arguments, in access path: Method[foo].Argument | | Invalid token 'Member' is missing its arguments, in access path: Method[foo].Member | | Invalid token name 'Arg' in access path: Method[foo].Arg[0] | From 547047ef19c0c3deea2690a0f16170828d535b47 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 27 Apr 2022 13:48:42 +0200 Subject: [PATCH 018/125] add self parameters to API-graphs, and add support for self parameters in MaD --- python/ql/lib/semmle/python/ApiGraphs.qll | 24 +++++++++++++++++++ .../data/internal/ApiGraphModelsSpecific.qll | 6 ++++- .../frameworks/data/test.expected | 2 ++ .../library-tests/frameworks/data/test.py | 10 +++++++- .../library-tests/frameworks/data/test.ql | 5 +++- 5 files changed, 44 insertions(+), 3 deletions(-) diff --git a/python/ql/lib/semmle/python/ApiGraphs.qll b/python/ql/lib/semmle/python/ApiGraphs.qll index 53c78dd3c19..61c57557bd9 100644 --- a/python/ql/lib/semmle/python/ApiGraphs.qll +++ b/python/ql/lib/semmle/python/ApiGraphs.qll @@ -136,6 +136,9 @@ module API { result = this.getASuccessor(Label::keywordParameter(name)) } + /** Gets the node representing the self parameter */ + Node getSelfParameter() { result = this.getASuccessor(Label::selfParameter()) } + /** * Gets the number of parameters of the function represented by this node. */ @@ -315,6 +318,12 @@ module API { /** Gets the API node for a parameter of this invocation. */ Node getAParameter() { result = this.getParameter(_) } + /** Gets the object that this method-call is being called on, if this is a method-call */ + Node getSelfParameter() { + result.getARhs() = this.(DataFlow::MethodCallNode).getObject() and + result = callee.getSelfParameter() + } + /** Gets the API node for the keyword parameter `name` of this invocation. */ Node getKeywordParameter(string name) { result = callee.getKeywordParameter(name) and @@ -595,6 +604,9 @@ module API { lbl = Label::keywordParameter(name) and ref.asExpr() = fn.getInnerScope().getArgByName(name) ) + or + lbl = Label::selfParameter() and + ref.asExpr() = any(PY::Parameter p | p = fn.getInnerScope().getAnArg() and p.isSelf()) ) or // Built-ins, treated as members of the module `builtins` @@ -661,6 +673,9 @@ module API { exists(string name | lbl = Label::keywordParameter(name) | arg = pred.getACall().getArgByName(name) ) + or + lbl = Label::selfParameter() and + arg = pred.getACall().(DataFlow::MethodCallNode).getObject() ) } @@ -777,6 +792,7 @@ module API { or exists(any(PY::Function f).getArgByName(name)) } or + MkLabelSelfParameter() or MkLabelReturn() or MkLabelSubclass() or MkLabelAwait() @@ -834,6 +850,11 @@ module API { string getName() { result = name } } + /** A label for the self parameter. */ + class LabelSelfParameter extends ApiLabel, MkLabelSelfParameter { + override string toString() { result = "getSelfParameter()" } + } + /** A label that gets the return value of a function. */ class LabelReturn extends ApiLabel, MkLabelReturn { override string toString() { result = "getReturn()" } @@ -873,6 +894,9 @@ module API { /** Gets the `parameter` edge label for the keyword parameter `name`. */ LabelKeywordParameter keywordParameter(string name) { result.getName() = name } + /** Gets the edge label for the self parameter. */ + LabelSelfParameter selfParameter() { any() } + /** Gets the `return` edge label. */ LabelReturn return() { any() } diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll index d2de1f21724..6136aa33bef 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -63,6 +63,10 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) { or token.getName() = "Method" and result = node.getMember(token.getAnArgument()).getReturn() + or + token.getName() = ["Argument", "Parameter"] and + token.getAnArgument() = "self" and + result = node.getSelfParameter() // Some features don't have MaD tokens yet, they would need to be added to API-graphs first. // - decorators ("DecoratedClass", "DecoratedMember", "DecoratedParameter") // - Array/Map elements ("ArrayElement", "Element", "MapKey", "MapValue") @@ -78,7 +82,7 @@ API::Node getExtraSuccessorFromInvoke(API::CallNode node, AccessPathToken token) or token.getName() = "Argument" and token.getAnArgument() = "self" and - result.getARhs() = node.(DataFlow::MethodCallNode).getObject() // TODO: Get proper support for this in API-graphs? + result = node.getSelfParameter() or token.getName() = "Argument" and exists(string arg | arg + ":" = token.getAnArgument() | result = node.getKeywordParameter(arg)) diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 72600cd8be5..36af5305a63 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -23,6 +23,7 @@ isSink | test.py:33:22:33:24 | ControlFlowNode for one | test-source | | test.py:33:27:33:29 | ControlFlowNode for two | test-source | | test.py:33:32:33:36 | ControlFlowNode for three | test-source | +| test.py:57:7:57:12 | ControlFlowNode for ArgPos | test-source | isSource | test.py:3:5:3:15 | ControlFlowNode for getSource() | test-source | | test.py:9:8:9:14 | ControlFlowNode for alias() | test-source | @@ -43,6 +44,7 @@ isSource | test.py:46:7:46:16 | ControlFlowNode for SubClass() | test-source | | test.py:51:8:51:18 | ControlFlowNode for Sub2Class() | test-source | | test.py:53:7:53:16 | ControlFlowNode for Attribute() | test-source | +| test.py:60:13:60:16 | ControlFlowNode for self | test-source | syntaxErrors | Member[foo | | Member[foo] .Member[bar] | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index c53b6c55fbe..920378d3470 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -50,4 +50,12 @@ class Sub2Class (CommonTokens.Class): sub2 = Sub2Class() -val = inst.foo() \ No newline at end of file +val = inst.foo() + +from testlib import ArgPos + +val = ArgPos.selfThing(arg, named=2) + +class SubClass (ArgPos.MyClass): + def foo(self, arg, named=2): + pass \ No newline at end of file diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index 43549a36d8e..81c105aecff 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -49,11 +49,12 @@ class Sinks extends ModelInput::SinkModelCsv { // callsite filter. "testlib;;Member[CallFilter].Member[arityOne].WithArity[1].Argument[0..];test-source", // "testlib;;Member[CallFilter].Member[twoOrMore].WithArity[2..].Argument[0..];test-source", // + // testing non-positional arguments + "testlib;;Member[ArgPos].Member[selfThing].Argument[self];test-source", // ] } } -// TODO: Uniform tokens for fields // TODO: Non-positional arguments (including Named parameters) // TODO: Any argument // TODO: Test taint steps. @@ -75,6 +76,8 @@ class Sources extends ModelInput::SourceModelCsv { "testlib;;Member[CommonTokens].Member[Super].Subclass.Instance;test-source", // // method "testlib;;Member[CommonTokens].Member[Class].Instance.Method[foo];test-source", // + // testing non-positional arguments + "testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[foo].Parameter[self];test-source", // ] } } From c1d3738fb8b61f08436cd6b08620352a36964e76 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 2 May 2022 12:52:02 +0200 Subject: [PATCH 019/125] fix API-graphs such that the first parameter is the first non-self parameter --- python/ql/lib/semmle/python/ApiGraphs.qll | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/python/ql/lib/semmle/python/ApiGraphs.qll b/python/ql/lib/semmle/python/ApiGraphs.qll index 61c57557bd9..77574ab44ad 100644 --- a/python/ql/lib/semmle/python/ApiGraphs.qll +++ b/python/ql/lib/semmle/python/ApiGraphs.qll @@ -595,8 +595,12 @@ module API { exists(DataFlow::Node def, PY::CallableExpr fn | rhs(base, def) and fn = trackDefNode(def).asExpr() | - exists(int i | - lbl = Label::parameter(i) and + exists(int i, int offset | + if exists(PY::Parameter p | p = fn.getInnerScope().getAnArg() and p.isSelf()) + then offset = 1 + else offset = 0 + | + lbl = Label::parameter(i - offset) and ref.asExpr() = fn.getInnerScope().getArg(i) ) or From 413d182bcfdf34f6f81237ebb530e6de3df0d3d9 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 2 May 2022 12:56:44 +0200 Subject: [PATCH 020/125] add support for named parameters --- .../data/internal/ApiGraphModelsSpecific.qll | 10 ++++++++-- python/ql/test/library-tests/frameworks/data/test.py | 7 +++++-- python/ql/test/library-tests/frameworks/data/test.ql | 3 ++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll index 6136aa33bef..4e6c3e571a8 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -65,8 +65,14 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) { result = node.getMember(token.getAnArgument()).getReturn() or token.getName() = ["Argument", "Parameter"] and - token.getAnArgument() = "self" and - result = node.getSelfParameter() + ( + token.getAnArgument() = "self" and + result = node.getSelfParameter() + or + exists(string name | token.getAnArgument() = name + ":" | + result = node.getKeywordParameter(name) + ) + ) // Some features don't have MaD tokens yet, they would need to be added to API-graphs first. // - decorators ("DecoratedClass", "DecoratedMember", "DecoratedParameter") // - Array/Map elements ("ArrayElement", "Element", "MapKey", "MapValue") diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index 920378d3470..67f77a54520 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -57,5 +57,8 @@ from testlib import ArgPos val = ArgPos.selfThing(arg, named=2) class SubClass (ArgPos.MyClass): - def foo(self, arg, named=2): - pass \ No newline at end of file + def foo(self, arg, named=2, otherName=3): + pass + + def secondAndAfter(self, arg1, arg2, arg3, arg4, arg5): + pass diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index 81c105aecff..acc78789459 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -55,7 +55,6 @@ class Sinks extends ModelInput::SinkModelCsv { } } -// TODO: Non-positional arguments (including Named parameters) // TODO: Any argument // TODO: Test taint steps. // TODO: // There are no API-graph edges for: ArrayElement, Element, MapKey, MapValue (remove from valid tokens list) @@ -78,6 +77,8 @@ class Sources extends ModelInput::SourceModelCsv { "testlib;;Member[CommonTokens].Member[Class].Instance.Method[foo];test-source", // // testing non-positional arguments "testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[foo].Parameter[self];test-source", // + "testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[foo].Parameter[named:];test-source", // + "testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[secondAndAfter].Parameter[1..];test-source", // ] } } From b1fa7f86a837b46b03afd1b9025ae9f13ee3daee Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 2 May 2022 12:58:15 +0200 Subject: [PATCH 021/125] add support for the any argument tokens --- .../frameworks/data/internal/ApiGraphModelsSpecific.qll | 8 +++++++- .../ql/test/library-tests/frameworks/data/test.expected | 9 +++++++++ python/ql/test/library-tests/frameworks/data/test.py | 3 +++ python/ql/test/library-tests/frameworks/data/test.ql | 3 +++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll index 4e6c3e571a8..abe41fd4e66 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -72,6 +72,12 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) { exists(string name | token.getAnArgument() = name + ":" | result = node.getKeywordParameter(name) ) + or + token.getAnArgument() = "any" and + result = [node.getParameter(_), node.getKeywordParameter(_)] + or + token.getAnArgument() = "any-named" and + result = node.getKeywordParameter(_) ) // Some features don't have MaD tokens yet, they would need to be added to API-graphs first. // - decorators ("DecoratedClass", "DecoratedMember", "DecoratedParameter") @@ -182,7 +188,7 @@ predicate isExtraValidTokenArgumentInIdentifyingAccessPath(string name, string a or name = ["Argument", "Parameter"] and ( - argument = "self" + argument = ["self", "any", "any-named"] or argument.regexpMatch("\\w+:") // keyword argument ) diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 36af5305a63..e31b3a38228 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -24,6 +24,10 @@ isSink | test.py:33:27:33:29 | ControlFlowNode for two | test-source | | test.py:33:32:33:36 | ControlFlowNode for three | test-source | | test.py:57:7:57:12 | ControlFlowNode for ArgPos | test-source | +| test.py:66:17:66:20 | ControlFlowNode for arg1 | test-source | +| test.py:66:23:66:26 | ControlFlowNode for arg2 | test-source | +| test.py:66:34:66:43 | ControlFlowNode for namedThing | test-source | +| test.py:67:34:67:44 | ControlFlowNode for secondNamed | test-source | isSource | test.py:3:5:3:15 | ControlFlowNode for getSource() | test-source | | test.py:9:8:9:14 | ControlFlowNode for alias() | test-source | @@ -45,6 +49,11 @@ isSource | test.py:51:8:51:18 | ControlFlowNode for Sub2Class() | test-source | | test.py:53:7:53:16 | ControlFlowNode for Attribute() | test-source | | test.py:60:13:60:16 | ControlFlowNode for self | test-source | +| test.py:60:24:60:28 | ControlFlowNode for named | test-source | +| test.py:63:36:63:39 | ControlFlowNode for arg2 | test-source | +| test.py:63:42:63:45 | ControlFlowNode for arg3 | test-source | +| test.py:63:48:63:51 | ControlFlowNode for arg4 | test-source | +| test.py:63:54:63:57 | ControlFlowNode for arg5 | test-source | syntaxErrors | Member[foo | | Member[foo] .Member[bar] | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index 67f77a54520..dbc241c71e9 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -62,3 +62,6 @@ class SubClass (ArgPos.MyClass): def secondAndAfter(self, arg1, arg2, arg3, arg4, arg5): pass + +ArgPos.anyParam(arg1, arg2, name=namedThing) +ArgPos.anyNamed(arg4, arg5, name=secondNamed) \ No newline at end of file diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index acc78789459..d2068df23e2 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -51,6 +51,9 @@ class Sinks extends ModelInput::SinkModelCsv { "testlib;;Member[CallFilter].Member[twoOrMore].WithArity[2..].Argument[0..];test-source", // // testing non-positional arguments "testlib;;Member[ArgPos].Member[selfThing].Argument[self];test-source", // + // any argument + "testlib;;Member[ArgPos].Member[anyParam].Argument[any];test-source", // + "testlib;;Member[ArgPos].Member[anyNamed].Argument[any-named];test-source", // ] } } From a8790412dda666894fd73cbb913f6186b43762a2 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 27 Apr 2022 22:49:43 +0200 Subject: [PATCH 022/125] add support for the Argument[any] and Argument[any-named] tokens --- .../data/internal/ApiGraphModelsSpecific.qll | 19 +++++++++++++------ .../library-tests/frameworks/data/test.ql | 3 +-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll index abe41fd4e66..206a6ff1e52 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -92,12 +92,19 @@ API::Node getExtraSuccessorFromInvoke(API::CallNode node, AccessPathToken token) token.getName() = "Instance" and result = node.getReturn() or - token.getName() = "Argument" and - token.getAnArgument() = "self" and - result = node.getSelfParameter() - or - token.getName() = "Argument" and - exists(string arg | arg + ":" = token.getAnArgument() | result = node.getKeywordParameter(arg)) + token.getName() = ["Argument", "Parameter"] and + ( + token.getAnArgument() = "self" and + result = node.getSelfParameter() + or + token.getAnArgument() = "any" and + result = [node.getParameter(_), node.getKeywordParameter(_)] + or + token.getAnArgument() = "any-named" and + result = node.getKeywordParameter(_) + or + exists(string arg | arg + ":" = token.getAnArgument() | result = node.getKeywordParameter(arg)) + ) } /** diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index d2068df23e2..9986011e89f 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -58,8 +58,7 @@ class Sinks extends ModelInput::SinkModelCsv { } } -// TODO: Any argument -// TODO: Test taint steps. +// TODO: Test taint steps (include that the base path may end with ".Call") // TODO: // There are no API-graph edges for: ArrayElement, Element, MapKey, MapValue (remove from valid tokens list) class Sources extends ModelInput::SourceModelCsv { // package;type;path;kind From 649df1dd317100c78a34c4b712c133422e224a37 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 27 Apr 2022 23:48:39 +0200 Subject: [PATCH 023/125] simple taint-flow test --- .../frameworks/data/test.expected | 5 +++ .../library-tests/frameworks/data/test.py | 7 +++- .../library-tests/frameworks/data/test.ql | 33 +++++++++---------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index e31b3a38228..eafa55318aa 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -4,6 +4,7 @@ taintFlow | test.py:9:8:9:14 | ControlFlowNode for alias() | test.py:9:8:9:14 | ControlFlowNode for alias() | | test.py:10:8:10:22 | ControlFlowNode for Attribute() | test.py:10:8:10:22 | ControlFlowNode for Attribute() | | test.py:11:8:11:30 | ControlFlowNode for Attribute() | test.py:11:8:11:30 | ControlFlowNode for Attribute() | +| test.py:71:28:71:38 | ControlFlowNode for getSource() | test.py:71:8:71:39 | ControlFlowNode for Attribute() | isSink | test.py:4:8:4:8 | ControlFlowNode for x | test-sink | | test.py:7:17:7:17 | ControlFlowNode for x | test-sink | @@ -28,6 +29,8 @@ isSink | test.py:66:23:66:26 | ControlFlowNode for arg2 | test-source | | test.py:66:34:66:43 | ControlFlowNode for namedThing | test-source | | test.py:67:34:67:44 | ControlFlowNode for secondNamed | test-source | +| test.py:71:8:71:39 | ControlFlowNode for Attribute() | test-sink | +| test.py:72:8:72:47 | ControlFlowNode for Attribute() | test-sink | isSource | test.py:3:5:3:15 | ControlFlowNode for getSource() | test-source | | test.py:9:8:9:14 | ControlFlowNode for alias() | test-source | @@ -54,6 +57,8 @@ isSource | test.py:63:42:63:45 | ControlFlowNode for arg3 | test-source | | test.py:63:48:63:51 | ControlFlowNode for arg4 | test-source | | test.py:63:54:63:57 | ControlFlowNode for arg5 | test-source | +| test.py:71:28:71:38 | ControlFlowNode for getSource() | test-source | +| test.py:72:36:72:46 | ControlFlowNode for getSource() | test-source | syntaxErrors | Member[foo | | Member[foo] .Member[bar] | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index dbc241c71e9..8780838930d 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -64,4 +64,9 @@ class SubClass (ArgPos.MyClass): pass ArgPos.anyParam(arg1, arg2, name=namedThing) -ArgPos.anyNamed(arg4, arg5, name=secondNamed) \ No newline at end of file +ArgPos.anyNamed(arg4, arg5, name=secondNamed) + +from testlib import Steps + +mySink(Steps.preserveTaint(getSource())) # FLOW +mySink(Steps.preserveTaint("safe", getSource())) # NO FLOW \ No newline at end of file diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index 9986011e89f..e2fd53b142a 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -5,24 +5,21 @@ import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.DataFlow private import semmle.python.ApiGraphs -// TODO: -/* - * class Steps extends ModelInput::SummaryModelCsv { - * override predicate row(string row) { - * // package;type;path;input;output;kind - * row = - * [ - * "testlib;;Member[preserveTaint];Argument[0];ReturnValue;taint", - * "testlib;;Member[taintIntoCallback];Argument[0];Argument[1..2].Parameter[0];taint", - * "testlib;;Member[taintIntoCallbackThis];Argument[0];Argument[1..2].Parameter[this];taint", - * "testlib;;Member[preserveArgZeroAndTwo];Argument[0,2];ReturnValue;taint", - * "testlib;;Member[preserveAllButFirstArgument];Argument[1..];ReturnValue;taint", - * "testlib;;Member[preserveAllIfCall].Call;Argument[0..];ReturnValue;taint", - * "testlib;;Member[getSource].ReturnValue.Member[continue];Argument[this];ReturnValue;taint", - * ] - * } - * } - */ +class Steps extends ModelInput::SummaryModelCsv { + override predicate row(string row) { + // package;type;path;input;output;kind + row = + [ + "testlib;;Member[Steps].Member[preserveTaint];Argument[0];ReturnValue;taint", + // "testlib;;Member[Steps].Member[taintIntoCallback];Argument[0];Argument[1..2].Parameter[0];taint", + // "testlib;;Member[Steps].Member[taintIntoCallbackThis];Argument[0];Argument[1..2].Parameter[this];taint", + // "testlib;;Member[Steps].Member[preserveArgZeroAndTwo];Argument[0,2];ReturnValue;taint", + // "testlib;;Member[Steps].Member[preserveAllButFirstArgument];Argument[1..];ReturnValue;taint", + // "testlib;;Member[Steps].Member[preserveAllIfCall].Call;Argument[0..];ReturnValue;taint", + // "testlib;;Member[Steps].Member[getSource].ReturnValue.Member[continue];Argument[this];ReturnValue;taint", + ] + } +} class Types extends ModelInput::TypeModelCsv { override predicate row(string row) { From 0f1e070d82409691a1e32abea69efdec06a365bc Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 27 Apr 2022 23:51:28 +0200 Subject: [PATCH 024/125] second test of taint steps --- .../ql/test/library-tests/frameworks/data/test.expected | 6 ++++++ python/ql/test/library-tests/frameworks/data/test.py | 9 ++++++++- python/ql/test/library-tests/frameworks/data/test.ql | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index eafa55318aa..d336ac76b88 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -5,6 +5,8 @@ taintFlow | test.py:10:8:10:22 | ControlFlowNode for Attribute() | test.py:10:8:10:22 | ControlFlowNode for Attribute() | | test.py:11:8:11:30 | ControlFlowNode for Attribute() | test.py:11:8:11:30 | ControlFlowNode for Attribute() | | test.py:71:28:71:38 | ControlFlowNode for getSource() | test.py:71:8:71:39 | ControlFlowNode for Attribute() | +| test.py:75:5:75:15 | ControlFlowNode for getSource() | test.py:76:22:76:22 | ControlFlowNode for x | +| test.py:75:5:75:15 | ControlFlowNode for getSource() | test.py:77:22:77:22 | ControlFlowNode for y | isSink | test.py:4:8:4:8 | ControlFlowNode for x | test-sink | | test.py:7:17:7:17 | ControlFlowNode for x | test-sink | @@ -31,6 +33,9 @@ isSink | test.py:67:34:67:44 | ControlFlowNode for secondNamed | test-source | | test.py:71:8:71:39 | ControlFlowNode for Attribute() | test-sink | | test.py:72:8:72:47 | ControlFlowNode for Attribute() | test-sink | +| test.py:76:22:76:22 | ControlFlowNode for x | test-sink | +| test.py:77:22:77:22 | ControlFlowNode for y | test-sink | +| test.py:78:22:78:22 | ControlFlowNode for z | test-sink | isSource | test.py:3:5:3:15 | ControlFlowNode for getSource() | test-source | | test.py:9:8:9:14 | ControlFlowNode for alias() | test-source | @@ -59,6 +64,7 @@ isSource | test.py:63:54:63:57 | ControlFlowNode for arg5 | test-source | | test.py:71:28:71:38 | ControlFlowNode for getSource() | test-source | | test.py:72:36:72:46 | ControlFlowNode for getSource() | test-source | +| test.py:75:5:75:15 | ControlFlowNode for getSource() | test-source | syntaxErrors | Member[foo | | Member[foo] .Member[bar] | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index 8780838930d..d25ceb97fd2 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -69,4 +69,11 @@ ArgPos.anyNamed(arg4, arg5, name=secondNamed) from testlib import Steps mySink(Steps.preserveTaint(getSource())) # FLOW -mySink(Steps.preserveTaint("safe", getSource())) # NO FLOW \ No newline at end of file +mySink(Steps.preserveTaint("safe", getSource())) # NO FLOW + +Steps.taintIntoCallback( + getSource(), + lambda x: mySink(x), # FLOW + lambda y: mySink(y), # FLOW + lambda z: mySink(z) # NO FLOW +) \ No newline at end of file diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index e2fd53b142a..21c246fac4a 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -11,7 +11,7 @@ class Steps extends ModelInput::SummaryModelCsv { row = [ "testlib;;Member[Steps].Member[preserveTaint];Argument[0];ReturnValue;taint", - // "testlib;;Member[Steps].Member[taintIntoCallback];Argument[0];Argument[1..2].Parameter[0];taint", + "testlib;;Member[Steps].Member[taintIntoCallback];Argument[0];Argument[1..2].Parameter[0];taint", // "testlib;;Member[Steps].Member[taintIntoCallbackThis];Argument[0];Argument[1..2].Parameter[this];taint", // "testlib;;Member[Steps].Member[preserveArgZeroAndTwo];Argument[0,2];ReturnValue;taint", // "testlib;;Member[Steps].Member[preserveAllButFirstArgument];Argument[1..];ReturnValue;taint", From 894252dfa723ebcf43814a26ffe321614e84e90e Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 28 Apr 2022 09:56:04 +0200 Subject: [PATCH 025/125] third test of taint steps --- .../ql/test/library-tests/frameworks/data/test.expected | 8 ++++++++ python/ql/test/library-tests/frameworks/data/test.py | 6 +++++- python/ql/test/library-tests/frameworks/data/test.ql | 6 +----- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index d336ac76b88..2b40ba5c4fb 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -7,6 +7,8 @@ taintFlow | test.py:71:28:71:38 | ControlFlowNode for getSource() | test.py:71:8:71:39 | ControlFlowNode for Attribute() | | test.py:75:5:75:15 | ControlFlowNode for getSource() | test.py:76:22:76:22 | ControlFlowNode for x | | test.py:75:5:75:15 | ControlFlowNode for getSource() | test.py:77:22:77:22 | ControlFlowNode for y | +| test.py:81:36:81:46 | ControlFlowNode for getSource() | test.py:81:8:81:47 | ControlFlowNode for Attribute() | +| test.py:83:50:83:60 | ControlFlowNode for getSource() | test.py:83:8:83:61 | ControlFlowNode for Attribute() | isSink | test.py:4:8:4:8 | ControlFlowNode for x | test-sink | | test.py:7:17:7:17 | ControlFlowNode for x | test-sink | @@ -36,6 +38,9 @@ isSink | test.py:76:22:76:22 | ControlFlowNode for x | test-sink | | test.py:77:22:77:22 | ControlFlowNode for y | test-sink | | test.py:78:22:78:22 | ControlFlowNode for z | test-sink | +| test.py:81:8:81:47 | ControlFlowNode for Attribute() | test-sink | +| test.py:82:8:82:54 | ControlFlowNode for Attribute() | test-sink | +| test.py:83:8:83:61 | ControlFlowNode for Attribute() | test-sink | isSource | test.py:3:5:3:15 | ControlFlowNode for getSource() | test-source | | test.py:9:8:9:14 | ControlFlowNode for alias() | test-source | @@ -65,6 +70,9 @@ isSource | test.py:71:28:71:38 | ControlFlowNode for getSource() | test-source | | test.py:72:36:72:46 | ControlFlowNode for getSource() | test-source | | test.py:75:5:75:15 | ControlFlowNode for getSource() | test-source | +| test.py:81:36:81:46 | ControlFlowNode for getSource() | test-source | +| test.py:82:43:82:53 | ControlFlowNode for getSource() | test-source | +| test.py:83:50:83:60 | ControlFlowNode for getSource() | test-source | syntaxErrors | Member[foo | | Member[foo] .Member[bar] | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index d25ceb97fd2..a7388f53832 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -76,4 +76,8 @@ Steps.taintIntoCallback( lambda x: mySink(x), # FLOW lambda y: mySink(y), # FLOW lambda z: mySink(z) # NO FLOW -) \ No newline at end of file +) + +mySink(Steps.preserveArgZeroAndTwo(getSource())) # FLOW +mySink(Steps.preserveArgZeroAndTwo("foo", getSource())) # NO FLOW +mySink(Steps.preserveArgZeroAndTwo("foo", "bar", getSource())) # FLOW \ No newline at end of file diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index 21c246fac4a..c9cb3033719 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -12,11 +12,8 @@ class Steps extends ModelInput::SummaryModelCsv { [ "testlib;;Member[Steps].Member[preserveTaint];Argument[0];ReturnValue;taint", "testlib;;Member[Steps].Member[taintIntoCallback];Argument[0];Argument[1..2].Parameter[0];taint", - // "testlib;;Member[Steps].Member[taintIntoCallbackThis];Argument[0];Argument[1..2].Parameter[this];taint", - // "testlib;;Member[Steps].Member[preserveArgZeroAndTwo];Argument[0,2];ReturnValue;taint", + "testlib;;Member[Steps].Member[preserveArgZeroAndTwo];Argument[0,2];ReturnValue;taint", // "testlib;;Member[Steps].Member[preserveAllButFirstArgument];Argument[1..];ReturnValue;taint", - // "testlib;;Member[Steps].Member[preserveAllIfCall].Call;Argument[0..];ReturnValue;taint", - // "testlib;;Member[Steps].Member[getSource].ReturnValue.Member[continue];Argument[this];ReturnValue;taint", ] } } @@ -56,7 +53,6 @@ class Sinks extends ModelInput::SinkModelCsv { } // TODO: Test taint steps (include that the base path may end with ".Call") -// TODO: // There are no API-graph edges for: ArrayElement, Element, MapKey, MapValue (remove from valid tokens list) class Sources extends ModelInput::SourceModelCsv { // package;type;path;kind override predicate row(string row) { From 9c3d45a16af06a70aa7f29fbf61285ed1afd1e05 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 28 Apr 2022 09:57:43 +0200 Subject: [PATCH 026/125] last test of taint steps --- .../ql/test/library-tests/frameworks/data/test.expected | 8 ++++++++ python/ql/test/library-tests/frameworks/data/test.py | 6 +++++- python/ql/test/library-tests/frameworks/data/test.ql | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 2b40ba5c4fb..721854dad37 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -9,6 +9,8 @@ taintFlow | test.py:75:5:75:15 | ControlFlowNode for getSource() | test.py:77:22:77:22 | ControlFlowNode for y | | test.py:81:36:81:46 | ControlFlowNode for getSource() | test.py:81:8:81:47 | ControlFlowNode for Attribute() | | test.py:83:50:83:60 | ControlFlowNode for getSource() | test.py:83:8:83:61 | ControlFlowNode for Attribute() | +| test.py:86:49:86:59 | ControlFlowNode for getSource() | test.py:86:8:86:60 | ControlFlowNode for Attribute() | +| test.py:87:56:87:66 | ControlFlowNode for getSource() | test.py:87:8:87:67 | ControlFlowNode for Attribute() | isSink | test.py:4:8:4:8 | ControlFlowNode for x | test-sink | | test.py:7:17:7:17 | ControlFlowNode for x | test-sink | @@ -41,6 +43,9 @@ isSink | test.py:81:8:81:47 | ControlFlowNode for Attribute() | test-sink | | test.py:82:8:82:54 | ControlFlowNode for Attribute() | test-sink | | test.py:83:8:83:61 | ControlFlowNode for Attribute() | test-sink | +| test.py:85:8:85:53 | ControlFlowNode for Attribute() | test-sink | +| test.py:86:8:86:60 | ControlFlowNode for Attribute() | test-sink | +| test.py:87:8:87:67 | ControlFlowNode for Attribute() | test-sink | isSource | test.py:3:5:3:15 | ControlFlowNode for getSource() | test-source | | test.py:9:8:9:14 | ControlFlowNode for alias() | test-source | @@ -73,6 +78,9 @@ isSource | test.py:81:36:81:46 | ControlFlowNode for getSource() | test-source | | test.py:82:43:82:53 | ControlFlowNode for getSource() | test-source | | test.py:83:50:83:60 | ControlFlowNode for getSource() | test-source | +| test.py:85:42:85:52 | ControlFlowNode for getSource() | test-source | +| test.py:86:49:86:59 | ControlFlowNode for getSource() | test-source | +| test.py:87:56:87:66 | ControlFlowNode for getSource() | test-source | syntaxErrors | Member[foo | | Member[foo] .Member[bar] | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index a7388f53832..db8e8c98e91 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -80,4 +80,8 @@ Steps.taintIntoCallback( mySink(Steps.preserveArgZeroAndTwo(getSource())) # FLOW mySink(Steps.preserveArgZeroAndTwo("foo", getSource())) # NO FLOW -mySink(Steps.preserveArgZeroAndTwo("foo", "bar", getSource())) # FLOW \ No newline at end of file +mySink(Steps.preserveArgZeroAndTwo("foo", "bar", getSource())) # FLOW + +mySink(Steps.preserveAllButFirstArgument(getSource())) # NO FLOW +mySink(Steps.preserveAllButFirstArgument("foo", getSource())) # FLOW +mySink(Steps.preserveAllButFirstArgument("foo", "bar", getSource())) # FLOW \ No newline at end of file diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index c9cb3033719..d5a74a93371 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -13,7 +13,7 @@ class Steps extends ModelInput::SummaryModelCsv { "testlib;;Member[Steps].Member[preserveTaint];Argument[0];ReturnValue;taint", "testlib;;Member[Steps].Member[taintIntoCallback];Argument[0];Argument[1..2].Parameter[0];taint", "testlib;;Member[Steps].Member[preserveArgZeroAndTwo];Argument[0,2];ReturnValue;taint", - // "testlib;;Member[Steps].Member[preserveAllButFirstArgument];Argument[1..];ReturnValue;taint", + "testlib;;Member[Steps].Member[preserveAllButFirstArgument];Argument[1..];ReturnValue;taint", ] } } From 6c67e51ec3fa9df0c4bb6577c194bd264a9dff55 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 28 Apr 2022 10:00:44 +0200 Subject: [PATCH 027/125] add test for the .Call token --- python/ql/test/library-tests/frameworks/data/test.ql | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index d5a74a93371..d694837b253 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -10,10 +10,10 @@ class Steps extends ModelInput::SummaryModelCsv { // package;type;path;input;output;kind row = [ - "testlib;;Member[Steps].Member[preserveTaint];Argument[0];ReturnValue;taint", + "testlib;;Member[Steps].Member[preserveTaint].Call;Argument[0];ReturnValue;taint", "testlib;;Member[Steps].Member[taintIntoCallback];Argument[0];Argument[1..2].Parameter[0];taint", "testlib;;Member[Steps].Member[preserveArgZeroAndTwo];Argument[0,2];ReturnValue;taint", - "testlib;;Member[Steps].Member[preserveAllButFirstArgument];Argument[1..];ReturnValue;taint", + "testlib;;Member[Steps].Member[preserveAllButFirstArgument].Call;Argument[1..];ReturnValue;taint", ] } } @@ -52,7 +52,6 @@ class Sinks extends ModelInput::SinkModelCsv { } } -// TODO: Test taint steps (include that the base path may end with ".Call") class Sources extends ModelInput::SourceModelCsv { // package;type;path;kind override predicate row(string row) { From c0eca0d09a76cef154e14dd75032e7b87f11a10a Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Sun, 1 May 2022 17:57:48 +0200 Subject: [PATCH 028/125] deprecate SqlConstruction --- .../2022-05-02-deprecate-sqlconstruction.md | 4 ++ python/ql/lib/semmle/python/Concepts.qll | 41 ++++--------------- .../lib/semmle/python/frameworks/Aiomysql.qll | 4 +- .../ql/lib/semmle/python/frameworks/Aiopg.qll | 4 +- .../lib/semmle/python/frameworks/Asyncpg.qll | 2 +- .../semmle/python/frameworks/SqlAlchemy.qll | 2 +- .../dataflow/SqlInjectionCustomizations.qll | 3 +- .../test/experimental/meta/ConceptsTest.qll | 18 -------- .../library-tests/frameworks/aiomysql/test.py | 10 ++--- .../library-tests/frameworks/aiopg/test.py | 10 ++--- .../library-tests/frameworks/asyncpg/test.py | 6 +-- .../flask_sqlalchemy/SqlExecution.py | 6 +-- .../frameworks/sqlalchemy/SqlExecution.py | 2 +- .../frameworks/sqlalchemy/new_tests.py | 6 +-- .../frameworks/sqlalchemy/taint_test.py | 16 ++++---- 15 files changed, 49 insertions(+), 85 deletions(-) create mode 100644 python/ql/lib/change-notes/2022-05-02-deprecate-sqlconstruction.md diff --git a/python/ql/lib/change-notes/2022-05-02-deprecate-sqlconstruction.md b/python/ql/lib/change-notes/2022-05-02-deprecate-sqlconstruction.md new file mode 100644 index 00000000000..4219fd12d9c --- /dev/null +++ b/python/ql/lib/change-notes/2022-05-02-deprecate-sqlconstruction.md @@ -0,0 +1,4 @@ +--- +category: deprecated +--- +The `SqlConstruction` class and module from `Concepts.qll` has been deprecated. Use `SqlExecution` from the same file instead. \ No newline at end of file diff --git a/python/ql/lib/semmle/python/Concepts.qll b/python/ql/lib/semmle/python/Concepts.qll index e734105cd2e..6154d24628b 100644 --- a/python/ql/lib/semmle/python/Concepts.qll +++ b/python/ql/lib/semmle/python/Concepts.qll @@ -308,36 +308,19 @@ module CodeExecution { } } -/** - * A data-flow node that constructs an SQL statement. - * - * Often, it is worthy of an alert if an SQL statement is constructed such that - * executing it would be a security risk. - * - * If it is important that the SQL statement is indeed executed, then use `SQLExecution`. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `SqlConstruction::Range` instead. - */ -class SqlConstruction extends DataFlow::Node instanceof SqlConstruction::Range { +/** DEPRECATED: Use `SqlExecution` instead. */ +deprecated class SqlConstruction extends DataFlow::Node instanceof SqlConstruction::Range { /** Gets the argument that specifies the SQL statements to be constructed. */ DataFlow::Node getSql() { result = super.getSql() } } -/** Provides a class for modeling new SQL execution APIs. */ -module SqlConstruction { - /** - * A data-flow node that constructs an SQL statement. - * - * Often, it is worthy of an alert if an SQL statement is constructed such that - * executing it would be a security risk. - * - * If it is important that the SQL statement is indeed executed, then use `SQLExecution`. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `SqlConstruction` instead. - */ - abstract class Range extends DataFlow::Node { +/** + * DEPRECATED: Use `SqlExecution` instead. + * Provides a class for modeling new SQL execution APIs. + */ +deprecated module SqlConstruction { + /** DEPRECATED: Use `SqlExecution::Range` instead. */ + abstract deprecated class Range extends DataFlow::Node { /** Gets the argument that specifies the SQL statements to be constructed. */ abstract DataFlow::Node getSql(); } @@ -346,9 +329,6 @@ module SqlConstruction { /** * A data-flow node that executes SQL statements. * - * If the context of interest is such that merely constructing an SQL statement - * would be valuabe to report, then consider using `SqlConstruction`. - * * Extend this class to refine existing API models. If you want to model new APIs, * extend `SqlExecution::Range` instead. */ @@ -362,9 +342,6 @@ module SqlExecution { /** * A data-flow node that executes SQL statements. * - * If the context of interest is such that merely constructing an SQL statement - * would be valuabe to report, then consider using `SqlConstruction`. - * * Extend this class to model new APIs. If you want to refine existing API models, * extend `SqlExecution` instead. */ diff --git a/python/ql/lib/semmle/python/frameworks/Aiomysql.qll b/python/ql/lib/semmle/python/frameworks/Aiomysql.qll index 112dc58d061..afff79783c2 100644 --- a/python/ql/lib/semmle/python/frameworks/Aiomysql.qll +++ b/python/ql/lib/semmle/python/frameworks/Aiomysql.qll @@ -50,7 +50,7 @@ private module Aiomysql { * A query. Calling `execute` on a `Cursor` constructs a query. * See https://aiomysql.readthedocs.io/en/stable/cursors.html#Cursor.execute */ - class CursorExecuteCall extends SqlConstruction::Range, API::CallNode { + class CursorExecuteCall extends SqlExecution::Range, API::CallNode { CursorExecuteCall() { this = cursor().getMember("execute").getACall() } override DataFlow::Node getSql() { result = this.getParameter(0, "operation").getARhs() } @@ -91,7 +91,7 @@ private module Aiomysql { * A query. Calling `execute` on a `SAConnection` constructs a query. * See https://aiomysql.readthedocs.io/en/stable/sa.html#aiomysql.sa.SAConnection.execute */ - class SAConnectionExecuteCall extends SqlConstruction::Range, API::CallNode { + class SAConnectionExecuteCall extends SqlExecution::Range, API::CallNode { SAConnectionExecuteCall() { this = saConnection().getMember("execute").getACall() } override DataFlow::Node getSql() { result = this.getParameter(0, "query").getARhs() } diff --git a/python/ql/lib/semmle/python/frameworks/Aiopg.qll b/python/ql/lib/semmle/python/frameworks/Aiopg.qll index 1a60c433150..67ba3c80db0 100644 --- a/python/ql/lib/semmle/python/frameworks/Aiopg.qll +++ b/python/ql/lib/semmle/python/frameworks/Aiopg.qll @@ -50,7 +50,7 @@ private module Aiopg { * A query. Calling `execute` on a `Cursor` constructs a query. * See https://aiopg.readthedocs.io/en/stable/core.html#aiopg.Cursor.execute */ - class CursorExecuteCall extends SqlConstruction::Range, API::CallNode { + class CursorExecuteCall extends SqlExecution::Range, API::CallNode { CursorExecuteCall() { this = cursor().getMember("execute").getACall() } override DataFlow::Node getSql() { result = this.getParameter(0, "operation").getARhs() } @@ -87,7 +87,7 @@ private module Aiopg { * A query. Calling `execute` on a `SAConnection` constructs a query. * See https://aiopg.readthedocs.io/en/stable/sa.html#aiopg.sa.SAConnection.execute */ - class SAConnectionExecuteCall extends SqlConstruction::Range, API::CallNode { + class SAConnectionExecuteCall extends SqlExecution::Range, API::CallNode { SAConnectionExecuteCall() { this = saConnection().getMember("execute").getACall() } override DataFlow::Node getSql() { result = this.getParameter(0, "query").getARhs() } diff --git a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll index 2b2b9ed2e1b..77678c92b51 100644 --- a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll +++ b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll @@ -56,7 +56,7 @@ private module Asyncpg { * The creation of the `Cursor` executes the query. */ module Cursor { - class CursorConstruction extends SqlConstruction::Range, API::CallNode { + class CursorConstruction extends SqlExecution::Range, API::CallNode { CursorConstruction() { this = ModelOutput::getATypeNode("asyncpg", "Connection").getMember("cursor").getACall() } diff --git a/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll b/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll index c1712e7e7eb..8a7e2f176e8 100644 --- a/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll +++ b/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll @@ -323,7 +323,7 @@ module SqlAlchemy { * A construction of a `sqlalchemy.sql.expression.TextClause`, which represents a * textual SQL string directly. */ - abstract class TextClauseConstruction extends SqlConstruction::Range, DataFlow::CallCfgNode { + abstract class TextClauseConstruction extends SqlExecution::Range, DataFlow::CallCfgNode { /** Gets the argument that specifies the SQL text. */ override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("text")] } } diff --git a/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll index 756a1f6b773..9e3e2749a2b 100644 --- a/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll @@ -43,9 +43,10 @@ module SqlInjection { class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } /** + * DEPRECATED: Use `SqlExecutionAsSink` instead. * A SQL statement of a SQL construction, considered as a flow sink. */ - class SqlConstructionAsSink extends Sink { + deprecated class SqlConstructionAsSink extends Sink { SqlConstructionAsSink() { this = any(SqlConstruction c).getSql() } } diff --git a/python/ql/test/experimental/meta/ConceptsTest.qll b/python/ql/test/experimental/meta/ConceptsTest.qll index 8f9435f633f..bdf71059680 100644 --- a/python/ql/test/experimental/meta/ConceptsTest.qll +++ b/python/ql/test/experimental/meta/ConceptsTest.qll @@ -128,24 +128,6 @@ class CodeExecutionTest extends InlineExpectationsTest { } } -class SqlConstructionTest extends InlineExpectationsTest { - SqlConstructionTest() { this = "SqlConstructionTest" } - - override string getARelevantTag() { result = "constructedSql" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { - exists(location.getFile().getRelativePath()) and - exists(SqlConstruction e, DataFlow::Node sql | - exists(location.getFile().getRelativePath()) and - sql = e.getSql() and - location = e.getLocation() and - element = sql.toString() and - value = prettyNodeForInlineTest(sql) and - tag = "constructedSql" - ) - } -} - class SqlExecutionTest extends InlineExpectationsTest { SqlExecutionTest() { this = "SqlExecutionTest" } diff --git a/python/ql/test/library-tests/frameworks/aiomysql/test.py b/python/ql/test/library-tests/frameworks/aiomysql/test.py index 782fa6cb7bf..5a06b46b9f1 100644 --- a/python/ql/test/library-tests/frameworks/aiomysql/test.py +++ b/python/ql/test/library-tests/frameworks/aiomysql/test.py @@ -5,24 +5,24 @@ async def test_cursor(): # Create connection directly conn = await aiomysql.connect() cur = await conn.cursor() - await cur.execute("sql") # $ getSql="sql" constructedSql="sql" + await cur.execute("sql") # $ getSql="sql" # Create connection via pool async with aiomysql.create_pool() as pool: # Create Cursor via Connection async with pool.acquire() as conn: async with conn.cursor() as cur: - await cur.execute("sql") # $ getSql="sql" constructedSql="sql" + await cur.execute("sql") # $ getSql="sql" # Create Cursor directly async with pool.cursor() as cur: - await cur.execute("sql") # $ getSql="sql" constructedSql="sql" + await cur.execute("sql") # $ getSql="sql" # variants using as few `async with` as possible pool = await aiomysql.create_pool() conn = await pool.acquire() cur = await conn.cursor() - await cur.execute("sql") # $ getSql="sql" constructedSql="sql" + await cur.execute("sql") # $ getSql="sql" # Test SQLAlchemy integration from aiomysql.sa import create_engine @@ -30,4 +30,4 @@ from aiomysql.sa import create_engine async def test_engine(): engine = await create_engine() conn = await engine.acquire() - await conn.execute("sql") # $ getSql="sql" constructedSql="sql" + await conn.execute("sql") # $ getSql="sql" diff --git a/python/ql/test/library-tests/frameworks/aiopg/test.py b/python/ql/test/library-tests/frameworks/aiopg/test.py index 63bf141f52d..1d085066688 100644 --- a/python/ql/test/library-tests/frameworks/aiopg/test.py +++ b/python/ql/test/library-tests/frameworks/aiopg/test.py @@ -5,24 +5,24 @@ async def test_cursor(): # Create connection directly conn = await aiopg.connect() cur = await conn.cursor() - await cur.execute("sql") # $ getSql="sql" constructedSql="sql" + await cur.execute("sql") # $ getSql="sql" # Create connection via pool async with aiopg.create_pool() as pool: # Create Cursor via Connection async with pool.acquire() as conn: async with conn.cursor() as cur: - await cur.execute("sql") # $ getSql="sql" constructedSql="sql" + await cur.execute("sql") # $ getSql="sql" # Create Cursor directly async with pool.cursor() as cur: - await cur.execute("sql") # $ getSql="sql" constructedSql="sql" + await cur.execute("sql") # $ getSql="sql" # variants using as few `async with` as possible pool = await aiopg.create_pool() conn = await pool.acquire() cur = await conn.cursor() - await cur.execute("sql") # $ getSql="sql" constructedSql="sql" + await cur.execute("sql") # $ getSql="sql" # Test SQLAlchemy integration from aiopg.sa import create_engine @@ -30,4 +30,4 @@ from aiopg.sa import create_engine async def test_engine(): engine = await create_engine() conn = await engine.acquire() - await conn.execute("sql") # $ getSql="sql" constructedSql="sql" + await conn.execute("sql") # $ getSql="sql" diff --git a/python/ql/test/library-tests/frameworks/asyncpg/test.py b/python/ql/test/library-tests/frameworks/asyncpg/test.py index 572fc454bed..7e2687e5766 100644 --- a/python/ql/test/library-tests/frameworks/asyncpg/test.py +++ b/python/ql/test/library-tests/frameworks/asyncpg/test.py @@ -43,20 +43,20 @@ async def test_cursor(): try: async with conn.transaction(): - cursor = await conn.cursor("sql") # $ getSql="sql" constructedSql="sql" + cursor = await conn.cursor("sql") # $ getSql="sql" await cursor.fetch() pstmt = await conn.prepare("psql") # $ getSql="psql" pcursor = await pstmt.cursor() # $ getSql="psql" await pcursor.fetch() - async for record in conn.cursor("sql"): # $ getSql="sql" constructedSql="sql" + async for record in conn.cursor("sql"): # $ getSql="sql" pass async for record in pstmt.cursor(): # $ getSql="psql" pass - cursor_factory = conn.cursor("sql") # $ constructedSql="sql" + cursor_factory = conn.cursor("sql") # $ getSql="sql" cursor = await cursor_factory # $ getSql="sql" pcursor_factory = pstmt.cursor() diff --git a/python/ql/test/library-tests/frameworks/flask_sqlalchemy/SqlExecution.py b/python/ql/test/library-tests/frameworks/flask_sqlalchemy/SqlExecution.py index 39cd49195d2..19a1349ffb6 100644 --- a/python/ql/test/library-tests/frameworks/flask_sqlalchemy/SqlExecution.py +++ b/python/ql/test/library-tests/frameworks/flask_sqlalchemy/SqlExecution.py @@ -12,7 +12,7 @@ db = SQLAlchemy(app) # - https://github.com/pallets/flask-sqlalchemy/blob/931ec00d1e27f51508e05706eef41cc4419a0b32/src/flask_sqlalchemy/__init__.py#L765 # - https://github.com/pallets/flask-sqlalchemy/blob/931ec00d1e27f51508e05706eef41cc4419a0b32/src/flask_sqlalchemy/__init__.py#L99-L109 -assert str(type(db.text("Foo"))) == "" # $ constructedSql="Foo" +assert str(type(db.text("Foo"))) == "" # $ getSql="Foo" # also has engine/session instantiated @@ -44,8 +44,8 @@ assert result.fetchall() == [("Foo",)] # text -t = db.text("foo") # $ constructedSql="foo" +t = db.text("foo") # $ getSql="foo" assert isinstance(t, sqlalchemy.sql.expression.TextClause) -t = db.text(text="foo") # $ constructedSql="foo" +t = db.text(text="foo") # $ getSql="foo" assert isinstance(t, sqlalchemy.sql.expression.TextClause) diff --git a/python/ql/test/library-tests/frameworks/sqlalchemy/SqlExecution.py b/python/ql/test/library-tests/frameworks/sqlalchemy/SqlExecution.py index fe75fc17d5c..7d9c478e27b 100644 --- a/python/ql/test/library-tests/frameworks/sqlalchemy/SqlExecution.py +++ b/python/ql/test/library-tests/frameworks/sqlalchemy/SqlExecution.py @@ -46,7 +46,7 @@ with engine.begin() as connection: connection.execute("some sql") # $ getSql="some sql" # Injection requiring the text() taint-step -t = text("some sql") # $ constructedSql="some sql" +t = text("some sql") # $ getSql="some sql" session.query(User).filter(t) session.query(User).group_by(User.id).having(t) session.query(User).group_by(t).first() diff --git a/python/ql/test/library-tests/frameworks/sqlalchemy/new_tests.py b/python/ql/test/library-tests/frameworks/sqlalchemy/new_tests.py index 6b11ee5d070..9cb13d15e05 100644 --- a/python/ql/test/library-tests/frameworks/sqlalchemy/new_tests.py +++ b/python/ql/test/library-tests/frameworks/sqlalchemy/new_tests.py @@ -6,7 +6,7 @@ import sqlalchemy.orm # either v1.4 or v2.0, such that we cover both. raw_sql = "select 'FOO'" -text_sql = sqlalchemy.text(raw_sql) # $ constructedSql=raw_sql +text_sql = sqlalchemy.text(raw_sql) # $ getSql=raw_sql Base = sqlalchemy.orm.declarative_base() @@ -176,7 +176,7 @@ assert session.query(For14).all()[0].id == 14 # and now we can do the actual querying -text_foo = sqlalchemy.text("'FOO'") # $ constructedSql="'FOO'" +text_foo = sqlalchemy.text("'FOO'") # $ getSql="'FOO'" # filter_by is only vulnerable to injection if sqlalchemy.text is used, which is evident # from the logs produced if this file is run @@ -305,7 +305,7 @@ with engine.connect() as conn: assert scalar_result == "FOO" # This is a contrived example - select = sqlalchemy.select(sqlalchemy.text("'BAR'")) # $ constructedSql="'BAR'" + select = sqlalchemy.select(sqlalchemy.text("'BAR'")) # $ getSql="'BAR'" result = conn.execute(select) # $ getSql=select assert result.fetchall() == [("BAR",)] diff --git a/python/ql/test/library-tests/frameworks/sqlalchemy/taint_test.py b/python/ql/test/library-tests/frameworks/sqlalchemy/taint_test.py index 884d30d3672..9d346d41979 100644 --- a/python/ql/test/library-tests/frameworks/sqlalchemy/taint_test.py +++ b/python/ql/test/library-tests/frameworks/sqlalchemy/taint_test.py @@ -8,14 +8,14 @@ def test_taint(): ensure_tainted(ts) # $ tainted - t1 = sqlalchemy.text(ts) # $ constructedSql=ts - t2 = sqlalchemy.text(text=ts) # $ constructedSql=ts - t3 = sqlalchemy.sql.text(ts) # $ constructedSql=ts - t4 = sqlalchemy.sql.text(text=ts) # $ constructedSql=ts - t5 = sqlalchemy.sql.expression.text(ts) # $ constructedSql=ts - t6 = sqlalchemy.sql.expression.text(text=ts) # $ constructedSql=ts - t7 = sqlalchemy.sql.expression.TextClause(ts) # $ constructedSql=ts - t8 = sqlalchemy.sql.expression.TextClause(text=ts) # $ constructedSql=ts + t1 = sqlalchemy.text(ts) # $ getSql=ts + t2 = sqlalchemy.text(text=ts) # $ getSql=ts + t3 = sqlalchemy.sql.text(ts) # $ getSql=ts + t4 = sqlalchemy.sql.text(text=ts) # $ getSql=ts + t5 = sqlalchemy.sql.expression.text(ts) # $ getSql=ts + t6 = sqlalchemy.sql.expression.text(text=ts) # $ getSql=ts + t7 = sqlalchemy.sql.expression.TextClause(ts) # $ getSql=ts + t8 = sqlalchemy.sql.expression.TextClause(text=ts) # $ getSql=ts # Since we flag user-input to a TextClause with its' own query, we don't want to # have a taint-step for it as that would lead to us also giving an alert for normal From 8ffc05c84b33594c61d2dac530ea58e719da8000 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 3 May 2022 21:21:57 +0200 Subject: [PATCH 029/125] count both named and positional arguments in the `WithArity` filter --- python/ql/lib/semmle/python/ApiGraphs.qll | 4 ++-- python/ql/test/library-tests/frameworks/data/test.expected | 2 ++ python/ql/test/library-tests/frameworks/data/test.py | 7 ++++++- python/ql/test/library-tests/frameworks/data/test.ql | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/python/ql/lib/semmle/python/ApiGraphs.qll b/python/ql/lib/semmle/python/ApiGraphs.qll index 77574ab44ad..e8fc14d90fd 100644 --- a/python/ql/lib/semmle/python/ApiGraphs.qll +++ b/python/ql/lib/semmle/python/ApiGraphs.qll @@ -349,8 +349,8 @@ module API { result.getAnImmediateUse() = this } - /** Gets the number of arguments of this call. */ - int getNumArgument() { result = count(this.getArg(_)) } + /** Gets the number of arguments of this call. Both positional and named arguments are counted. */ + int getNumArgument() { result = count([this.getArg(_), this.getArgByName(_)]) } } /** diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 721854dad37..8fb6efc0f95 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -46,6 +46,8 @@ isSink | test.py:85:8:85:53 | ControlFlowNode for Attribute() | test-sink | | test.py:86:8:86:60 | ControlFlowNode for Attribute() | test-sink | | test.py:87:8:87:67 | ControlFlowNode for Attribute() | test-sink | +| test.py:89:21:89:23 | ControlFlowNode for one | test-source | +| test.py:90:25:90:27 | ControlFlowNode for one | test-source | isSource | test.py:3:5:3:15 | ControlFlowNode for getSource() | test-source | | test.py:9:8:9:14 | ControlFlowNode for alias() | test-source | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index db8e8c98e91..96f09c51598 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -84,4 +84,9 @@ mySink(Steps.preserveArgZeroAndTwo("foo", "bar", getSource())) # FLOW mySink(Steps.preserveAllButFirstArgument(getSource())) # NO FLOW mySink(Steps.preserveAllButFirstArgument("foo", getSource())) # FLOW -mySink(Steps.preserveAllButFirstArgument("foo", "bar", getSource())) # FLOW \ No newline at end of file +mySink(Steps.preserveAllButFirstArgument("foo", "bar", getSource())) # FLOW + +CallFilter.arityOne(one) # match +CallFilter.arityOne(one=one) # match +CallFilter.arityOne(one, two=two) # NO match +CallFilter.arityOne(one=one, two=two) # NO match diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index d694837b253..2558631cec6 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -41,7 +41,7 @@ class Sinks extends ModelInput::SinkModelCsv { "testlib;;Member[Args].Member[lastarg].Argument[N-1];test-source", // "testlib;;Member[Args].Member[nonFist].Argument[1..];test-source", // // callsite filter. - "testlib;;Member[CallFilter].Member[arityOne].WithArity[1].Argument[0..];test-source", // + "testlib;;Member[CallFilter].Member[arityOne].WithArity[1].Argument[any];test-source", // "testlib;;Member[CallFilter].Member[twoOrMore].WithArity[2..].Argument[0..];test-source", // // testing non-positional arguments "testlib;;Member[ArgPos].Member[selfThing].Argument[self];test-source", // From ead978187d01d34c1f6b73f6f988705798fc0d6f Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 3 May 2022 21:25:48 +0200 Subject: [PATCH 030/125] adjust the source-type for remote-flow from MaD --- python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll b/python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll index 6c45c9a4036..1ced4d15ee9 100644 --- a/python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll +++ b/python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll @@ -24,7 +24,7 @@ private import semmle.python.dataflow.new.TaintTracking private class RemoteFlowSourceFromCsv extends RemoteFlowSource { RemoteFlowSourceFromCsv() { this = ModelOutput::getASourceNode("remote").getAnImmediateUse() } - override string getSourceType() { result = "Remote flow" } + override string getSourceType() { result = "Remote flow (from model)" } } /** From 1062aae21c5ffbcd4147f60a05142cce315ca8ca Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 3 May 2022 21:45:33 +0200 Subject: [PATCH 031/125] add test that the foo.bar package syntax works --- python/ql/test/library-tests/frameworks/data/test.expected | 2 ++ python/ql/test/library-tests/frameworks/data/test.py | 6 ++++++ python/ql/test/library-tests/frameworks/data/test.ql | 3 +++ 3 files changed, 11 insertions(+) diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 8fb6efc0f95..7fed228e022 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -48,6 +48,8 @@ isSink | test.py:87:8:87:67 | ControlFlowNode for Attribute() | test-sink | | test.py:89:21:89:23 | ControlFlowNode for one | test-source | | test.py:90:25:90:27 | ControlFlowNode for one | test-source | +| test.py:95:6:95:9 | ControlFlowNode for baz1 | test-source | +| test.py:98:6:98:9 | ControlFlowNode for baz2 | test-source | isSource | test.py:3:5:3:15 | ControlFlowNode for getSource() | test-source | | test.py:9:8:9:14 | ControlFlowNode for alias() | test-source | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index 96f09c51598..67f3ac2fbad 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -90,3 +90,9 @@ CallFilter.arityOne(one) # match CallFilter.arityOne(one=one) # match CallFilter.arityOne(one, two=two) # NO match CallFilter.arityOne(one=one, two=two) # NO match + +from foo1.bar import baz1 +baz1(baz1) # match + +from foo2.bar import baz2 +baz2(baz2) # match \ No newline at end of file diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index 2558631cec6..4b585309a6a 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -48,6 +48,9 @@ class Sinks extends ModelInput::SinkModelCsv { // any argument "testlib;;Member[ArgPos].Member[anyParam].Argument[any];test-source", // "testlib;;Member[ArgPos].Member[anyNamed].Argument[any-named];test-source", // + // testing package syntax + "foo1.bar;;Member[baz1].Argument[any];test-source", // + "foo2;;Member[bar].Member[baz2].Argument[any];test-source", // ] } } From 571fc3e73b8430bb7596f2da00dde6081094e1ab Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 3 May 2022 22:49:33 +0200 Subject: [PATCH 032/125] Revert "deprecate SqlConstruction" This reverts commit c0eca0d09a76cef154e14dd75032e7b87f11a10a. --- .../2022-05-02-deprecate-sqlconstruction.md | 4 -- python/ql/lib/semmle/python/Concepts.qll | 41 +++++++++++++++---- .../lib/semmle/python/frameworks/Aiomysql.qll | 4 +- .../ql/lib/semmle/python/frameworks/Aiopg.qll | 4 +- .../lib/semmle/python/frameworks/Asyncpg.qll | 2 +- .../semmle/python/frameworks/SqlAlchemy.qll | 2 +- .../dataflow/SqlInjectionCustomizations.qll | 3 +- .../test/experimental/meta/ConceptsTest.qll | 18 ++++++++ .../library-tests/frameworks/aiomysql/test.py | 10 ++--- .../library-tests/frameworks/aiopg/test.py | 10 ++--- .../library-tests/frameworks/asyncpg/test.py | 6 +-- .../flask_sqlalchemy/SqlExecution.py | 6 +-- .../frameworks/sqlalchemy/SqlExecution.py | 2 +- .../frameworks/sqlalchemy/new_tests.py | 6 +-- .../frameworks/sqlalchemy/taint_test.py | 16 ++++---- 15 files changed, 85 insertions(+), 49 deletions(-) delete mode 100644 python/ql/lib/change-notes/2022-05-02-deprecate-sqlconstruction.md diff --git a/python/ql/lib/change-notes/2022-05-02-deprecate-sqlconstruction.md b/python/ql/lib/change-notes/2022-05-02-deprecate-sqlconstruction.md deleted file mode 100644 index 4219fd12d9c..00000000000 --- a/python/ql/lib/change-notes/2022-05-02-deprecate-sqlconstruction.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: deprecated ---- -The `SqlConstruction` class and module from `Concepts.qll` has been deprecated. Use `SqlExecution` from the same file instead. \ No newline at end of file diff --git a/python/ql/lib/semmle/python/Concepts.qll b/python/ql/lib/semmle/python/Concepts.qll index 6154d24628b..e734105cd2e 100644 --- a/python/ql/lib/semmle/python/Concepts.qll +++ b/python/ql/lib/semmle/python/Concepts.qll @@ -308,19 +308,36 @@ module CodeExecution { } } -/** DEPRECATED: Use `SqlExecution` instead. */ -deprecated class SqlConstruction extends DataFlow::Node instanceof SqlConstruction::Range { +/** + * A data-flow node that constructs an SQL statement. + * + * Often, it is worthy of an alert if an SQL statement is constructed such that + * executing it would be a security risk. + * + * If it is important that the SQL statement is indeed executed, then use `SQLExecution`. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `SqlConstruction::Range` instead. + */ +class SqlConstruction extends DataFlow::Node instanceof SqlConstruction::Range { /** Gets the argument that specifies the SQL statements to be constructed. */ DataFlow::Node getSql() { result = super.getSql() } } -/** - * DEPRECATED: Use `SqlExecution` instead. - * Provides a class for modeling new SQL execution APIs. - */ -deprecated module SqlConstruction { - /** DEPRECATED: Use `SqlExecution::Range` instead. */ - abstract deprecated class Range extends DataFlow::Node { +/** Provides a class for modeling new SQL execution APIs. */ +module SqlConstruction { + /** + * A data-flow node that constructs an SQL statement. + * + * Often, it is worthy of an alert if an SQL statement is constructed such that + * executing it would be a security risk. + * + * If it is important that the SQL statement is indeed executed, then use `SQLExecution`. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `SqlConstruction` instead. + */ + abstract class Range extends DataFlow::Node { /** Gets the argument that specifies the SQL statements to be constructed. */ abstract DataFlow::Node getSql(); } @@ -329,6 +346,9 @@ deprecated module SqlConstruction { /** * A data-flow node that executes SQL statements. * + * If the context of interest is such that merely constructing an SQL statement + * would be valuabe to report, then consider using `SqlConstruction`. + * * Extend this class to refine existing API models. If you want to model new APIs, * extend `SqlExecution::Range` instead. */ @@ -342,6 +362,9 @@ module SqlExecution { /** * A data-flow node that executes SQL statements. * + * If the context of interest is such that merely constructing an SQL statement + * would be valuabe to report, then consider using `SqlConstruction`. + * * Extend this class to model new APIs. If you want to refine existing API models, * extend `SqlExecution` instead. */ diff --git a/python/ql/lib/semmle/python/frameworks/Aiomysql.qll b/python/ql/lib/semmle/python/frameworks/Aiomysql.qll index afff79783c2..112dc58d061 100644 --- a/python/ql/lib/semmle/python/frameworks/Aiomysql.qll +++ b/python/ql/lib/semmle/python/frameworks/Aiomysql.qll @@ -50,7 +50,7 @@ private module Aiomysql { * A query. Calling `execute` on a `Cursor` constructs a query. * See https://aiomysql.readthedocs.io/en/stable/cursors.html#Cursor.execute */ - class CursorExecuteCall extends SqlExecution::Range, API::CallNode { + class CursorExecuteCall extends SqlConstruction::Range, API::CallNode { CursorExecuteCall() { this = cursor().getMember("execute").getACall() } override DataFlow::Node getSql() { result = this.getParameter(0, "operation").getARhs() } @@ -91,7 +91,7 @@ private module Aiomysql { * A query. Calling `execute` on a `SAConnection` constructs a query. * See https://aiomysql.readthedocs.io/en/stable/sa.html#aiomysql.sa.SAConnection.execute */ - class SAConnectionExecuteCall extends SqlExecution::Range, API::CallNode { + class SAConnectionExecuteCall extends SqlConstruction::Range, API::CallNode { SAConnectionExecuteCall() { this = saConnection().getMember("execute").getACall() } override DataFlow::Node getSql() { result = this.getParameter(0, "query").getARhs() } diff --git a/python/ql/lib/semmle/python/frameworks/Aiopg.qll b/python/ql/lib/semmle/python/frameworks/Aiopg.qll index 67ba3c80db0..1a60c433150 100644 --- a/python/ql/lib/semmle/python/frameworks/Aiopg.qll +++ b/python/ql/lib/semmle/python/frameworks/Aiopg.qll @@ -50,7 +50,7 @@ private module Aiopg { * A query. Calling `execute` on a `Cursor` constructs a query. * See https://aiopg.readthedocs.io/en/stable/core.html#aiopg.Cursor.execute */ - class CursorExecuteCall extends SqlExecution::Range, API::CallNode { + class CursorExecuteCall extends SqlConstruction::Range, API::CallNode { CursorExecuteCall() { this = cursor().getMember("execute").getACall() } override DataFlow::Node getSql() { result = this.getParameter(0, "operation").getARhs() } @@ -87,7 +87,7 @@ private module Aiopg { * A query. Calling `execute` on a `SAConnection` constructs a query. * See https://aiopg.readthedocs.io/en/stable/sa.html#aiopg.sa.SAConnection.execute */ - class SAConnectionExecuteCall extends SqlExecution::Range, API::CallNode { + class SAConnectionExecuteCall extends SqlConstruction::Range, API::CallNode { SAConnectionExecuteCall() { this = saConnection().getMember("execute").getACall() } override DataFlow::Node getSql() { result = this.getParameter(0, "query").getARhs() } diff --git a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll index 77678c92b51..2b2b9ed2e1b 100644 --- a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll +++ b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll @@ -56,7 +56,7 @@ private module Asyncpg { * The creation of the `Cursor` executes the query. */ module Cursor { - class CursorConstruction extends SqlExecution::Range, API::CallNode { + class CursorConstruction extends SqlConstruction::Range, API::CallNode { CursorConstruction() { this = ModelOutput::getATypeNode("asyncpg", "Connection").getMember("cursor").getACall() } diff --git a/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll b/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll index 8a7e2f176e8..c1712e7e7eb 100644 --- a/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll +++ b/python/ql/lib/semmle/python/frameworks/SqlAlchemy.qll @@ -323,7 +323,7 @@ module SqlAlchemy { * A construction of a `sqlalchemy.sql.expression.TextClause`, which represents a * textual SQL string directly. */ - abstract class TextClauseConstruction extends SqlExecution::Range, DataFlow::CallCfgNode { + abstract class TextClauseConstruction extends SqlConstruction::Range, DataFlow::CallCfgNode { /** Gets the argument that specifies the SQL text. */ override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("text")] } } diff --git a/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll index 9e3e2749a2b..756a1f6b773 100644 --- a/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll @@ -43,10 +43,9 @@ module SqlInjection { class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } /** - * DEPRECATED: Use `SqlExecutionAsSink` instead. * A SQL statement of a SQL construction, considered as a flow sink. */ - deprecated class SqlConstructionAsSink extends Sink { + class SqlConstructionAsSink extends Sink { SqlConstructionAsSink() { this = any(SqlConstruction c).getSql() } } diff --git a/python/ql/test/experimental/meta/ConceptsTest.qll b/python/ql/test/experimental/meta/ConceptsTest.qll index bdf71059680..8f9435f633f 100644 --- a/python/ql/test/experimental/meta/ConceptsTest.qll +++ b/python/ql/test/experimental/meta/ConceptsTest.qll @@ -128,6 +128,24 @@ class CodeExecutionTest extends InlineExpectationsTest { } } +class SqlConstructionTest extends InlineExpectationsTest { + SqlConstructionTest() { this = "SqlConstructionTest" } + + override string getARelevantTag() { result = "constructedSql" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + exists(location.getFile().getRelativePath()) and + exists(SqlConstruction e, DataFlow::Node sql | + exists(location.getFile().getRelativePath()) and + sql = e.getSql() and + location = e.getLocation() and + element = sql.toString() and + value = prettyNodeForInlineTest(sql) and + tag = "constructedSql" + ) + } +} + class SqlExecutionTest extends InlineExpectationsTest { SqlExecutionTest() { this = "SqlExecutionTest" } diff --git a/python/ql/test/library-tests/frameworks/aiomysql/test.py b/python/ql/test/library-tests/frameworks/aiomysql/test.py index 5a06b46b9f1..782fa6cb7bf 100644 --- a/python/ql/test/library-tests/frameworks/aiomysql/test.py +++ b/python/ql/test/library-tests/frameworks/aiomysql/test.py @@ -5,24 +5,24 @@ async def test_cursor(): # Create connection directly conn = await aiomysql.connect() cur = await conn.cursor() - await cur.execute("sql") # $ getSql="sql" + await cur.execute("sql") # $ getSql="sql" constructedSql="sql" # Create connection via pool async with aiomysql.create_pool() as pool: # Create Cursor via Connection async with pool.acquire() as conn: async with conn.cursor() as cur: - await cur.execute("sql") # $ getSql="sql" + await cur.execute("sql") # $ getSql="sql" constructedSql="sql" # Create Cursor directly async with pool.cursor() as cur: - await cur.execute("sql") # $ getSql="sql" + await cur.execute("sql") # $ getSql="sql" constructedSql="sql" # variants using as few `async with` as possible pool = await aiomysql.create_pool() conn = await pool.acquire() cur = await conn.cursor() - await cur.execute("sql") # $ getSql="sql" + await cur.execute("sql") # $ getSql="sql" constructedSql="sql" # Test SQLAlchemy integration from aiomysql.sa import create_engine @@ -30,4 +30,4 @@ from aiomysql.sa import create_engine async def test_engine(): engine = await create_engine() conn = await engine.acquire() - await conn.execute("sql") # $ getSql="sql" + await conn.execute("sql") # $ getSql="sql" constructedSql="sql" diff --git a/python/ql/test/library-tests/frameworks/aiopg/test.py b/python/ql/test/library-tests/frameworks/aiopg/test.py index 1d085066688..63bf141f52d 100644 --- a/python/ql/test/library-tests/frameworks/aiopg/test.py +++ b/python/ql/test/library-tests/frameworks/aiopg/test.py @@ -5,24 +5,24 @@ async def test_cursor(): # Create connection directly conn = await aiopg.connect() cur = await conn.cursor() - await cur.execute("sql") # $ getSql="sql" + await cur.execute("sql") # $ getSql="sql" constructedSql="sql" # Create connection via pool async with aiopg.create_pool() as pool: # Create Cursor via Connection async with pool.acquire() as conn: async with conn.cursor() as cur: - await cur.execute("sql") # $ getSql="sql" + await cur.execute("sql") # $ getSql="sql" constructedSql="sql" # Create Cursor directly async with pool.cursor() as cur: - await cur.execute("sql") # $ getSql="sql" + await cur.execute("sql") # $ getSql="sql" constructedSql="sql" # variants using as few `async with` as possible pool = await aiopg.create_pool() conn = await pool.acquire() cur = await conn.cursor() - await cur.execute("sql") # $ getSql="sql" + await cur.execute("sql") # $ getSql="sql" constructedSql="sql" # Test SQLAlchemy integration from aiopg.sa import create_engine @@ -30,4 +30,4 @@ from aiopg.sa import create_engine async def test_engine(): engine = await create_engine() conn = await engine.acquire() - await conn.execute("sql") # $ getSql="sql" + await conn.execute("sql") # $ getSql="sql" constructedSql="sql" diff --git a/python/ql/test/library-tests/frameworks/asyncpg/test.py b/python/ql/test/library-tests/frameworks/asyncpg/test.py index 7e2687e5766..572fc454bed 100644 --- a/python/ql/test/library-tests/frameworks/asyncpg/test.py +++ b/python/ql/test/library-tests/frameworks/asyncpg/test.py @@ -43,20 +43,20 @@ async def test_cursor(): try: async with conn.transaction(): - cursor = await conn.cursor("sql") # $ getSql="sql" + cursor = await conn.cursor("sql") # $ getSql="sql" constructedSql="sql" await cursor.fetch() pstmt = await conn.prepare("psql") # $ getSql="psql" pcursor = await pstmt.cursor() # $ getSql="psql" await pcursor.fetch() - async for record in conn.cursor("sql"): # $ getSql="sql" + async for record in conn.cursor("sql"): # $ getSql="sql" constructedSql="sql" pass async for record in pstmt.cursor(): # $ getSql="psql" pass - cursor_factory = conn.cursor("sql") # $ getSql="sql" + cursor_factory = conn.cursor("sql") # $ constructedSql="sql" cursor = await cursor_factory # $ getSql="sql" pcursor_factory = pstmt.cursor() diff --git a/python/ql/test/library-tests/frameworks/flask_sqlalchemy/SqlExecution.py b/python/ql/test/library-tests/frameworks/flask_sqlalchemy/SqlExecution.py index 19a1349ffb6..39cd49195d2 100644 --- a/python/ql/test/library-tests/frameworks/flask_sqlalchemy/SqlExecution.py +++ b/python/ql/test/library-tests/frameworks/flask_sqlalchemy/SqlExecution.py @@ -12,7 +12,7 @@ db = SQLAlchemy(app) # - https://github.com/pallets/flask-sqlalchemy/blob/931ec00d1e27f51508e05706eef41cc4419a0b32/src/flask_sqlalchemy/__init__.py#L765 # - https://github.com/pallets/flask-sqlalchemy/blob/931ec00d1e27f51508e05706eef41cc4419a0b32/src/flask_sqlalchemy/__init__.py#L99-L109 -assert str(type(db.text("Foo"))) == "" # $ getSql="Foo" +assert str(type(db.text("Foo"))) == "" # $ constructedSql="Foo" # also has engine/session instantiated @@ -44,8 +44,8 @@ assert result.fetchall() == [("Foo",)] # text -t = db.text("foo") # $ getSql="foo" +t = db.text("foo") # $ constructedSql="foo" assert isinstance(t, sqlalchemy.sql.expression.TextClause) -t = db.text(text="foo") # $ getSql="foo" +t = db.text(text="foo") # $ constructedSql="foo" assert isinstance(t, sqlalchemy.sql.expression.TextClause) diff --git a/python/ql/test/library-tests/frameworks/sqlalchemy/SqlExecution.py b/python/ql/test/library-tests/frameworks/sqlalchemy/SqlExecution.py index 7d9c478e27b..fe75fc17d5c 100644 --- a/python/ql/test/library-tests/frameworks/sqlalchemy/SqlExecution.py +++ b/python/ql/test/library-tests/frameworks/sqlalchemy/SqlExecution.py @@ -46,7 +46,7 @@ with engine.begin() as connection: connection.execute("some sql") # $ getSql="some sql" # Injection requiring the text() taint-step -t = text("some sql") # $ getSql="some sql" +t = text("some sql") # $ constructedSql="some sql" session.query(User).filter(t) session.query(User).group_by(User.id).having(t) session.query(User).group_by(t).first() diff --git a/python/ql/test/library-tests/frameworks/sqlalchemy/new_tests.py b/python/ql/test/library-tests/frameworks/sqlalchemy/new_tests.py index 9cb13d15e05..6b11ee5d070 100644 --- a/python/ql/test/library-tests/frameworks/sqlalchemy/new_tests.py +++ b/python/ql/test/library-tests/frameworks/sqlalchemy/new_tests.py @@ -6,7 +6,7 @@ import sqlalchemy.orm # either v1.4 or v2.0, such that we cover both. raw_sql = "select 'FOO'" -text_sql = sqlalchemy.text(raw_sql) # $ getSql=raw_sql +text_sql = sqlalchemy.text(raw_sql) # $ constructedSql=raw_sql Base = sqlalchemy.orm.declarative_base() @@ -176,7 +176,7 @@ assert session.query(For14).all()[0].id == 14 # and now we can do the actual querying -text_foo = sqlalchemy.text("'FOO'") # $ getSql="'FOO'" +text_foo = sqlalchemy.text("'FOO'") # $ constructedSql="'FOO'" # filter_by is only vulnerable to injection if sqlalchemy.text is used, which is evident # from the logs produced if this file is run @@ -305,7 +305,7 @@ with engine.connect() as conn: assert scalar_result == "FOO" # This is a contrived example - select = sqlalchemy.select(sqlalchemy.text("'BAR'")) # $ getSql="'BAR'" + select = sqlalchemy.select(sqlalchemy.text("'BAR'")) # $ constructedSql="'BAR'" result = conn.execute(select) # $ getSql=select assert result.fetchall() == [("BAR",)] diff --git a/python/ql/test/library-tests/frameworks/sqlalchemy/taint_test.py b/python/ql/test/library-tests/frameworks/sqlalchemy/taint_test.py index 9d346d41979..884d30d3672 100644 --- a/python/ql/test/library-tests/frameworks/sqlalchemy/taint_test.py +++ b/python/ql/test/library-tests/frameworks/sqlalchemy/taint_test.py @@ -8,14 +8,14 @@ def test_taint(): ensure_tainted(ts) # $ tainted - t1 = sqlalchemy.text(ts) # $ getSql=ts - t2 = sqlalchemy.text(text=ts) # $ getSql=ts - t3 = sqlalchemy.sql.text(ts) # $ getSql=ts - t4 = sqlalchemy.sql.text(text=ts) # $ getSql=ts - t5 = sqlalchemy.sql.expression.text(ts) # $ getSql=ts - t6 = sqlalchemy.sql.expression.text(text=ts) # $ getSql=ts - t7 = sqlalchemy.sql.expression.TextClause(ts) # $ getSql=ts - t8 = sqlalchemy.sql.expression.TextClause(text=ts) # $ getSql=ts + t1 = sqlalchemy.text(ts) # $ constructedSql=ts + t2 = sqlalchemy.text(text=ts) # $ constructedSql=ts + t3 = sqlalchemy.sql.text(ts) # $ constructedSql=ts + t4 = sqlalchemy.sql.text(text=ts) # $ constructedSql=ts + t5 = sqlalchemy.sql.expression.text(ts) # $ constructedSql=ts + t6 = sqlalchemy.sql.expression.text(text=ts) # $ constructedSql=ts + t7 = sqlalchemy.sql.expression.TextClause(ts) # $ constructedSql=ts + t8 = sqlalchemy.sql.expression.TextClause(text=ts) # $ constructedSql=ts # Since we flag user-input to a TextClause with its' own query, we don't want to # have a taint-step for it as that would lead to us also giving an alert for normal From a812d4dd34e82ca168e9037da2d9f993a5963f58 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 3 May 2022 22:51:54 +0200 Subject: [PATCH 033/125] move the MaD sql-injection sink to SqlInjectionCustomizations.qll --- python/ql/lib/semmle/python/Concepts.qll | 8 -------- .../security/dataflow/SqlInjectionCustomizations.qll | 7 +++++++ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/python/ql/lib/semmle/python/Concepts.qll b/python/ql/lib/semmle/python/Concepts.qll index e734105cd2e..296b36417f3 100644 --- a/python/ql/lib/semmle/python/Concepts.qll +++ b/python/ql/lib/semmle/python/Concepts.qll @@ -372,14 +372,6 @@ module SqlExecution { /** Gets the argument that specifies the SQL statements to be executed. */ abstract DataFlow::Node getSql(); } - - private import semmle.python.frameworks.data.ModelsAsData - - private class DataAsSqlExecution extends Range { - DataAsSqlExecution() { this = ModelOutput::getASinkNode("sql-injection").getARhs() } - - override DataFlow::Node getSql() { result = this } - } } /** diff --git a/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll index 756a1f6b773..cf21a5c0e94 100644 --- a/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll @@ -60,4 +60,11 @@ module SqlInjection { * A comparison with a constant string, considered as a sanitizer-guard. */ class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { } + + private import semmle.python.frameworks.data.ModelsAsData + + /** A sink for sql-injection from model data. */ + private class DataAsSqlSink extends Sink { + DataAsSqlSink() { this = ModelOutput::getASinkNode("sql-injection").getARhs() } + } } From 4b9c9b0c8d71d8ed444ceff248443a38f32cbc37 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 4 May 2022 10:58:29 +0200 Subject: [PATCH 034/125] move most of asyncpg test into SqlInjection after moving MaD sql-injection sink --- .../library-tests/frameworks/asyncpg/test.py | 44 +++++----- .../SqlInjection.expected | 73 ++++++++++++++++ .../Security/CWE-089-SqlInjection/asyncpg.py | 87 +++++++++++++++++++ 3 files changed, 182 insertions(+), 22 deletions(-) create mode 100644 python/ql/test/query-tests/Security/CWE-089-SqlInjection/asyncpg.py diff --git a/python/ql/test/library-tests/frameworks/asyncpg/test.py b/python/ql/test/library-tests/frameworks/asyncpg/test.py index 572fc454bed..b9d6aee5b18 100644 --- a/python/ql/test/library-tests/frameworks/asyncpg/test.py +++ b/python/ql/test/library-tests/frameworks/asyncpg/test.py @@ -7,17 +7,17 @@ async def test_connection(): try: # The file-like object is passed in as a keyword-only argument. # See https://magicstack.github.io/asyncpg/current/api/index.html#asyncpg.connection.Connection.copy_from_query - await conn.copy_from_query("sql", output="filepath") # $ getSql="sql" getAPathArgument="filepath" - await conn.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getSql="sql" getAPathArgument="filepath" + await conn.copy_from_query("sql", output="filepath") # $ getAPathArgument="filepath" + await conn.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getAPathArgument="filepath" await conn.copy_from_table("table", output="filepath") # $ getAPathArgument="filepath" await conn.copy_to_table("table", source="filepath") # $ getAPathArgument="filepath" - await conn.execute("sql") # $ getSql="sql" - await conn.executemany("sql") # $ getSql="sql" - await conn.fetch("sql") # $ getSql="sql" - await conn.fetchrow("sql") # $ getSql="sql" - await conn.fetchval("sql") # $ getSql="sql" + await conn.execute("sql") # $ + await conn.executemany("sql") # $ + await conn.fetch("sql") # $ + await conn.fetchrow("sql") # $ + await conn.fetchval("sql") # $ finally: await conn.close() @@ -27,7 +27,7 @@ async def test_prepared_statement(): conn = await asyncpg.connect() try: - pstmt = await conn.prepare("psql") # $ getSql="psql" + pstmt = await conn.prepare("psql") # $ pstmt.executemany() pstmt.fetch() pstmt.fetchrow() @@ -43,10 +43,10 @@ async def test_cursor(): try: async with conn.transaction(): - cursor = await conn.cursor("sql") # $ getSql="sql" constructedSql="sql" + cursor = await conn.cursor("sql") # $ constructedSql="sql" getSql="sql" await cursor.fetch() - pstmt = await conn.prepare("psql") # $ getSql="psql" + pstmt = await conn.prepare("psql") # pcursor = await pstmt.cursor() # $ getSql="psql" await pcursor.fetch() @@ -69,23 +69,23 @@ async def test_connection_pool(): pool = await asyncpg.create_pool() try: - await pool.copy_from_query("sql", output="filepath") # $ getSql="sql" getAPathArgument="filepath" - await pool.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getSql="sql" getAPathArgument="filepath" + await pool.copy_from_query("sql", output="filepath") # $ getAPathArgument="filepath" + await pool.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getAPathArgument="filepath" await pool.copy_from_table("table", output="filepath") # $ getAPathArgument="filepath" await pool.copy_to_table("table", source="filepath") # $ getAPathArgument="filepath" - await pool.execute("sql") # $ getSql="sql" - await pool.executemany("sql") # $ getSql="sql" - await pool.fetch("sql") # $ getSql="sql" - await pool.fetchrow("sql") # $ getSql="sql" - await pool.fetchval("sql") # $ getSql="sql" + await pool.execute("sql") # $ + await pool.executemany("sql") # $ + await pool.fetch("sql") # $ + await pool.fetchrow("sql") # $ + await pool.fetchval("sql") # $ async with pool.acquire() as conn: - await conn.execute("sql") # $ getSql="sql" + await conn.execute("sql") # $ conn = await pool.acquire() try: - await conn.fetch("sql") # $ getSql="sql" + await conn.fetch("sql") # $ finally: await pool.release(conn) @@ -93,13 +93,13 @@ async def test_connection_pool(): await pool.close() async with asyncpg.create_pool() as pool: - await pool.execute("sql") # $ getSql="sql" + await pool.execute("sql") # $ async with pool.acquire() as conn: - await conn.execute("sql") # $ getSql="sql" + await conn.execute("sql") # $ conn = await pool.acquire() try: - await conn.fetch("sql") # $ getSql="sql" + await conn.fetch("sql") # $ finally: await pool.release(conn) diff --git a/python/ql/test/query-tests/Security/CWE-089-SqlInjection/SqlInjection.expected b/python/ql/test/query-tests/Security/CWE-089-SqlInjection/SqlInjection.expected index ed4c3e3d313..fe01f3b548d 100644 --- a/python/ql/test/query-tests/Security/CWE-089-SqlInjection/SqlInjection.expected +++ b/python/ql/test/query-tests/Security/CWE-089-SqlInjection/SqlInjection.expected @@ -1,4 +1,28 @@ edges +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:19:36:19:43 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:20:36:20:43 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:22:28:22:35 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:23:32:23:39 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:24:26:24:33 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:25:29:25:36 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:26:29:26:36 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:28:36:28:43 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:37:40:37:47 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:40:40:40:47 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:44:45:44:52 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:47:42:47:49 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:56:36:56:43 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:57:36:57:43 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:59:28:59:35 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:60:32:60:39 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:61:26:61:33 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:62:29:62:36 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:63:29:63:36 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:66:32:66:39 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:70:30:70:37 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:78:28:78:35 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:81:32:81:39 | ControlFlowNode for username | +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:85:30:85:37 | ControlFlowNode for username | | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:21:24:21:77 | ControlFlowNode for BinaryExpr | | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:24:38:24:95 | ControlFlowNode for BinaryExpr | | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:25:26:25:83 | ControlFlowNode for BinaryExpr | @@ -16,6 +40,31 @@ edges | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:50:18:50:25 | ControlFlowNode for username | | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:51:24:51:31 | ControlFlowNode for username | nodes +| asyncpg.py:13:21:13:28 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:19:36:19:43 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:20:36:20:43 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:22:28:22:35 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:23:32:23:39 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:24:26:24:33 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:25:29:25:36 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:26:29:26:36 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:28:36:28:43 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:37:40:37:47 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:40:40:40:47 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:44:45:44:52 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:47:42:47:49 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:56:36:56:43 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:57:36:57:43 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:59:28:59:35 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:60:32:60:39 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:61:26:61:33 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:62:29:62:36 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:63:29:63:36 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:66:32:66:39 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:70:30:70:37 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:78:28:78:35 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:81:32:81:39 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | +| asyncpg.py:85:30:85:37 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | | sql_injection.py:14:15:14:22 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | | sql_injection.py:21:24:21:77 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | sql_injection.py:24:38:24:95 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | @@ -36,6 +85,30 @@ nodes | sqlalchemy_textclause.py:51:24:51:31 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | subpaths #select +| asyncpg.py:19:36:19:43 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:19:36:19:43 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:20:36:20:43 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:20:36:20:43 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:22:28:22:35 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:22:28:22:35 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:23:32:23:39 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:23:32:23:39 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:24:26:24:33 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:24:26:24:33 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:25:29:25:36 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:25:29:25:36 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:26:29:26:36 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:26:29:26:36 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:28:36:28:43 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:28:36:28:43 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:37:40:37:47 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:37:40:37:47 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:40:40:40:47 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:40:40:40:47 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:44:45:44:52 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:44:45:44:52 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:47:42:47:49 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:47:42:47:49 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:56:36:56:43 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:56:36:56:43 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:57:36:57:43 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:57:36:57:43 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:59:28:59:35 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:59:28:59:35 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:60:32:60:39 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:60:32:60:39 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:61:26:61:33 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:61:26:61:33 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:62:29:62:36 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:62:29:62:36 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:63:29:63:36 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:63:29:63:36 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:66:32:66:39 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:66:32:66:39 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:70:30:70:37 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:70:30:70:37 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:78:28:78:35 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:78:28:78:35 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:81:32:81:39 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:81:32:81:39 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | +| asyncpg.py:85:30:85:37 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:85:30:85:37 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | | sql_injection.py:21:24:21:77 | ControlFlowNode for BinaryExpr | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:21:24:21:77 | ControlFlowNode for BinaryExpr | This SQL query depends on $@. | sql_injection.py:14:15:14:22 | ControlFlowNode for username | a user-provided value | | sql_injection.py:24:38:24:95 | ControlFlowNode for BinaryExpr | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:24:38:24:95 | ControlFlowNode for BinaryExpr | This SQL query depends on $@. | sql_injection.py:14:15:14:22 | ControlFlowNode for username | a user-provided value | | sql_injection.py:25:26:25:83 | ControlFlowNode for BinaryExpr | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:25:26:25:83 | ControlFlowNode for BinaryExpr | This SQL query depends on $@. | sql_injection.py:14:15:14:22 | ControlFlowNode for username | a user-provided value | diff --git a/python/ql/test/query-tests/Security/CWE-089-SqlInjection/asyncpg.py b/python/ql/test/query-tests/Security/CWE-089-SqlInjection/asyncpg.py new file mode 100644 index 00000000000..fd9dfed5877 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-089-SqlInjection/asyncpg.py @@ -0,0 +1,87 @@ +import asyncio +import asyncpg + +"""This is adapted from ql/python/ql/test/query-tests\Security\CWE-089 +we now prefer to setup routing by flask +""" + +from django.db import connection, models +from flask import Flask +app = Flask(__name__) + +@app.route("/users/") +async def show_user(username): + conn = await asyncpg.connect() + + try: + # The file-like object is passed in as a keyword-only argument. + # See https://magicstack.github.io/asyncpg/current/api/index.html#asyncpg.connection.Connection.copy_from_query + await conn.copy_from_query(username, output="filepath") # BAD + await conn.copy_from_query(username, "arg1", "arg2", output="filepath") # BAD + + await conn.execute(username) # BAD + await conn.executemany(username) # BAD + await conn.fetch(username) # BAD + await conn.fetchrow(username) # BAD + await conn.fetchval(username) # BAD + + pstmt = await conn.prepare(username) # BAD + pstmt.executemany() + pstmt.fetch() + pstmt.fetchrow() + pstmt.fetchval() + + # The sql statement is executed when the `CursorFactory` (obtained by e.g. `conn.cursor()`) is awaited. + # See https://magicstack.github.io/asyncpg/current/api/index.html#asyncpg.cursor.CursorFactory + async with conn.transaction(): + cursor = await conn.cursor(username) # BAD + await cursor.fetch() + + pstmt = await conn.prepare(username) # BAD + pcursor = await pstmt.cursor() + await pcursor.fetch() + + async for record in conn.cursor(username): # BAD + pass + + cursor_factory = conn.cursor(username) # BAD + cursor = await cursor_factory + + finally: + await conn.close() + + pool = await asyncpg.create_pool() + + try: + await pool.copy_from_query(username, output="filepath") # BAD + await pool.copy_from_query(username, "arg1", "arg2", output="filepath") # BAD + + await pool.execute(username) # BAD + await pool.executemany(username) # BAD + await pool.fetch(username) # BAD + await pool.fetchrow(username) # BAD + await pool.fetchval(username) # BAD + + async with pool.acquire() as conn: + await conn.execute(username) # BAD + + conn = await pool.acquire() + try: + await conn.fetch(username) # BAD + finally: + await pool.release(conn) + + finally: + await pool.close() + + async with asyncpg.create_pool() as pool: + await pool.execute(username) # BAD + + async with pool.acquire() as conn: + await conn.execute(username) # BAD + + conn = await pool.acquire() + try: + await conn.fetch(username) # BAD + finally: + await pool.release(conn) From 6ae5ef9f3b3bc4ce0d8576a31b91714e49a4f50c Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 5 May 2022 10:20:41 +0200 Subject: [PATCH 035/125] Revert "move most of asyncpg test into SqlInjection after moving MaD sql-injection sink" This reverts commit 4b9c9b0c8d71d8ed444ceff248443a38f32cbc37. --- .../library-tests/frameworks/asyncpg/test.py | 44 +++++----- .../SqlInjection.expected | 73 ---------------- .../Security/CWE-089-SqlInjection/asyncpg.py | 87 ------------------- 3 files changed, 22 insertions(+), 182 deletions(-) delete mode 100644 python/ql/test/query-tests/Security/CWE-089-SqlInjection/asyncpg.py diff --git a/python/ql/test/library-tests/frameworks/asyncpg/test.py b/python/ql/test/library-tests/frameworks/asyncpg/test.py index b9d6aee5b18..572fc454bed 100644 --- a/python/ql/test/library-tests/frameworks/asyncpg/test.py +++ b/python/ql/test/library-tests/frameworks/asyncpg/test.py @@ -7,17 +7,17 @@ async def test_connection(): try: # The file-like object is passed in as a keyword-only argument. # See https://magicstack.github.io/asyncpg/current/api/index.html#asyncpg.connection.Connection.copy_from_query - await conn.copy_from_query("sql", output="filepath") # $ getAPathArgument="filepath" - await conn.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getAPathArgument="filepath" + await conn.copy_from_query("sql", output="filepath") # $ getSql="sql" getAPathArgument="filepath" + await conn.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getSql="sql" getAPathArgument="filepath" await conn.copy_from_table("table", output="filepath") # $ getAPathArgument="filepath" await conn.copy_to_table("table", source="filepath") # $ getAPathArgument="filepath" - await conn.execute("sql") # $ - await conn.executemany("sql") # $ - await conn.fetch("sql") # $ - await conn.fetchrow("sql") # $ - await conn.fetchval("sql") # $ + await conn.execute("sql") # $ getSql="sql" + await conn.executemany("sql") # $ getSql="sql" + await conn.fetch("sql") # $ getSql="sql" + await conn.fetchrow("sql") # $ getSql="sql" + await conn.fetchval("sql") # $ getSql="sql" finally: await conn.close() @@ -27,7 +27,7 @@ async def test_prepared_statement(): conn = await asyncpg.connect() try: - pstmt = await conn.prepare("psql") # $ + pstmt = await conn.prepare("psql") # $ getSql="psql" pstmt.executemany() pstmt.fetch() pstmt.fetchrow() @@ -43,10 +43,10 @@ async def test_cursor(): try: async with conn.transaction(): - cursor = await conn.cursor("sql") # $ constructedSql="sql" getSql="sql" + cursor = await conn.cursor("sql") # $ getSql="sql" constructedSql="sql" await cursor.fetch() - pstmt = await conn.prepare("psql") # + pstmt = await conn.prepare("psql") # $ getSql="psql" pcursor = await pstmt.cursor() # $ getSql="psql" await pcursor.fetch() @@ -69,23 +69,23 @@ async def test_connection_pool(): pool = await asyncpg.create_pool() try: - await pool.copy_from_query("sql", output="filepath") # $ getAPathArgument="filepath" - await pool.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getAPathArgument="filepath" + await pool.copy_from_query("sql", output="filepath") # $ getSql="sql" getAPathArgument="filepath" + await pool.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getSql="sql" getAPathArgument="filepath" await pool.copy_from_table("table", output="filepath") # $ getAPathArgument="filepath" await pool.copy_to_table("table", source="filepath") # $ getAPathArgument="filepath" - await pool.execute("sql") # $ - await pool.executemany("sql") # $ - await pool.fetch("sql") # $ - await pool.fetchrow("sql") # $ - await pool.fetchval("sql") # $ + await pool.execute("sql") # $ getSql="sql" + await pool.executemany("sql") # $ getSql="sql" + await pool.fetch("sql") # $ getSql="sql" + await pool.fetchrow("sql") # $ getSql="sql" + await pool.fetchval("sql") # $ getSql="sql" async with pool.acquire() as conn: - await conn.execute("sql") # $ + await conn.execute("sql") # $ getSql="sql" conn = await pool.acquire() try: - await conn.fetch("sql") # $ + await conn.fetch("sql") # $ getSql="sql" finally: await pool.release(conn) @@ -93,13 +93,13 @@ async def test_connection_pool(): await pool.close() async with asyncpg.create_pool() as pool: - await pool.execute("sql") # $ + await pool.execute("sql") # $ getSql="sql" async with pool.acquire() as conn: - await conn.execute("sql") # $ + await conn.execute("sql") # $ getSql="sql" conn = await pool.acquire() try: - await conn.fetch("sql") # $ + await conn.fetch("sql") # $ getSql="sql" finally: await pool.release(conn) diff --git a/python/ql/test/query-tests/Security/CWE-089-SqlInjection/SqlInjection.expected b/python/ql/test/query-tests/Security/CWE-089-SqlInjection/SqlInjection.expected index fe01f3b548d..ed4c3e3d313 100644 --- a/python/ql/test/query-tests/Security/CWE-089-SqlInjection/SqlInjection.expected +++ b/python/ql/test/query-tests/Security/CWE-089-SqlInjection/SqlInjection.expected @@ -1,28 +1,4 @@ edges -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:19:36:19:43 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:20:36:20:43 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:22:28:22:35 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:23:32:23:39 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:24:26:24:33 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:25:29:25:36 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:26:29:26:36 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:28:36:28:43 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:37:40:37:47 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:40:40:40:47 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:44:45:44:52 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:47:42:47:49 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:56:36:56:43 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:57:36:57:43 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:59:28:59:35 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:60:32:60:39 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:61:26:61:33 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:62:29:62:36 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:63:29:63:36 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:66:32:66:39 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:70:30:70:37 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:78:28:78:35 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:81:32:81:39 | ControlFlowNode for username | -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:85:30:85:37 | ControlFlowNode for username | | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:21:24:21:77 | ControlFlowNode for BinaryExpr | | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:24:38:24:95 | ControlFlowNode for BinaryExpr | | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:25:26:25:83 | ControlFlowNode for BinaryExpr | @@ -40,31 +16,6 @@ edges | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:50:18:50:25 | ControlFlowNode for username | | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:51:24:51:31 | ControlFlowNode for username | nodes -| asyncpg.py:13:21:13:28 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:19:36:19:43 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:20:36:20:43 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:22:28:22:35 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:23:32:23:39 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:24:26:24:33 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:25:29:25:36 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:26:29:26:36 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:28:36:28:43 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:37:40:37:47 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:40:40:40:47 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:44:45:44:52 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:47:42:47:49 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:56:36:56:43 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:57:36:57:43 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:59:28:59:35 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:60:32:60:39 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:61:26:61:33 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:62:29:62:36 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:63:29:63:36 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:66:32:66:39 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:70:30:70:37 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:78:28:78:35 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:81:32:81:39 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | -| asyncpg.py:85:30:85:37 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | | sql_injection.py:14:15:14:22 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | | sql_injection.py:21:24:21:77 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | sql_injection.py:24:38:24:95 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | @@ -85,30 +36,6 @@ nodes | sqlalchemy_textclause.py:51:24:51:31 | ControlFlowNode for username | semmle.label | ControlFlowNode for username | subpaths #select -| asyncpg.py:19:36:19:43 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:19:36:19:43 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:20:36:20:43 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:20:36:20:43 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:22:28:22:35 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:22:28:22:35 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:23:32:23:39 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:23:32:23:39 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:24:26:24:33 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:24:26:24:33 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:25:29:25:36 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:25:29:25:36 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:26:29:26:36 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:26:29:26:36 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:28:36:28:43 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:28:36:28:43 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:37:40:37:47 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:37:40:37:47 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:40:40:40:47 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:40:40:40:47 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:44:45:44:52 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:44:45:44:52 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:47:42:47:49 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:47:42:47:49 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:56:36:56:43 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:56:36:56:43 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:57:36:57:43 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:57:36:57:43 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:59:28:59:35 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:59:28:59:35 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:60:32:60:39 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:60:32:60:39 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:61:26:61:33 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:61:26:61:33 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:62:29:62:36 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:62:29:62:36 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:63:29:63:36 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:63:29:63:36 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:66:32:66:39 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:66:32:66:39 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:70:30:70:37 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:70:30:70:37 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:78:28:78:35 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:78:28:78:35 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:81:32:81:39 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:81:32:81:39 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | -| asyncpg.py:85:30:85:37 | ControlFlowNode for username | asyncpg.py:13:21:13:28 | ControlFlowNode for username | asyncpg.py:85:30:85:37 | ControlFlowNode for username | This SQL query depends on $@. | asyncpg.py:13:21:13:28 | ControlFlowNode for username | a user-provided value | | sql_injection.py:21:24:21:77 | ControlFlowNode for BinaryExpr | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:21:24:21:77 | ControlFlowNode for BinaryExpr | This SQL query depends on $@. | sql_injection.py:14:15:14:22 | ControlFlowNode for username | a user-provided value | | sql_injection.py:24:38:24:95 | ControlFlowNode for BinaryExpr | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:24:38:24:95 | ControlFlowNode for BinaryExpr | This SQL query depends on $@. | sql_injection.py:14:15:14:22 | ControlFlowNode for username | a user-provided value | | sql_injection.py:25:26:25:83 | ControlFlowNode for BinaryExpr | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:25:26:25:83 | ControlFlowNode for BinaryExpr | This SQL query depends on $@. | sql_injection.py:14:15:14:22 | ControlFlowNode for username | a user-provided value | diff --git a/python/ql/test/query-tests/Security/CWE-089-SqlInjection/asyncpg.py b/python/ql/test/query-tests/Security/CWE-089-SqlInjection/asyncpg.py deleted file mode 100644 index fd9dfed5877..00000000000 --- a/python/ql/test/query-tests/Security/CWE-089-SqlInjection/asyncpg.py +++ /dev/null @@ -1,87 +0,0 @@ -import asyncio -import asyncpg - -"""This is adapted from ql/python/ql/test/query-tests\Security\CWE-089 -we now prefer to setup routing by flask -""" - -from django.db import connection, models -from flask import Flask -app = Flask(__name__) - -@app.route("/users/") -async def show_user(username): - conn = await asyncpg.connect() - - try: - # The file-like object is passed in as a keyword-only argument. - # See https://magicstack.github.io/asyncpg/current/api/index.html#asyncpg.connection.Connection.copy_from_query - await conn.copy_from_query(username, output="filepath") # BAD - await conn.copy_from_query(username, "arg1", "arg2", output="filepath") # BAD - - await conn.execute(username) # BAD - await conn.executemany(username) # BAD - await conn.fetch(username) # BAD - await conn.fetchrow(username) # BAD - await conn.fetchval(username) # BAD - - pstmt = await conn.prepare(username) # BAD - pstmt.executemany() - pstmt.fetch() - pstmt.fetchrow() - pstmt.fetchval() - - # The sql statement is executed when the `CursorFactory` (obtained by e.g. `conn.cursor()`) is awaited. - # See https://magicstack.github.io/asyncpg/current/api/index.html#asyncpg.cursor.CursorFactory - async with conn.transaction(): - cursor = await conn.cursor(username) # BAD - await cursor.fetch() - - pstmt = await conn.prepare(username) # BAD - pcursor = await pstmt.cursor() - await pcursor.fetch() - - async for record in conn.cursor(username): # BAD - pass - - cursor_factory = conn.cursor(username) # BAD - cursor = await cursor_factory - - finally: - await conn.close() - - pool = await asyncpg.create_pool() - - try: - await pool.copy_from_query(username, output="filepath") # BAD - await pool.copy_from_query(username, "arg1", "arg2", output="filepath") # BAD - - await pool.execute(username) # BAD - await pool.executemany(username) # BAD - await pool.fetch(username) # BAD - await pool.fetchrow(username) # BAD - await pool.fetchval(username) # BAD - - async with pool.acquire() as conn: - await conn.execute(username) # BAD - - conn = await pool.acquire() - try: - await conn.fetch(username) # BAD - finally: - await pool.release(conn) - - finally: - await pool.close() - - async with asyncpg.create_pool() as pool: - await pool.execute(username) # BAD - - async with pool.acquire() as conn: - await conn.execute(username) # BAD - - conn = await pool.acquire() - try: - await conn.fetch(username) # BAD - finally: - await pool.release(conn) From 0a589bed4e20cf4c0e7ee7f152c32245447f8a67 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 5 May 2022 13:11:43 +0200 Subject: [PATCH 036/125] Python: Add inline test of MaD sinks This enables us to keep the framework modeling tests under `/frameworks` folder I had hoped to use `mad-sink[]` syntax, but that was not allowed :( Maybe it oculd be allowed in the future, but for now I'll stick with the more ugly solution of `mad-sink__` --- python/ql/test/experimental/meta/MaDTest.qll | 50 +++++++++++++++++ .../frameworks/asyncpg/MaDTest.expected | 0 .../frameworks/asyncpg/MaDTest.ql | 2 + .../library-tests/frameworks/asyncpg/test.py | 54 +++++++++---------- 4 files changed, 79 insertions(+), 27 deletions(-) create mode 100644 python/ql/test/experimental/meta/MaDTest.qll create mode 100644 python/ql/test/library-tests/frameworks/asyncpg/MaDTest.expected create mode 100644 python/ql/test/library-tests/frameworks/asyncpg/MaDTest.ql diff --git a/python/ql/test/experimental/meta/MaDTest.qll b/python/ql/test/experimental/meta/MaDTest.qll new file mode 100644 index 00000000000..f3f701b6494 --- /dev/null +++ b/python/ql/test/experimental/meta/MaDTest.qll @@ -0,0 +1,50 @@ +import python +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.dataflow.new.internal.PrintNode +private import semmle.python.frameworks.data.ModelsAsData +// need to import Frameworks to get the actual modeling imported +private import semmle.python.Frameworks +// this improt needs to be public to get the query predicates propagated to the actual test files +import TestUtilities.InlineExpectationsTest + +class MadSinkTest extends InlineExpectationsTest { + MadSinkTest() { this = "MadSinkTest" } + + override string getARelevantTag() { + exists(string kind | exists(ModelOutput::getASinkNode(kind)) | + result = "mad-sink__" + kind + ) + } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + exists(location.getFile().getRelativePath()) and + exists(DataFlow::Node sink, string kind | + sink = ModelOutput::getASinkNode(kind).getARhs() and + location = sink.getLocation() and + element = sink.toString() and + value = prettyNodeForInlineTest(sink) and + tag = "mad-sink__" + kind + ) + } +} + +class MadSourceTest extends InlineExpectationsTest { + MadSourceTest() { this = "MadSourceTest" } + + override string getARelevantTag() { + exists(string kind | exists(ModelOutput::getASourceNode(kind)) | + result = "mad-source__" + kind + ) + } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + exists(location.getFile().getRelativePath()) and + exists(DataFlow::Node source, string kind | + source = ModelOutput::getASourceNode(kind).getAnImmediateUse() and + location = source.getLocation() and + element = source.toString() and + value = prettyNodeForInlineTest(source) and + tag = "mad-source__" + kind + ) + } +} diff --git a/python/ql/test/library-tests/frameworks/asyncpg/MaDTest.expected b/python/ql/test/library-tests/frameworks/asyncpg/MaDTest.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/frameworks/asyncpg/MaDTest.ql b/python/ql/test/library-tests/frameworks/asyncpg/MaDTest.ql new file mode 100644 index 00000000000..fef4356ab35 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/asyncpg/MaDTest.ql @@ -0,0 +1,2 @@ +import python +import experimental.meta.MaDTest diff --git a/python/ql/test/library-tests/frameworks/asyncpg/test.py b/python/ql/test/library-tests/frameworks/asyncpg/test.py index 572fc454bed..d739ee88770 100644 --- a/python/ql/test/library-tests/frameworks/asyncpg/test.py +++ b/python/ql/test/library-tests/frameworks/asyncpg/test.py @@ -7,17 +7,17 @@ async def test_connection(): try: # The file-like object is passed in as a keyword-only argument. # See https://magicstack.github.io/asyncpg/current/api/index.html#asyncpg.connection.Connection.copy_from_query - await conn.copy_from_query("sql", output="filepath") # $ getSql="sql" getAPathArgument="filepath" - await conn.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getSql="sql" getAPathArgument="filepath" + await conn.copy_from_query("sql", output="filepath") # $ mad-sink__sql-injection="sql" mad-sink__file-access="filepath" getAPathArgument="filepath" + await conn.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ mad-sink__sql-injection="sql" mad-sink__file-access="filepath" getAPathArgument="filepath" - await conn.copy_from_table("table", output="filepath") # $ getAPathArgument="filepath" - await conn.copy_to_table("table", source="filepath") # $ getAPathArgument="filepath" + await conn.copy_from_table("table", output="filepath") # $ mad-sink__file-access="filepath" getAPathArgument="filepath" + await conn.copy_to_table("table", source="filepath") # $ mad-sink__file-access="filepath" getAPathArgument="filepath" - await conn.execute("sql") # $ getSql="sql" - await conn.executemany("sql") # $ getSql="sql" - await conn.fetch("sql") # $ getSql="sql" - await conn.fetchrow("sql") # $ getSql="sql" - await conn.fetchval("sql") # $ getSql="sql" + await conn.execute("sql") # $ mad-sink__sql-injection="sql" + await conn.executemany("sql") # $ mad-sink__sql-injection="sql" + await conn.fetch("sql") # $ mad-sink__sql-injection="sql" + await conn.fetchrow("sql") # $ mad-sink__sql-injection="sql" + await conn.fetchval("sql") # $ mad-sink__sql-injection="sql" finally: await conn.close() @@ -27,9 +27,9 @@ async def test_prepared_statement(): conn = await asyncpg.connect() try: - pstmt = await conn.prepare("psql") # $ getSql="psql" - pstmt.executemany() - pstmt.fetch() + pstmt = await conn.prepare("psql") # $ mad-sink__sql-injection="psql" + pstmt.executemany() + pstmt.fetch() pstmt.fetchrow() pstmt.fetchval() @@ -46,7 +46,7 @@ async def test_cursor(): cursor = await conn.cursor("sql") # $ getSql="sql" constructedSql="sql" await cursor.fetch() - pstmt = await conn.prepare("psql") # $ getSql="psql" + pstmt = await conn.prepare("psql") # $ mad-sink__sql-injection="psql" pcursor = await pstmt.cursor() # $ getSql="psql" await pcursor.fetch() @@ -69,23 +69,23 @@ async def test_connection_pool(): pool = await asyncpg.create_pool() try: - await pool.copy_from_query("sql", output="filepath") # $ getSql="sql" getAPathArgument="filepath" - await pool.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ getSql="sql" getAPathArgument="filepath" - await pool.copy_from_table("table", output="filepath") # $ getAPathArgument="filepath" - await pool.copy_to_table("table", source="filepath") # $ getAPathArgument="filepath" + await pool.copy_from_query("sql", output="filepath") # $ mad-sink__sql-injection="sql" mad-sink__file-access="filepath" getAPathArgument="filepath" + await pool.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ mad-sink__sql-injection="sql" mad-sink__file-access="filepath" getAPathArgument="filepath" + await pool.copy_from_table("table", output="filepath") # $ mad-sink__file-access="filepath" getAPathArgument="filepath" + await pool.copy_to_table("table", source="filepath") # $ mad-sink__file-access="filepath" getAPathArgument="filepath" - await pool.execute("sql") # $ getSql="sql" - await pool.executemany("sql") # $ getSql="sql" - await pool.fetch("sql") # $ getSql="sql" - await pool.fetchrow("sql") # $ getSql="sql" - await pool.fetchval("sql") # $ getSql="sql" + await pool.execute("sql") # $ mad-sink__sql-injection="sql" + await pool.executemany("sql") # $ mad-sink__sql-injection="sql" + await pool.fetch("sql") # $ mad-sink__sql-injection="sql" + await pool.fetchrow("sql") # $ mad-sink__sql-injection="sql" + await pool.fetchval("sql") # $ mad-sink__sql-injection="sql" async with pool.acquire() as conn: - await conn.execute("sql") # $ getSql="sql" + await conn.execute("sql") # $ mad-sink__sql-injection="sql" conn = await pool.acquire() try: - await conn.fetch("sql") # $ getSql="sql" + await conn.fetch("sql") # $ mad-sink__sql-injection="sql" finally: await pool.release(conn) @@ -93,13 +93,13 @@ async def test_connection_pool(): await pool.close() async with asyncpg.create_pool() as pool: - await pool.execute("sql") # $ getSql="sql" + await pool.execute("sql") # $ mad-sink__sql-injection="sql" async with pool.acquire() as conn: - await conn.execute("sql") # $ getSql="sql" + await conn.execute("sql") # $ mad-sink__sql-injection="sql" conn = await pool.acquire() try: - await conn.fetch("sql") # $ getSql="sql" + await conn.fetch("sql") # $ mad-sink__sql-injection="sql" finally: await pool.release(conn) From dfe99b0b51b5f716cb4723f3eea54d08962d6f28 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 5 May 2022 14:14:44 +0200 Subject: [PATCH 037/125] Python: Apply suggestions from code review Co-authored-by: Erik Krogh Kristensen --- python/ql/test/experimental/meta/MaDTest.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/test/experimental/meta/MaDTest.qll b/python/ql/test/experimental/meta/MaDTest.qll index f3f701b6494..03358fe5414 100644 --- a/python/ql/test/experimental/meta/MaDTest.qll +++ b/python/ql/test/experimental/meta/MaDTest.qll @@ -4,7 +4,7 @@ private import semmle.python.dataflow.new.internal.PrintNode private import semmle.python.frameworks.data.ModelsAsData // need to import Frameworks to get the actual modeling imported private import semmle.python.Frameworks -// this improt needs to be public to get the query predicates propagated to the actual test files +// this import needs to be public to get the query predicates propagated to the actual test files import TestUtilities.InlineExpectationsTest class MadSinkTest extends InlineExpectationsTest { From efe306733e680771066a825b9977271b7db36781 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 5 May 2022 16:51:39 +0200 Subject: [PATCH 038/125] move path-injection MaD to PathInjectionCustomizations.qll --- python/ql/lib/semmle/python/Concepts.qll | 8 -------- .../ql/lib/semmle/python/frameworks/Asyncpg.qll | 4 ++-- .../dataflow/PathInjectionCustomizations.qll | 6 ++++++ .../library-tests/frameworks/asyncpg/test.py | 16 ++++++++-------- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/python/ql/lib/semmle/python/Concepts.qll b/python/ql/lib/semmle/python/Concepts.qll index 296b36417f3..dc461861ace 100644 --- a/python/ql/lib/semmle/python/Concepts.qll +++ b/python/ql/lib/semmle/python/Concepts.qll @@ -62,14 +62,6 @@ module FileSystemAccess { /** Gets an argument to this file system access that is interpreted as a path. */ abstract DataFlow::Node getAPathArgument(); } - - private import semmle.python.frameworks.data.ModelsAsData - - private class DataAsFileAccess extends Range { - DataAsFileAccess() { this = ModelOutput::getASinkNode("file-access").getARhs() } - - override DataFlow::Node getAPathArgument() { result = this } - } } /** diff --git a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll index 2b2b9ed2e1b..48d3e1534ae 100644 --- a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll +++ b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll @@ -38,8 +38,8 @@ private module Asyncpg { "asyncpg;~Connection;Member[copy_from_query,execute,fetch,fetchrow,fetchval].Argument[0,query:];sql-injection", "asyncpg;~Connection;Member[executemany].Argument[0,command:];sql-injection", // A model of `Connection` and `ConnectionPool`, which provide some methods that access the file system. - "asyncpg;~Connection;Member[copy_from_query,copy_from_table].Argument[output:];file-access", - "asyncpg;~Connection;Member[copy_to_table].Argument[source:];file-access", + "asyncpg;~Connection;Member[copy_from_query,copy_from_table].Argument[output:];path-injection", + "asyncpg;~Connection;Member[copy_to_table].Argument[source:];path-injection", // the `PreparedStatement` class in `asyncpg`. "asyncpg;Connection;Member[prepare].Argument[0,query:];sql-injection", ] diff --git a/python/ql/lib/semmle/python/security/dataflow/PathInjectionCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/PathInjectionCustomizations.qll index 410eee50b29..5a033664823 100644 --- a/python/ql/lib/semmle/python/security/dataflow/PathInjectionCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/PathInjectionCustomizations.qll @@ -59,6 +59,12 @@ module PathInjection { FileSystemAccessAsSink() { this = any(FileSystemAccess e).getAPathArgument() } } + private import semmle.python.frameworks.data.ModelsAsData + + private class DataAsFileSink extends Sink { + DataAsFileSink() { this = ModelOutput::getASinkNode("path-injection").getARhs() } + } + /** * A comparison with a constant string, considered as a sanitizer-guard. */ diff --git a/python/ql/test/library-tests/frameworks/asyncpg/test.py b/python/ql/test/library-tests/frameworks/asyncpg/test.py index d739ee88770..e2e5e1c5826 100644 --- a/python/ql/test/library-tests/frameworks/asyncpg/test.py +++ b/python/ql/test/library-tests/frameworks/asyncpg/test.py @@ -7,11 +7,11 @@ async def test_connection(): try: # The file-like object is passed in as a keyword-only argument. # See https://magicstack.github.io/asyncpg/current/api/index.html#asyncpg.connection.Connection.copy_from_query - await conn.copy_from_query("sql", output="filepath") # $ mad-sink__sql-injection="sql" mad-sink__file-access="filepath" getAPathArgument="filepath" - await conn.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ mad-sink__sql-injection="sql" mad-sink__file-access="filepath" getAPathArgument="filepath" + await conn.copy_from_query("sql", output="filepath") # $ mad-sink__sql-injection="sql" mad-sink__path-injection="filepath" + await conn.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ mad-sink__sql-injection="sql" mad-sink__path-injection="filepath" - await conn.copy_from_table("table", output="filepath") # $ mad-sink__file-access="filepath" getAPathArgument="filepath" - await conn.copy_to_table("table", source="filepath") # $ mad-sink__file-access="filepath" getAPathArgument="filepath" + await conn.copy_from_table("table", output="filepath") # $ mad-sink__path-injection="filepath" + await conn.copy_to_table("table", source="filepath") # $ mad-sink__path-injection="filepath" await conn.execute("sql") # $ mad-sink__sql-injection="sql" await conn.executemany("sql") # $ mad-sink__sql-injection="sql" @@ -69,10 +69,10 @@ async def test_connection_pool(): pool = await asyncpg.create_pool() try: - await pool.copy_from_query("sql", output="filepath") # $ mad-sink__sql-injection="sql" mad-sink__file-access="filepath" getAPathArgument="filepath" - await pool.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ mad-sink__sql-injection="sql" mad-sink__file-access="filepath" getAPathArgument="filepath" - await pool.copy_from_table("table", output="filepath") # $ mad-sink__file-access="filepath" getAPathArgument="filepath" - await pool.copy_to_table("table", source="filepath") # $ mad-sink__file-access="filepath" getAPathArgument="filepath" + await pool.copy_from_query("sql", output="filepath") # $ mad-sink__sql-injection="sql" mad-sink__path-injection="filepath" + await pool.copy_from_query("sql", "arg1", "arg2", output="filepath") # $ mad-sink__sql-injection="sql" mad-sink__path-injection="filepath" + await pool.copy_from_table("table", output="filepath") # $ mad-sink__path-injection="filepath" + await pool.copy_to_table("table", source="filepath") # $ mad-sink__path-injection="filepath" await pool.execute("sql") # $ mad-sink__sql-injection="sql" await pool.executemany("sql") # $ mad-sink__sql-injection="sql" From fc1ab06c1c2ac6d03a7058ce0bb3ab739255e570 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 9 May 2022 12:39:38 +0200 Subject: [PATCH 039/125] autoformat --- python/ql/test/experimental/meta/MaDTest.qll | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/python/ql/test/experimental/meta/MaDTest.qll b/python/ql/test/experimental/meta/MaDTest.qll index 03358fe5414..345fc973284 100644 --- a/python/ql/test/experimental/meta/MaDTest.qll +++ b/python/ql/test/experimental/meta/MaDTest.qll @@ -11,9 +11,7 @@ class MadSinkTest extends InlineExpectationsTest { MadSinkTest() { this = "MadSinkTest" } override string getARelevantTag() { - exists(string kind | exists(ModelOutput::getASinkNode(kind)) | - result = "mad-sink__" + kind - ) + exists(string kind | exists(ModelOutput::getASinkNode(kind)) | result = "mad-sink__" + kind) } override predicate hasActualResult(Location location, string element, string tag, string value) { @@ -32,9 +30,7 @@ class MadSourceTest extends InlineExpectationsTest { MadSourceTest() { this = "MadSourceTest" } override string getARelevantTag() { - exists(string kind | exists(ModelOutput::getASourceNode(kind)) | - result = "mad-source__" + kind - ) + exists(string kind | exists(ModelOutput::getASourceNode(kind)) | result = "mad-source__" + kind) } override predicate hasActualResult(Location location, string element, string tag, string value) { From dea55962893c52dbd9964ed07e0cfdc23745ff01 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 12 May 2022 14:45:29 +0200 Subject: [PATCH 040/125] update MaD test to reflect that dotted module names don't work --- python/ql/test/library-tests/frameworks/data/test.expected | 1 - python/ql/test/library-tests/frameworks/data/test.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 7fed228e022..591b5286b63 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -48,7 +48,6 @@ isSink | test.py:87:8:87:67 | ControlFlowNode for Attribute() | test-sink | | test.py:89:21:89:23 | ControlFlowNode for one | test-source | | test.py:90:25:90:27 | ControlFlowNode for one | test-source | -| test.py:95:6:95:9 | ControlFlowNode for baz1 | test-source | | test.py:98:6:98:9 | ControlFlowNode for baz2 | test-source | isSource | test.py:3:5:3:15 | ControlFlowNode for getSource() | test-source | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index 67f3ac2fbad..4064d1a808f 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -92,7 +92,7 @@ CallFilter.arityOne(one, two=two) # NO match CallFilter.arityOne(one=one, two=two) # NO match from foo1.bar import baz1 -baz1(baz1) # match +baz1(baz1) # no match, and that's the point. from foo2.bar import baz2 baz2(baz2) # match \ No newline at end of file From fb077bec661fda388ef7f0c91ee4cd1eb35f9e3a Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 12 May 2022 14:46:54 +0200 Subject: [PATCH 041/125] sync AccessPathSyntax changes --- .../python/frameworks/data/internal/AccessPathSyntax.qll | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/AccessPathSyntax.qll b/python/ql/lib/semmle/python/frameworks/data/internal/AccessPathSyntax.qll index 3d40e04b815..076e12f2671 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/AccessPathSyntax.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/AccessPathSyntax.qll @@ -167,9 +167,16 @@ class AccessPathToken extends string { /** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */ string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() } + /** Gets the `n`th argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */ + pragma[nomagic] + string getArgument(string name, int n) { name = this.getName() and result = this.getArgument(n) } + /** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */ string getAnArgument() { result = this.getArgument(_) } + /** Gets an argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */ + string getAnArgument(string name) { result = this.getArgument(name, _) } + /** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */ int getNumArgument() { result = count(int n | exists(this.getArgument(n))) } } From 1f8e7c39f4e874cc3d37d0dcb9f5d9aefe6c327b Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 17 May 2022 10:32:31 +0200 Subject: [PATCH 042/125] fix typo in comment Co-authored-by: Rasmus Wriedt Larsen --- .../semmle/python/frameworks/data/internal/ApiGraphModels.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll index 4681c2b91a5..0a81edf221d 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll @@ -299,7 +299,7 @@ private class AccessPathRange extends AccessPath::Range { bindingset[token] API::Node getSuccessorFromNode(API::Node node, AccessPathToken token) { // API graphs use the same label for arguments and parameters. An edge originating from a - // use-node represents be an argument, and an edge originating from a def-node represents a parameter. + // use-node represents an argument, and an edge originating from a def-node represents a parameter. // We just map both to the same thing. token.getName() = ["Argument", "Parameter"] and result = node.getParameter(AccessPath::parseIntUnbounded(token.getAnArgument())) From 55ffdb4aa144efc2f4cfd36f6680444fbfb6bdee Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 17 May 2022 10:34:17 +0200 Subject: [PATCH 043/125] make most imports in ApiGraphModelsSpecific.qll private --- .../frameworks/data/internal/ApiGraphModelsSpecific.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll index 206a6ff1e52..ae1c59a30d0 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -20,9 +20,9 @@ */ private import python as PY -import semmle.python.dataflow.new.DataFlow +private import semmle.python.dataflow.new.DataFlow private import ApiGraphModels -import semmle.python.ApiGraphs +import semmle.python.ApiGraphs::API as API class Unit = PY::Unit; From aef592fec843914e8e1a4a3e4006d90dde461ad7 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 17 May 2022 11:10:31 +0200 Subject: [PATCH 044/125] make a more realistic test for self-parameter --- python/ql/test/library-tests/frameworks/data/test.expected | 2 +- python/ql/test/library-tests/frameworks/data/test.py | 4 ++-- python/ql/test/library-tests/frameworks/data/test.ql | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 591b5286b63..6c23dc276de 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -30,7 +30,7 @@ isSink | test.py:33:22:33:24 | ControlFlowNode for one | test-source | | test.py:33:27:33:29 | ControlFlowNode for two | test-source | | test.py:33:32:33:36 | ControlFlowNode for three | test-source | -| test.py:57:7:57:12 | ControlFlowNode for ArgPos | test-source | +| test.py:57:27:57:33 | ControlFlowNode for arg_pos | test-source | | test.py:66:17:66:20 | ControlFlowNode for arg1 | test-source | | test.py:66:23:66:26 | ControlFlowNode for arg2 | test-source | | test.py:66:34:66:43 | ControlFlowNode for namedThing | test-source | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index 4064d1a808f..a037ddba9a2 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -54,7 +54,7 @@ val = inst.foo() from testlib import ArgPos -val = ArgPos.selfThing(arg, named=2) +arg_pos = ArgPos(); val = arg_pos.self_thing(arg, named=2); class SubClass (ArgPos.MyClass): def foo(self, arg, named=2, otherName=3): @@ -95,4 +95,4 @@ from foo1.bar import baz1 baz1(baz1) # no match, and that's the point. from foo2.bar import baz2 -baz2(baz2) # match \ No newline at end of file +baz2(baz2) # match diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index 4b585309a6a..cc056058514 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -44,7 +44,7 @@ class Sinks extends ModelInput::SinkModelCsv { "testlib;;Member[CallFilter].Member[arityOne].WithArity[1].Argument[any];test-source", // "testlib;;Member[CallFilter].Member[twoOrMore].WithArity[2..].Argument[0..];test-source", // // testing non-positional arguments - "testlib;;Member[ArgPos].Member[selfThing].Argument[self];test-source", // + "testlib;;Member[ArgPos].Instance.Member[self_thing].Argument[self];test-source", // // any argument "testlib;;Member[ArgPos].Member[anyParam].Argument[any];test-source", // "testlib;;Member[ArgPos].Member[anyNamed].Argument[any-named];test-source", // From ce21d7e5a8bcdbaf3b6adbbf4d072d7e90041c98 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 17 May 2022 11:13:59 +0200 Subject: [PATCH 045/125] use `test-sink` for sinks in the MaD test --- .../frameworks/data/test.expected | 40 +++++++++---------- .../library-tests/frameworks/data/test.ql | 22 +++++----- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 6c23dc276de..1a95bc0d359 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -18,23 +18,23 @@ isSink | test.py:10:8:10:22 | ControlFlowNode for Attribute() | test-sink | | test.py:11:8:11:30 | ControlFlowNode for Attribute() | test-sink | | test.py:12:8:12:34 | ControlFlowNode for Attribute() | test-sink | -| test.py:16:11:16:13 | ControlFlowNode for one | test-source | -| test.py:17:19:17:21 | ControlFlowNode for two | test-source | -| test.py:17:24:17:28 | ControlFlowNode for three | test-source | -| test.py:17:31:17:34 | ControlFlowNode for four | test-source | -| test.py:18:37:18:40 | ControlFlowNode for five | test-source | -| test.py:19:21:19:26 | ControlFlowNode for second | test-source | -| test.py:30:21:30:23 | ControlFlowNode for one | test-source | -| test.py:32:22:32:24 | ControlFlowNode for one | test-source | -| test.py:32:27:32:29 | ControlFlowNode for two | test-source | -| test.py:33:22:33:24 | ControlFlowNode for one | test-source | -| test.py:33:27:33:29 | ControlFlowNode for two | test-source | -| test.py:33:32:33:36 | ControlFlowNode for three | test-source | -| test.py:57:27:57:33 | ControlFlowNode for arg_pos | test-source | -| test.py:66:17:66:20 | ControlFlowNode for arg1 | test-source | -| test.py:66:23:66:26 | ControlFlowNode for arg2 | test-source | -| test.py:66:34:66:43 | ControlFlowNode for namedThing | test-source | -| test.py:67:34:67:44 | ControlFlowNode for secondNamed | test-source | +| test.py:16:11:16:13 | ControlFlowNode for one | test-sink | +| test.py:17:19:17:21 | ControlFlowNode for two | test-sink | +| test.py:17:24:17:28 | ControlFlowNode for three | test-sink | +| test.py:17:31:17:34 | ControlFlowNode for four | test-sink | +| test.py:18:37:18:40 | ControlFlowNode for five | test-sink | +| test.py:19:21:19:26 | ControlFlowNode for second | test-sink | +| test.py:30:21:30:23 | ControlFlowNode for one | test-sink | +| test.py:32:22:32:24 | ControlFlowNode for one | test-sink | +| test.py:32:27:32:29 | ControlFlowNode for two | test-sink | +| test.py:33:22:33:24 | ControlFlowNode for one | test-sink | +| test.py:33:27:33:29 | ControlFlowNode for two | test-sink | +| test.py:33:32:33:36 | ControlFlowNode for three | test-sink | +| test.py:57:27:57:33 | ControlFlowNode for arg_pos | test-sink | +| test.py:66:17:66:20 | ControlFlowNode for arg1 | test-sink | +| test.py:66:23:66:26 | ControlFlowNode for arg2 | test-sink | +| test.py:66:34:66:43 | ControlFlowNode for namedThing | test-sink | +| test.py:67:34:67:44 | ControlFlowNode for secondNamed | test-sink | | test.py:71:8:71:39 | ControlFlowNode for Attribute() | test-sink | | test.py:72:8:72:47 | ControlFlowNode for Attribute() | test-sink | | test.py:76:22:76:22 | ControlFlowNode for x | test-sink | @@ -46,9 +46,9 @@ isSink | test.py:85:8:85:53 | ControlFlowNode for Attribute() | test-sink | | test.py:86:8:86:60 | ControlFlowNode for Attribute() | test-sink | | test.py:87:8:87:67 | ControlFlowNode for Attribute() | test-sink | -| test.py:89:21:89:23 | ControlFlowNode for one | test-source | -| test.py:90:25:90:27 | ControlFlowNode for one | test-source | -| test.py:98:6:98:9 | ControlFlowNode for baz2 | test-source | +| test.py:89:21:89:23 | ControlFlowNode for one | test-sink | +| test.py:90:25:90:27 | ControlFlowNode for one | test-sink | +| test.py:98:6:98:9 | ControlFlowNode for baz2 | test-sink | isSource | test.py:3:5:3:15 | ControlFlowNode for getSource() | test-source | | test.py:9:8:9:14 | ControlFlowNode for alias() | test-source | diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index cc056058514..bff07833a2a 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -36,21 +36,21 @@ class Sinks extends ModelInput::SinkModelCsv { [ "testlib;;Member[mySink].Argument[0,sinkName:];test-sink", // testing argument syntax - "testlib;;Member[Args].Member[arg0].Argument[0];test-source", // - "testlib;;Member[Args].Member[arg1to3].Argument[1..3];test-source", // - "testlib;;Member[Args].Member[lastarg].Argument[N-1];test-source", // - "testlib;;Member[Args].Member[nonFist].Argument[1..];test-source", // + "testlib;;Member[Args].Member[arg0].Argument[0];test-sink", // + "testlib;;Member[Args].Member[arg1to3].Argument[1..3];test-sink", // + "testlib;;Member[Args].Member[lastarg].Argument[N-1];test-sink", // + "testlib;;Member[Args].Member[nonFist].Argument[1..];test-sink", // // callsite filter. - "testlib;;Member[CallFilter].Member[arityOne].WithArity[1].Argument[any];test-source", // - "testlib;;Member[CallFilter].Member[twoOrMore].WithArity[2..].Argument[0..];test-source", // + "testlib;;Member[CallFilter].Member[arityOne].WithArity[1].Argument[any];test-sink", // + "testlib;;Member[CallFilter].Member[twoOrMore].WithArity[2..].Argument[0..];test-sink", // // testing non-positional arguments - "testlib;;Member[ArgPos].Instance.Member[self_thing].Argument[self];test-source", // + "testlib;;Member[ArgPos].Instance.Member[self_thing].Argument[self];test-sink", // // any argument - "testlib;;Member[ArgPos].Member[anyParam].Argument[any];test-source", // - "testlib;;Member[ArgPos].Member[anyNamed].Argument[any-named];test-source", // + "testlib;;Member[ArgPos].Member[anyParam].Argument[any];test-sink", // + "testlib;;Member[ArgPos].Member[anyNamed].Argument[any-named];test-sink", // // testing package syntax - "foo1.bar;;Member[baz1].Argument[any];test-source", // - "foo2;;Member[bar].Member[baz2].Argument[any];test-source", // + "foo1.bar;;Member[baz1].Argument[any];test-sink", // + "foo2;;Member[bar].Member[baz2].Argument[any];test-sink", // ] } } From f273ccf73bc5d9b97972ff4b18d21a07e2e62cc8 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 17 May 2022 11:17:15 +0200 Subject: [PATCH 046/125] add explicit test of what Parameter[0] matches --- python/ql/test/library-tests/frameworks/data/test.expected | 1 + python/ql/test/library-tests/frameworks/data/test.py | 4 ++++ python/ql/test/library-tests/frameworks/data/test.ql | 1 + 3 files changed, 6 insertions(+) diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 1a95bc0d359..0b962e9be63 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -84,6 +84,7 @@ isSource | test.py:85:42:85:52 | ControlFlowNode for getSource() | test-source | | test.py:86:49:86:59 | ControlFlowNode for getSource() | test-source | | test.py:87:56:87:66 | ControlFlowNode for getSource() | test-source | +| test.py:101:29:101:31 | ControlFlowNode for arg | test-source | syntaxErrors | Member[foo | | Member[foo] .Member[bar] | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index a037ddba9a2..7b060946e57 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -96,3 +96,7 @@ baz1(baz1) # no match, and that's the point. from foo2.bar import baz2 baz2(baz2) # match + +class OtherSubClass (ArgPos.MyClass): + def otherSelfTest(self, arg, named=2, otherName=3): # test that Parameter[0] hits `arg`. + pass diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index bff07833a2a..e42fd024e98 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -76,6 +76,7 @@ class Sources extends ModelInput::SourceModelCsv { "testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[foo].Parameter[self];test-source", // "testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[foo].Parameter[named:];test-source", // "testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[secondAndAfter].Parameter[1..];test-source", // + "testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[otherSelfTest].Parameter[0];test-source", // ] } } From 2868eb61eab77b9fa038a91a75c68f375d2516d0 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 17 May 2022 12:08:53 +0200 Subject: [PATCH 047/125] add test for Parameter[any] and Parameter[any-named] --- python/ql/test/library-tests/frameworks/data/test.expected | 6 ++++++ python/ql/test/library-tests/frameworks/data/test.py | 6 ++++++ python/ql/test/library-tests/frameworks/data/test.ql | 2 ++ 3 files changed, 14 insertions(+) diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 0b962e9be63..16ec37811a0 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -85,6 +85,12 @@ isSource | test.py:86:49:86:59 | ControlFlowNode for getSource() | test-source | | test.py:87:56:87:66 | ControlFlowNode for getSource() | test-source | | test.py:101:29:101:31 | ControlFlowNode for arg | test-source | +| test.py:104:18:104:21 | ControlFlowNode for self | test-source | +| test.py:104:24:104:29 | ControlFlowNode for param1 | test-source | +| test.py:104:32:104:37 | ControlFlowNode for param2 | test-source | +| test.py:107:18:107:21 | ControlFlowNode for self | test-source | +| test.py:107:24:107:28 | ControlFlowNode for name1 | test-source | +| test.py:107:31:107:35 | ControlFlowNode for name2 | test-source | syntaxErrors | Member[foo | | Member[foo] .Member[bar] | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index 7b060946e57..abb4af545fa 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -100,3 +100,9 @@ baz2(baz2) # match class OtherSubClass (ArgPos.MyClass): def otherSelfTest(self, arg, named=2, otherName=3): # test that Parameter[0] hits `arg`. pass + + def anyParam(self, param1, param2): # Parameter[any] matches all 3. + pass + + def anyNamed(self, name1, name2=2): # Parameter[any-named] matches all 3. + pass diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index e42fd024e98..86f960b1adf 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -77,6 +77,8 @@ class Sources extends ModelInput::SourceModelCsv { "testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[foo].Parameter[named:];test-source", // "testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[secondAndAfter].Parameter[1..];test-source", // "testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[otherSelfTest].Parameter[0];test-source", // + "testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[anyParam].Parameter[any];test-source", // + "testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[anyNamed].Parameter[any-named];test-source", // ] } } From 818975dc569cc96b07930dfc86880fc5acdfcd58 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 17 May 2022 12:25:52 +0200 Subject: [PATCH 048/125] sync upstream typo fixes --- .../semmle/python/frameworks/data/internal/ApiGraphModels.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll index 0a81edf221d..69563a3eab4 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll @@ -59,7 +59,7 @@ * A `(package,type)` pair may refer to a static type or a synthetic type name used internally in the model. * Synthetic type names can be used to reuse intermediate sub-paths, when there are multiple ways to access the same * element. - * See `ModelsAsData.qll` for the langauge-specific interpretation of packages and static type names. + * See `ModelsAsData.qll` for the language-specific interpretation of packages and static type names. * * By convention, if one wants to avoid clashes with static types from the package, the type name * should be prefixed with a tilde character (`~`). For example, `(foo, ~Bar)` can be used to indicate that @@ -396,7 +396,7 @@ predicate isValidTokenNameInIdentifyingAccessPath(string name) { } /** - * Holds if `name` is a valid name for an access path token with no arguments, occuring + * Holds if `name` is a valid name for an access path token with no arguments, occurring * in an identifying access path. */ bindingset[name] From bb289e29b90b6afdee91e0b1d045ee555cb4522a Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 17 May 2022 12:26:31 +0200 Subject: [PATCH 049/125] sync typo fix to JS/RB --- .../javascript/frameworks/data/internal/ApiGraphModels.qll | 2 +- .../lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll index 127d9ca5122..69563a3eab4 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll @@ -299,7 +299,7 @@ private class AccessPathRange extends AccessPath::Range { bindingset[token] API::Node getSuccessorFromNode(API::Node node, AccessPathToken token) { // API graphs use the same label for arguments and parameters. An edge originating from a - // use-node represents be an argument, and an edge originating from a def-node represents a parameter. + // use-node represents an argument, and an edge originating from a def-node represents a parameter. // We just map both to the same thing. token.getName() = ["Argument", "Parameter"] and result = node.getParameter(AccessPath::parseIntUnbounded(token.getAnArgument())) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll index 127d9ca5122..69563a3eab4 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll @@ -299,7 +299,7 @@ private class AccessPathRange extends AccessPath::Range { bindingset[token] API::Node getSuccessorFromNode(API::Node node, AccessPathToken token) { // API graphs use the same label for arguments and parameters. An edge originating from a - // use-node represents be an argument, and an edge originating from a def-node represents a parameter. + // use-node represents an argument, and an edge originating from a def-node represents a parameter. // We just map both to the same thing. token.getName() = ["Argument", "Parameter"] and result = node.getParameter(AccessPath::parseIntUnbounded(token.getAnArgument())) From 03da62713cd0560e1581503e4cb282d1fa0b5536 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 17 May 2022 12:32:40 +0200 Subject: [PATCH 050/125] fix typo identified by QL-for-QL --- .../python/frameworks/data/internal/ApiGraphModelsSpecific.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll index ae1c59a30d0..20d99eb0335 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -177,7 +177,7 @@ predicate isExtraValidTokenNameInIdentifyingAccessPath(string name) { } /** - * Holds if `name` is a valid name for an access path token with no arguments, occuring + * Holds if `name` is a valid name for an access path token with no arguments, occurring * in an identifying access path. */ predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name) { From d5f0446940b7430aca96aa9fba5d6adb217dc50f Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 17 May 2022 22:34:38 +0200 Subject: [PATCH 051/125] exclude self parameter from the API-graph edge for keywordParameter --- python/ql/lib/semmle/python/ApiGraphs.qll | 6 ++++-- python/ql/test/library-tests/frameworks/data/test.expected | 2 -- python/ql/test/library-tests/frameworks/data/test.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/python/ql/lib/semmle/python/ApiGraphs.qll b/python/ql/lib/semmle/python/ApiGraphs.qll index eff5b6ab64e..33aabe35383 100644 --- a/python/ql/lib/semmle/python/ApiGraphs.qll +++ b/python/ql/lib/semmle/python/ApiGraphs.qll @@ -610,9 +610,11 @@ module API { ref.asExpr() = fn.getInnerScope().getArg(i) ) or - exists(string name | + exists(string name, PY::Parameter param | lbl = Label::keywordParameter(name) and - ref.asExpr() = fn.getInnerScope().getArgByName(name) + param = fn.getInnerScope().getArgByName(name) and + not param.isSelf() and + ref.asExpr() = param ) or lbl = Label::selfParameter() and diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 16ec37811a0..08300f4d905 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -85,10 +85,8 @@ isSource | test.py:86:49:86:59 | ControlFlowNode for getSource() | test-source | | test.py:87:56:87:66 | ControlFlowNode for getSource() | test-source | | test.py:101:29:101:31 | ControlFlowNode for arg | test-source | -| test.py:104:18:104:21 | ControlFlowNode for self | test-source | | test.py:104:24:104:29 | ControlFlowNode for param1 | test-source | | test.py:104:32:104:37 | ControlFlowNode for param2 | test-source | -| test.py:107:18:107:21 | ControlFlowNode for self | test-source | | test.py:107:24:107:28 | ControlFlowNode for name1 | test-source | | test.py:107:31:107:35 | ControlFlowNode for name2 | test-source | syntaxErrors diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index abb4af545fa..f01b613ce40 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -101,8 +101,8 @@ class OtherSubClass (ArgPos.MyClass): def otherSelfTest(self, arg, named=2, otherName=3): # test that Parameter[0] hits `arg`. pass - def anyParam(self, param1, param2): # Parameter[any] matches all 3. + def anyParam(self, param1, param2): # Parameter[any] matches all non-self parameters pass - def anyNamed(self, name1, name2=2): # Parameter[any-named] matches all 3. + def anyNamed(self, name1, name2=2): # Parameter[any-named] matches all non-self named parameters pass From b928ca518f11aa10ee4cc083cb71a1ff1c1032db Mon Sep 17 00:00:00 2001 From: Stephan Brandauer Date: Thu, 19 May 2022 10:47:08 +0200 Subject: [PATCH 052/125] update dependency version to 4.7.1-rc --- javascript/extractor/lib/typescript/package.json | 2 +- javascript/extractor/lib/typescript/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/javascript/extractor/lib/typescript/package.json b/javascript/extractor/lib/typescript/package.json index 3053bcef1bf..858719325db 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": "4.6.2" + "typescript": "4.7.1-rc" }, "scripts": { "build": "tsc --project tsconfig.json", diff --git a/javascript/extractor/lib/typescript/yarn.lock b/javascript/extractor/lib/typescript/yarn.lock index f7d0c40fed8..4fbd1283dda 100644 --- a/javascript/extractor/lib/typescript/yarn.lock +++ b/javascript/extractor/lib/typescript/yarn.lock @@ -6,7 +6,7 @@ version "12.7.11" resolved node-12.7.11.tgz#be879b52031cfb5d295b047f5462d8ef1a716446 -typescript@4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4" - integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== +typescript@4.7.1-rc: + version "4.7.1-rc" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.1-rc.tgz#23a0517d36c56de887b4457f29e2d265647bbd7c" + integrity sha512-EQd2NVelDe6ZVc2sO1CSpuSs+RHzY8c2n/kTNQAHw4um/eAXY+ZY4IKoUpNK0wO6C5hN+XcUXR7yqT8VbwwNIQ== From 0f3448dc24205a3b2b7337d2eba25d614ae9dfd0 Mon Sep 17 00:00:00 2001 From: Stephan Brandauer Date: Thu, 19 May 2022 14:53:46 +0200 Subject: [PATCH 053/125] update tests for typescript 4.7 --- .../TypeScript/Types/printAst.expected | 591 +++++++++++++++++- .../TypeScript/Types/tests.expected | 128 ++++ .../library-tests/TypeScript/Types/tst.ts | 82 ++- 3 files changed, 774 insertions(+), 27 deletions(-) diff --git a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected index e50154887a2..8ebfa8473ec 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected +++ b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected @@ -94,6 +94,7 @@ 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 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | @@ -120,6 +121,17 @@ nodes | 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) | +| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | +| file://:0:0:0:0 | (TypeParameters) | semmle.label | (TypeParameters) | +| file://:0:0:0:0 | (TypeParameters) | semmle.label | (TypeParameters) | +| file://:0:0:0:0 | (TypeParameters) | semmle.label | (TypeParameters) | +| file://:0:0:0:0 | (TypeParameters) | semmle.label | (TypeParameters) | | file://:0:0:0:0 | (TypeParameters) | semmle.label | (TypeParameters) | | file://:0:0:0:0 | (TypeParameters) | semmle.label | (TypeParameters) | | file://:0:0:0:0 | (TypeParameters) | semmle.label | (TypeParameters) | @@ -1128,17 +1140,186 @@ nodes | tst.ts:293:7:293:23 | [MethodCallExpr] payload.toFixed() | semmle.label | [MethodCallExpr] payload.toFixed() | | tst.ts:293:7:293:24 | [ExprStmt] payload.toFixed(); | semmle.label | [ExprStmt] payload.toFixed(); | | tst.ts:293:15:293:21 | [Label] toFixed | semmle.label | [Label] toFixed | +| tst.ts:298:1:376:1 | [FunctionDeclStmt] functio ... ring } | semmle.label | [FunctionDeclStmt] functio ... ring } | +| tst.ts:298:1:376:1 | [FunctionDeclStmt] functio ... ring } | semmle.order | 61 | +| tst.ts:298:10:298:15 | [VarDecl] foo_47 | semmle.label | [VarDecl] foo_47 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | semmle.label | [BlockStmt] { con ... ring } | +| tst.ts:299:3:299:23 | [DeclStmt] const key = ... | semmle.label | [DeclStmt] const key = ... | +| tst.ts:299:9:299:11 | [VarDecl] key | semmle.label | [VarDecl] key | +| tst.ts:299:9:299:22 | [VariableDeclarator] key = Symbol() | semmle.label | [VariableDeclarator] key = Symbol() | +| tst.ts:299:15:299:20 | [VarRef] Symbol | semmle.label | [VarRef] Symbol | +| tst.ts:299:15:299:22 | [CallExpr] Symbol() | semmle.label | [CallExpr] Symbol() | +| tst.ts:301:3:301:60 | [DeclStmt] const numberOrString = ... | semmle.label | [DeclStmt] const numberOrString = ... | +| tst.ts:301:9:301:22 | [VarDecl] numberOrString | semmle.label | [VarDecl] numberOrString | +| tst.ts:301:9:301:59 | [VariableDeclarator] numberO ... "hello" | semmle.label | [VariableDeclarator] numberO ... "hello" | +| tst.ts:301:26:301:29 | [VarRef] Math | semmle.label | [VarRef] Math | +| tst.ts:301:26:301:36 | [DotExpr] Math.random | semmle.label | [DotExpr] Math.random | +| tst.ts:301:26:301:38 | [MethodCallExpr] Math.random() | semmle.label | [MethodCallExpr] Math.random() | +| tst.ts:301:26:301:44 | [BinaryExpr] Math.random() < 0.5 | semmle.label | [BinaryExpr] Math.random() < 0.5 | +| tst.ts:301:26:301:59 | [ConditionalExpr] Math.ra ... "hello" | semmle.label | [ConditionalExpr] Math.ra ... "hello" | +| tst.ts:301:31:301:36 | [Label] random | semmle.label | [Label] random | +| tst.ts:301:42:301:44 | [Literal] 0.5 | semmle.label | [Literal] 0.5 | +| tst.ts:301:48:301:49 | [Literal] 42 | semmle.label | [Literal] 42 | +| tst.ts:301:53:301:59 | [Literal] "hello" | semmle.label | [Literal] "hello" | +| tst.ts:303:3:305:4 | [DeclStmt] let obj = ... | semmle.label | [DeclStmt] let obj = ... | +| tst.ts:303:7:303:9 | [VarDecl] obj | semmle.label | [VarDecl] obj | +| tst.ts:303:7:305:3 | [VariableDeclarator] obj = { ... ng, } | semmle.label | [VariableDeclarator] obj = { ... ng, } | +| tst.ts:303:13:305:3 | [ObjectExpr] { [ ... ng, } | semmle.label | [ObjectExpr] { [ ... ng, } | +| tst.ts:304:5:304:25 | [Property] [key]: ... rString | semmle.label | [Property] [key]: ... rString | +| tst.ts:304:6:304:8 | [VarRef] key | semmle.label | [VarRef] key | +| tst.ts:304:12:304:25 | [VarRef] numberOrString | semmle.label | [VarRef] numberOrString | +| tst.ts:307:3:310:3 | [IfStmt] if (typ ... (); } | semmle.label | [IfStmt] if (typ ... (); } | +| tst.ts:307:7:307:21 | [UnaryExpr] typeof obj[key] | semmle.label | [UnaryExpr] typeof obj[key] | +| tst.ts:307:7:307:34 | [BinaryExpr] typeof ... string" | semmle.label | [BinaryExpr] typeof ... string" | +| tst.ts:307:14:307:16 | [VarRef] obj | semmle.label | [VarRef] obj | +| tst.ts:307:14:307:21 | [IndexExpr] obj[key] | semmle.label | [IndexExpr] obj[key] | +| tst.ts:307:18:307:20 | [VarRef] key | semmle.label | [VarRef] key | +| tst.ts:307:27:307:34 | [Literal] "string" | semmle.label | [Literal] "string" | +| tst.ts:307:37:310:3 | [BlockStmt] { l ... (); } | semmle.label | [BlockStmt] { l ... (); } | +| tst.ts:308:5:308:23 | [DeclStmt] let str = ... | semmle.label | [DeclStmt] let str = ... | +| tst.ts:308:9:308:11 | [VarDecl] str | semmle.label | [VarDecl] str | +| tst.ts:308:9:308:22 | [VariableDeclarator] str = obj[key] | semmle.label | [VariableDeclarator] str = obj[key] | +| tst.ts:308:15:308:17 | [VarRef] obj | semmle.label | [VarRef] obj | +| tst.ts:308:15:308:22 | [IndexExpr] obj[key] | semmle.label | [IndexExpr] obj[key] | +| tst.ts:308:19:308:21 | [VarRef] key | semmle.label | [VarRef] key | +| tst.ts:309:5:309:7 | [VarRef] str | semmle.label | [VarRef] str | +| tst.ts:309:5:309:19 | [DotExpr] str.toUpperCase | semmle.label | [DotExpr] str.toUpperCase | +| tst.ts:309:5:309:21 | [MethodCallExpr] str.toUpperCase() | semmle.label | [MethodCallExpr] str.toUpperCase() | +| tst.ts:309:5:309:22 | [ExprStmt] str.toUpperCase(); | semmle.label | [ExprStmt] str.toUpperCase(); | +| tst.ts:309:9:309:19 | [Label] toUpperCase | semmle.label | [Label] toUpperCase | +| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | semmle.label | [FunctionDeclStmt] functio ... void {} | +| tst.ts:314:12:314:12 | [VarDecl] f | semmle.label | [VarDecl] f | +| tst.ts:314:14:314:14 | [Identifier] T | semmle.label | [Identifier] T | +| tst.ts:314:14:314:14 | [TypeParameter] T | semmle.label | [TypeParameter] T | +| tst.ts:314:17:314:19 | [SimpleParameter] arg | semmle.label | [SimpleParameter] arg | +| tst.ts:314:22:316:29 | [InterfaceTypeExpr] { p ... void } | semmle.label | [InterfaceTypeExpr] { p ... void } | +| tst.ts:315:5:315:11 | [Label] produce | semmle.label | [Label] produce | +| tst.ts:315:5:315:30 | [FieldDeclaration] produce ... ) => T, | semmle.label | [FieldDeclaration] produce ... ) => T, | +| tst.ts:315:14:315:29 | [FunctionExpr] (n: string) => T | semmle.label | [FunctionExpr] (n: string) => T | +| tst.ts:315:14:315:29 | [FunctionTypeExpr] (n: string) => T | semmle.label | [FunctionTypeExpr] (n: string) => T | +| tst.ts:315:15:315:15 | [SimpleParameter] n | semmle.label | [SimpleParameter] n | +| tst.ts:315:18:315:23 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | +| tst.ts:315:29:315:29 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | +| tst.ts:316:5:316:11 | [Label] consume | semmle.label | [Label] consume | +| tst.ts:316:5:316:27 | [FieldDeclaration] consume ... => void | semmle.label | [FieldDeclaration] consume ... => void | +| tst.ts:316:14:316:27 | [FunctionExpr] (x: T) => void | semmle.label | [FunctionExpr] (x: T) => void | +| tst.ts:316:14:316:27 | [FunctionTypeExpr] (x: T) => void | semmle.label | [FunctionTypeExpr] (x: T) => void | +| tst.ts:316:15:316:15 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | +| tst.ts:316:18:316:18 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | +| tst.ts:316:24:316:27 | [KeywordTypeExpr] void | semmle.label | [KeywordTypeExpr] void | +| tst.ts:317:6:317:9 | [KeywordTypeExpr] void | semmle.label | [KeywordTypeExpr] void | +| tst.ts:317:11:317:12 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | +| tst.ts:317:13:317:13 | [EmptyStmt] ; | semmle.label | [EmptyStmt] ; | +| tst.ts:319:3:319:3 | [VarRef] f | semmle.label | [VarRef] f | +| tst.ts:319:3:322:4 | [CallExpr] f({ ... () }) | semmle.label | [CallExpr] f({ ... () }) | +| tst.ts:319:3:322:5 | [ExprStmt] f({ ... ) }); | semmle.label | [ExprStmt] f({ ... ) }); | +| tst.ts:319:5:322:3 | [ObjectExpr] {produce: ...} | semmle.label | [ObjectExpr] {produce: ...} | +| tst.ts:320:5:320:11 | [Label] produce | semmle.label | [Label] produce | +| tst.ts:320:5:320:19 | [Property] produce: n => n | semmle.label | [Property] produce: n => n | +| tst.ts:320:14:320:14 | [SimpleParameter] n | semmle.label | [SimpleParameter] n | +| tst.ts:320:14:320:19 | [ArrowFunctionExpr] n => n | semmle.label | [ArrowFunctionExpr] n => n | +| tst.ts:320:19:320:19 | [VarRef] n | semmle.label | [VarRef] n | +| tst.ts:321:5:321:11 | [Label] consume | semmle.label | [Label] consume | +| tst.ts:321:5:321:33 | [Property] consume ... rCase() | semmle.label | [Property] consume ... rCase() | +| tst.ts:321:14:321:14 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | +| tst.ts:321:14:321:33 | [ArrowFunctionExpr] x => x.toLowerCase() | semmle.label | [ArrowFunctionExpr] x => x.toLowerCase() | +| tst.ts:321:19:321:19 | [VarRef] x | semmle.label | [VarRef] x | +| tst.ts:321:19:321:31 | [DotExpr] x.toLowerCase | semmle.label | [DotExpr] x.toLowerCase | +| tst.ts:321:19:321:33 | [MethodCallExpr] x.toLowerCase() | semmle.label | [MethodCallExpr] x.toLowerCase() | +| tst.ts:321:21:321:31 | [Label] toLowerCase | semmle.label | [Label] toLowerCase | +| tst.ts:326:3:326:38 | [DeclStmt] const ErrorMap = ... | semmle.label | [DeclStmt] const ErrorMap = ... | +| tst.ts:326:9:326:16 | [VarDecl] ErrorMap | semmle.label | [VarDecl] ErrorMap | +| tst.ts:326:9:326:37 | [VariableDeclarator] ErrorMa ... Error> | semmle.label | [VariableDeclarator] ErrorMa ... Error> | +| tst.ts:326:20:326:22 | [VarRef] Map | semmle.label | [VarRef] Map | +| tst.ts:326:20:326:37 | [???] Map | semmle.label | [???] Map | +| tst.ts:326:24:326:29 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | +| tst.ts:326:32:326:36 | [LocalTypeAccess] Error | semmle.label | [LocalTypeAccess] Error | +| tst.ts:328:3:328:34 | [DeclStmt] const errorMap = ... | semmle.label | [DeclStmt] const errorMap = ... | +| tst.ts:328:9:328:16 | [VarDecl] errorMap | semmle.label | [VarDecl] errorMap | +| tst.ts:328:9:328:33 | [VariableDeclarator] errorMa ... orMap() | semmle.label | [VariableDeclarator] errorMa ... orMap() | +| tst.ts:328:20:328:33 | [NewExpr] new ErrorMap() | semmle.label | [NewExpr] new ErrorMap() | +| tst.ts:328:24:328:31 | [VarRef] ErrorMap | semmle.label | [VarRef] ErrorMap | +| tst.ts:332:3:335:16 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | +| tst.ts:332:8:332:18 | [Identifier] FirstString | semmle.label | [Identifier] FirstString | +| tst.ts:332:20:332:20 | [Identifier] T | semmle.label | [Identifier] T | +| tst.ts:332:20:332:20 | [TypeParameter] T | semmle.label | [TypeParameter] T | +| tst.ts:333:5:333:5 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | +| tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | semmle.label | [ConditionalTypeExpr] T exten ... : never | +| tst.ts:333:15:333:52 | [TupleTypeExpr] [infer ... nown[]] | semmle.label | [TupleTypeExpr] [infer ... nown[]] | +| tst.ts:333:16:333:37 | [InferTypeExpr] infer S ... string | semmle.label | [InferTypeExpr] infer S ... string | +| tst.ts:333:22:333:22 | [Identifier] S | semmle.label | [Identifier] S | +| tst.ts:333:22:333:37 | [TypeParameter] S extends string | semmle.label | [TypeParameter] S extends string | +| tst.ts:333:32:333:37 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | +| tst.ts:333:40:333:51 | [RestTypeExpr] ...unknown[] | semmle.label | [RestTypeExpr] ...unknown[] | +| tst.ts:333:43:333:49 | [KeywordTypeExpr] unknown | semmle.label | [KeywordTypeExpr] unknown | +| tst.ts:333:43:333:51 | [ArrayTypeExpr] unknown[] | semmle.label | [ArrayTypeExpr] unknown[] | +| tst.ts:334:11:334:11 | [LocalTypeAccess] S | semmle.label | [LocalTypeAccess] S | +| tst.ts:335:11:335:15 | [KeywordTypeExpr] never | semmle.label | [KeywordTypeExpr] never | +| tst.ts:337:3:337:53 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | +| tst.ts:337:8:337:8 | [Identifier] F | semmle.label | [Identifier] F | +| tst.ts:337:12:337:22 | [LocalTypeAccess] FirstString | semmle.label | [LocalTypeAccess] FirstString | +| tst.ts:337:12:337:52 | [GenericTypeExpr] FirstSt ... olean]> | semmle.label | [GenericTypeExpr] FirstSt ... olean]> | +| tst.ts:337:24:337:51 | [TupleTypeExpr] ['a' \| ... oolean] | semmle.label | [TupleTypeExpr] ['a' \| ... oolean] | +| tst.ts:337:25:337:27 | [LiteralTypeExpr] 'a' | semmle.label | [LiteralTypeExpr] 'a' | +| tst.ts:337:25:337:33 | [UnionTypeExpr] 'a' \| 'b' | semmle.label | [UnionTypeExpr] 'a' \| 'b' | +| tst.ts:337:31:337:33 | [LiteralTypeExpr] 'b' | semmle.label | [LiteralTypeExpr] 'b' | +| tst.ts:337:36:337:41 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | +| tst.ts:337:44:337:50 | [KeywordTypeExpr] boolean | semmle.label | [KeywordTypeExpr] boolean | +| tst.ts:339:3:339:19 | [DeclStmt] const a = ... | semmle.label | [DeclStmt] const a = ... | +| tst.ts:339:9:339:9 | [VarDecl] a | semmle.label | [VarDecl] a | +| tst.ts:339:9:339:18 | [VariableDeclarator] a: F = 'a' | semmle.label | [VariableDeclarator] a: F = 'a' | +| tst.ts:339:12:339:12 | [LocalTypeAccess] F | semmle.label | [LocalTypeAccess] F | +| tst.ts:339:16:339:18 | [Literal] 'a' | semmle.label | [Literal] 'a' | +| tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | +| tst.ts:343:13:343:17 | [Identifier] State | semmle.label | [Identifier] State | +| tst.ts:343:19:343:26 | [TypeParameter] in out T | semmle.label | [TypeParameter] in out T | +| tst.ts:343:26:343:26 | [Identifier] T | semmle.label | [Identifier] T | +| tst.ts:344:5:344:7 | [Label] get | semmle.label | [Label] get | +| tst.ts:344:5:344:17 | [FieldDeclaration] get: () => T; | semmle.label | [FieldDeclaration] get: () => T; | +| tst.ts:344:10:344:16 | [FunctionExpr] () => T | semmle.label | [FunctionExpr] () => T | +| tst.ts:344:10:344:16 | [FunctionTypeExpr] () => T | semmle.label | [FunctionTypeExpr] () => T | +| tst.ts:344:16:344:16 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | +| tst.ts:345:5:345:7 | [Label] set | semmle.label | [Label] set | +| tst.ts:345:5:345:28 | [FieldDeclaration] set: (v ... > void; | semmle.label | [FieldDeclaration] set: (v ... > void; | +| tst.ts:345:10:345:27 | [FunctionExpr] (value: T) => void | semmle.label | [FunctionExpr] (value: T) => void | +| tst.ts:345:10:345:27 | [FunctionTypeExpr] (value: T) => void | semmle.label | [FunctionTypeExpr] (value: T) => void | +| tst.ts:345:11:345:15 | [SimpleParameter] value | semmle.label | [SimpleParameter] value | +| tst.ts:345:18:345:18 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | +| tst.ts:345:24:345:27 | [KeywordTypeExpr] void | semmle.label | [KeywordTypeExpr] void | +| tst.ts:348:3:351:3 | [DeclStmt] const state = ... | semmle.label | [DeclStmt] const state = ... | +| tst.ts:348:9:348:13 | [VarDecl] state | semmle.label | [VarDecl] state | +| tst.ts:348:9:351:3 | [VariableDeclarator] state: ... { } } | semmle.label | [VariableDeclarator] state: ... { } } | +| tst.ts:348:16:348:20 | [LocalTypeAccess] State | semmle.label | [LocalTypeAccess] State | +| tst.ts:348:16:348:28 | [GenericTypeExpr] State | semmle.label | [GenericTypeExpr] State | +| tst.ts:348:22:348:27 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | +| tst.ts:348:32:351:3 | [ObjectExpr] {get: ...} | semmle.label | [ObjectExpr] {get: ...} | +| tst.ts:349:5:349:7 | [Label] get | semmle.label | [Label] get | +| tst.ts:349:5:349:17 | [Property] get: () => 42 | semmle.label | [Property] get: () => 42 | +| tst.ts:349:10:349:17 | [ArrowFunctionExpr] () => 42 | semmle.label | [ArrowFunctionExpr] () => 42 | +| tst.ts:349:16:349:17 | [Literal] 42 | semmle.label | [Literal] 42 | +| tst.ts:350:5:350:7 | [Label] set | semmle.label | [Label] set | +| tst.ts:350:5:350:23 | [Property] set: (value) => { } | semmle.label | [Property] set: (value) => { } | +| tst.ts:350:10:350:23 | [ArrowFunctionExpr] (value) => { } | semmle.label | [ArrowFunctionExpr] (value) => { } | +| tst.ts:350:11:350:15 | [SimpleParameter] value | semmle.label | [SimpleParameter] value | +| tst.ts:350:21:350:23 | [BlockStmt] { } | semmle.label | [BlockStmt] { } | +| tst.ts:353:3:353:31 | [DeclStmt] const fortyTwo = ... | semmle.label | [DeclStmt] const fortyTwo = ... | +| tst.ts:353:9:353:16 | [VarDecl] fortyTwo | semmle.label | [VarDecl] fortyTwo | +| tst.ts:353:9:353:30 | [VariableDeclarator] fortyTw ... e.get() | semmle.label | [VariableDeclarator] fortyTw ... e.get() | +| tst.ts:353:20:353:24 | [VarRef] state | semmle.label | [VarRef] state | +| tst.ts:353:20:353:28 | [DotExpr] state.get | semmle.label | [DotExpr] state.get | +| tst.ts:353:20:353:30 | [MethodCallExpr] state.get() | semmle.label | [MethodCallExpr] state.get() | +| tst.ts:353:26:353:28 | [Label] get | semmle.label | [Label] get | | 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 | 61 | +| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.order | 62 | | 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 | 62 | +| type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.order | 63 | | 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 | 63 | +| type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.order | 64 | | 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 | @@ -1150,14 +1331,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 | 64 | +| type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.order | 65 | | 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 | 65 | +| type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.order | 66 | | 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 | @@ -1173,12 +1354,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 | 66 | +| type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.order | 67 | | 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 | 67 | +| type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.order | 68 | | 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 | @@ -1194,7 +1375,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 | 68 | +| type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.order | 69 | | 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 | @@ -1219,12 +1400,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 | 69 | +| type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 70 | | 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 | 70 | +| type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.order | 71 | | 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] {} | @@ -1232,36 +1413,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 | 71 | +| type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.order | 72 | | 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 | 72 | +| type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.order | 73 | | 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 | 73 | +| type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.order | 74 | | 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 | 74 | +| type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.order | 75 | | 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 | 75 | +| type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.order | 76 | | 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 | 76 | +| type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 77 | | 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 | 77 | +| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.order | 78 | | 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 | @@ -1269,14 +1450,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 | 78 | +| type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.order | 79 | | 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 | 79 | +| type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.order | 80 | | 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() {} | @@ -1288,14 +1469,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 | 80 | +| type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.order | 81 | | 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 | 81 | +| type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.order | 82 | | 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 | @@ -1304,29 +1485,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 | 82 | +| type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.order | 83 | | 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 | 83 | +| type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.order | 84 | | 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 | 84 | +| type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.order | 85 | | 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 | 85 | +| type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.order | 86 | | 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 | 86 | +| type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.order | 87 | | 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 | @@ -1471,6 +1652,8 @@ edges | file://:0:0:0:0 | (Arguments) | tst.ts:282:17:287:3 | [ObjectExpr] {kind: ...} | semmle.order | 0 | | file://:0:0:0:0 | (Arguments) | tst.ts:285:19:285:35 | [MethodCallExpr] val.toUpperCase() | semmle.label | 0 | | file://:0:0:0:0 | (Arguments) | tst.ts:285:19:285:35 | [MethodCallExpr] val.toUpperCase() | semmle.order | 0 | +| file://:0:0:0:0 | (Arguments) | tst.ts:319:5:322:3 | [ObjectExpr] {produce: ...} | semmle.label | 0 | +| file://:0:0:0:0 | (Arguments) | tst.ts:319:5:322:3 | [ObjectExpr] {produce: ...} | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | tst.ts:14:17:14:17 | [SimpleParameter] x | semmle.label | 0 | | file://:0:0:0:0 | (Parameters) | tst.ts:14:17:14:17 | [SimpleParameter] x | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | tst.ts:14:28:14:28 | [SimpleParameter] y | semmle.label | 1 | @@ -1529,6 +1712,20 @@ edges | file://:0:0:0:0 | (Parameters) | tst.ts:291:21:291:24 | [SimpleParameter] kind | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | tst.ts:291:27:291:33 | [SimpleParameter] payload | semmle.label | 1 | | file://:0:0:0:0 | (Parameters) | tst.ts:291:27:291:33 | [SimpleParameter] payload | semmle.order | 1 | +| file://:0:0:0:0 | (Parameters) | tst.ts:314:17:314:19 | [SimpleParameter] arg | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:314:17:314:19 | [SimpleParameter] arg | semmle.order | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:315:15:315:15 | [SimpleParameter] n | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:315:15:315:15 | [SimpleParameter] n | semmle.order | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:316:15:316:15 | [SimpleParameter] x | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:316:15:316:15 | [SimpleParameter] x | semmle.order | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:320:14:320:14 | [SimpleParameter] n | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:320:14:320:14 | [SimpleParameter] n | semmle.order | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:321:14:321:14 | [SimpleParameter] x | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:321:14:321:14 | [SimpleParameter] x | semmle.order | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:345:11:345:15 | [SimpleParameter] value | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:345:11:345:15 | [SimpleParameter] value | semmle.order | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:350:11:350:15 | [SimpleParameter] value | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:350:11:350:15 | [SimpleParameter] value | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | type_alias.ts:14:10:14:17 | [SimpleParameter] property | semmle.label | 0 | | file://:0:0:0:0 | (Parameters) | type_alias.ts:14:10:14:17 | [SimpleParameter] property | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | type_alias.ts:21:19:21:21 | [SimpleParameter] key | semmle.label | 0 | @@ -1539,6 +1736,14 @@ edges | file://:0:0:0:0 | (TypeParameters) | tst.ts:272:6:272:11 | [TypeParameter] K in P | semmle.order | 0 | | file://:0:0:0:0 | (TypeParameters) | tst.ts:278:26:278:48 | [TypeParameter] K exten ... TypeMap | semmle.label | 0 | | file://:0:0:0:0 | (TypeParameters) | tst.ts:278:26:278:48 | [TypeParameter] K exten ... TypeMap | semmle.order | 0 | +| file://:0:0:0:0 | (TypeParameters) | tst.ts:314:14:314:14 | [TypeParameter] T | semmle.label | 0 | +| file://:0:0:0:0 | (TypeParameters) | tst.ts:314:14:314:14 | [TypeParameter] T | semmle.order | 0 | +| file://:0:0:0:0 | (TypeParameters) | tst.ts:332:20:332:20 | [TypeParameter] T | semmle.label | 0 | +| file://:0:0:0:0 | (TypeParameters) | tst.ts:332:20:332:20 | [TypeParameter] T | semmle.order | 0 | +| file://:0:0:0:0 | (TypeParameters) | tst.ts:333:22:333:37 | [TypeParameter] S extends string | semmle.label | 0 | +| file://:0:0:0:0 | (TypeParameters) | tst.ts:333:22:333:37 | [TypeParameter] S extends string | semmle.order | 0 | +| file://:0:0:0:0 | (TypeParameters) | tst.ts:343:19:343:26 | [TypeParameter] in out T | semmle.label | 0 | +| file://:0:0:0:0 | (TypeParameters) | tst.ts:343:19:343:26 | [TypeParameter] in out T | semmle.order | 0 | | file://:0:0:0:0 | (TypeParameters) | type_alias.ts:5:19:5:19 | [TypeParameter] T | semmle.label | 0 | | file://:0:0:0:0 | (TypeParameters) | type_alias.ts:5:19:5:19 | [TypeParameter] T | semmle.order | 0 | | file://:0:0:0:0 | (TypeParameters) | type_definitions.ts:3:13:3:13 | [TypeParameter] S | semmle.label | 0 | @@ -3351,6 +3556,340 @@ edges | tst.ts:293:7:293:23 | [MethodCallExpr] payload.toFixed() | tst.ts:293:7:293:21 | [DotExpr] payload.toFixed | semmle.order | 0 | | tst.ts:293:7:293:24 | [ExprStmt] payload.toFixed(); | tst.ts:293:7:293:23 | [MethodCallExpr] payload.toFixed() | semmle.label | 1 | | tst.ts:293:7:293:24 | [ExprStmt] payload.toFixed(); | tst.ts:293:7:293:23 | [MethodCallExpr] payload.toFixed() | semmle.order | 1 | +| tst.ts:298:1:376:1 | [FunctionDeclStmt] functio ... ring } | tst.ts:298:10:298:15 | [VarDecl] foo_47 | semmle.label | 0 | +| tst.ts:298:1:376:1 | [FunctionDeclStmt] functio ... ring } | tst.ts:298:10:298:15 | [VarDecl] foo_47 | semmle.order | 0 | +| tst.ts:298:1:376:1 | [FunctionDeclStmt] functio ... ring } | tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | semmle.label | 5 | +| tst.ts:298:1:376:1 | [FunctionDeclStmt] functio ... ring } | tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | semmle.order | 5 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:299:3:299:23 | [DeclStmt] const key = ... | semmle.label | 1 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:299:3:299:23 | [DeclStmt] const key = ... | semmle.order | 1 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:301:3:301:60 | [DeclStmt] const numberOrString = ... | semmle.label | 2 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:301:3:301:60 | [DeclStmt] const numberOrString = ... | semmle.order | 2 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:303:3:305:4 | [DeclStmt] let obj = ... | semmle.label | 3 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:303:3:305:4 | [DeclStmt] let obj = ... | semmle.order | 3 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:307:3:310:3 | [IfStmt] if (typ ... (); } | semmle.label | 4 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:307:3:310:3 | [IfStmt] if (typ ... (); } | semmle.order | 4 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | semmle.label | 5 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | semmle.order | 5 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:317:13:317:13 | [EmptyStmt] ; | semmle.label | 6 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:317:13:317:13 | [EmptyStmt] ; | semmle.order | 6 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:319:3:322:5 | [ExprStmt] f({ ... ) }); | semmle.label | 7 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:319:3:322:5 | [ExprStmt] f({ ... ) }); | semmle.order | 7 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:326:3:326:38 | [DeclStmt] const ErrorMap = ... | semmle.label | 8 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:326:3:326:38 | [DeclStmt] const ErrorMap = ... | semmle.order | 8 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:328:3:328:34 | [DeclStmt] const errorMap = ... | semmle.label | 9 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:328:3:328:34 | [DeclStmt] const errorMap = ... | semmle.order | 9 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:332:3:335:16 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | semmle.label | 10 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:332:3:335:16 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | semmle.order | 10 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:337:3:337:53 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | semmle.label | 11 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:337:3:337:53 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | semmle.order | 11 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:339:3:339:19 | [DeclStmt] const a = ... | semmle.label | 12 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:339:3:339:19 | [DeclStmt] const a = ... | semmle.order | 12 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | semmle.label | 13 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | semmle.order | 13 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:348:3:351:3 | [DeclStmt] const state = ... | semmle.label | 14 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:348:3:351:3 | [DeclStmt] const state = ... | semmle.order | 14 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:353:3:353:31 | [DeclStmt] const fortyTwo = ... | semmle.label | 15 | +| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:353:3:353:31 | [DeclStmt] const fortyTwo = ... | semmle.order | 15 | +| tst.ts:299:3:299:23 | [DeclStmt] const key = ... | tst.ts:299:9:299:22 | [VariableDeclarator] key = Symbol() | semmle.label | 1 | +| tst.ts:299:3:299:23 | [DeclStmt] const key = ... | tst.ts:299:9:299:22 | [VariableDeclarator] key = Symbol() | semmle.order | 1 | +| tst.ts:299:9:299:22 | [VariableDeclarator] key = Symbol() | tst.ts:299:9:299:11 | [VarDecl] key | semmle.label | 1 | +| tst.ts:299:9:299:22 | [VariableDeclarator] key = Symbol() | tst.ts:299:9:299:11 | [VarDecl] key | semmle.order | 1 | +| tst.ts:299:9:299:22 | [VariableDeclarator] key = Symbol() | tst.ts:299:15:299:22 | [CallExpr] Symbol() | semmle.label | 2 | +| tst.ts:299:9:299:22 | [VariableDeclarator] key = Symbol() | tst.ts:299:15:299:22 | [CallExpr] Symbol() | semmle.order | 2 | +| tst.ts:299:15:299:22 | [CallExpr] Symbol() | tst.ts:299:15:299:20 | [VarRef] Symbol | semmle.label | 0 | +| tst.ts:299:15:299:22 | [CallExpr] Symbol() | tst.ts:299:15:299:20 | [VarRef] Symbol | semmle.order | 0 | +| tst.ts:301:3:301:60 | [DeclStmt] const numberOrString = ... | tst.ts:301:9:301:59 | [VariableDeclarator] numberO ... "hello" | semmle.label | 1 | +| tst.ts:301:3:301:60 | [DeclStmt] const numberOrString = ... | tst.ts:301:9:301:59 | [VariableDeclarator] numberO ... "hello" | semmle.order | 1 | +| tst.ts:301:9:301:59 | [VariableDeclarator] numberO ... "hello" | tst.ts:301:9:301:22 | [VarDecl] numberOrString | semmle.label | 1 | +| tst.ts:301:9:301:59 | [VariableDeclarator] numberO ... "hello" | tst.ts:301:9:301:22 | [VarDecl] numberOrString | semmle.order | 1 | +| tst.ts:301:9:301:59 | [VariableDeclarator] numberO ... "hello" | tst.ts:301:26:301:59 | [ConditionalExpr] Math.ra ... "hello" | semmle.label | 2 | +| tst.ts:301:9:301:59 | [VariableDeclarator] numberO ... "hello" | tst.ts:301:26:301:59 | [ConditionalExpr] Math.ra ... "hello" | semmle.order | 2 | +| tst.ts:301:26:301:36 | [DotExpr] Math.random | tst.ts:301:26:301:29 | [VarRef] Math | semmle.label | 1 | +| tst.ts:301:26:301:36 | [DotExpr] Math.random | tst.ts:301:26:301:29 | [VarRef] Math | semmle.order | 1 | +| tst.ts:301:26:301:36 | [DotExpr] Math.random | tst.ts:301:31:301:36 | [Label] random | semmle.label | 2 | +| tst.ts:301:26:301:36 | [DotExpr] Math.random | tst.ts:301:31:301:36 | [Label] random | semmle.order | 2 | +| tst.ts:301:26:301:38 | [MethodCallExpr] Math.random() | tst.ts:301:26:301:36 | [DotExpr] Math.random | semmle.label | 0 | +| tst.ts:301:26:301:38 | [MethodCallExpr] Math.random() | tst.ts:301:26:301:36 | [DotExpr] Math.random | semmle.order | 0 | +| tst.ts:301:26:301:44 | [BinaryExpr] Math.random() < 0.5 | tst.ts:301:26:301:38 | [MethodCallExpr] Math.random() | semmle.label | 1 | +| tst.ts:301:26:301:44 | [BinaryExpr] Math.random() < 0.5 | tst.ts:301:26:301:38 | [MethodCallExpr] Math.random() | semmle.order | 1 | +| tst.ts:301:26:301:44 | [BinaryExpr] Math.random() < 0.5 | tst.ts:301:42:301:44 | [Literal] 0.5 | semmle.label | 2 | +| tst.ts:301:26:301:44 | [BinaryExpr] Math.random() < 0.5 | tst.ts:301:42:301:44 | [Literal] 0.5 | semmle.order | 2 | +| tst.ts:301:26:301:59 | [ConditionalExpr] Math.ra ... "hello" | tst.ts:301:26:301:44 | [BinaryExpr] Math.random() < 0.5 | semmle.label | 1 | +| tst.ts:301:26:301:59 | [ConditionalExpr] Math.ra ... "hello" | tst.ts:301:26:301:44 | [BinaryExpr] Math.random() < 0.5 | semmle.order | 1 | +| tst.ts:301:26:301:59 | [ConditionalExpr] Math.ra ... "hello" | tst.ts:301:48:301:49 | [Literal] 42 | semmle.label | 2 | +| tst.ts:301:26:301:59 | [ConditionalExpr] Math.ra ... "hello" | tst.ts:301:48:301:49 | [Literal] 42 | semmle.order | 2 | +| tst.ts:301:26:301:59 | [ConditionalExpr] Math.ra ... "hello" | tst.ts:301:53:301:59 | [Literal] "hello" | semmle.label | 3 | +| tst.ts:301:26:301:59 | [ConditionalExpr] Math.ra ... "hello" | tst.ts:301:53:301:59 | [Literal] "hello" | semmle.order | 3 | +| tst.ts:303:3:305:4 | [DeclStmt] let obj = ... | tst.ts:303:7:305:3 | [VariableDeclarator] obj = { ... ng, } | semmle.label | 1 | +| tst.ts:303:3:305:4 | [DeclStmt] let obj = ... | tst.ts:303:7:305:3 | [VariableDeclarator] obj = { ... ng, } | semmle.order | 1 | +| tst.ts:303:7:305:3 | [VariableDeclarator] obj = { ... ng, } | tst.ts:303:7:303:9 | [VarDecl] obj | semmle.label | 1 | +| tst.ts:303:7:305:3 | [VariableDeclarator] obj = { ... ng, } | tst.ts:303:7:303:9 | [VarDecl] obj | semmle.order | 1 | +| tst.ts:303:7:305:3 | [VariableDeclarator] obj = { ... ng, } | tst.ts:303:13:305:3 | [ObjectExpr] { [ ... ng, } | semmle.label | 2 | +| tst.ts:303:7:305:3 | [VariableDeclarator] obj = { ... ng, } | tst.ts:303:13:305:3 | [ObjectExpr] { [ ... ng, } | semmle.order | 2 | +| tst.ts:303:13:305:3 | [ObjectExpr] { [ ... ng, } | tst.ts:304:5:304:25 | [Property] [key]: ... rString | semmle.label | 1 | +| tst.ts:303:13:305:3 | [ObjectExpr] { [ ... ng, } | tst.ts:304:5:304:25 | [Property] [key]: ... rString | semmle.order | 1 | +| tst.ts:304:5:304:25 | [Property] [key]: ... rString | tst.ts:304:6:304:8 | [VarRef] key | semmle.label | 1 | +| tst.ts:304:5:304:25 | [Property] [key]: ... rString | tst.ts:304:6:304:8 | [VarRef] key | semmle.order | 1 | +| tst.ts:304:5:304:25 | [Property] [key]: ... rString | tst.ts:304:12:304:25 | [VarRef] numberOrString | semmle.label | 2 | +| tst.ts:304:5:304:25 | [Property] [key]: ... rString | tst.ts:304:12:304:25 | [VarRef] numberOrString | semmle.order | 2 | +| tst.ts:307:3:310:3 | [IfStmt] if (typ ... (); } | tst.ts:307:7:307:34 | [BinaryExpr] typeof ... string" | semmle.label | 1 | +| tst.ts:307:3:310:3 | [IfStmt] if (typ ... (); } | tst.ts:307:7:307:34 | [BinaryExpr] typeof ... string" | semmle.order | 1 | +| tst.ts:307:3:310:3 | [IfStmt] if (typ ... (); } | tst.ts:307:37:310:3 | [BlockStmt] { l ... (); } | semmle.label | 2 | +| tst.ts:307:3:310:3 | [IfStmt] if (typ ... (); } | tst.ts:307:37:310:3 | [BlockStmt] { l ... (); } | semmle.order | 2 | +| tst.ts:307:7:307:21 | [UnaryExpr] typeof obj[key] | tst.ts:307:14:307:21 | [IndexExpr] obj[key] | semmle.label | 1 | +| tst.ts:307:7:307:21 | [UnaryExpr] typeof obj[key] | tst.ts:307:14:307:21 | [IndexExpr] obj[key] | semmle.order | 1 | +| tst.ts:307:7:307:34 | [BinaryExpr] typeof ... string" | tst.ts:307:7:307:21 | [UnaryExpr] typeof obj[key] | semmle.label | 1 | +| tst.ts:307:7:307:34 | [BinaryExpr] typeof ... string" | tst.ts:307:7:307:21 | [UnaryExpr] typeof obj[key] | semmle.order | 1 | +| tst.ts:307:7:307:34 | [BinaryExpr] typeof ... string" | tst.ts:307:27:307:34 | [Literal] "string" | semmle.label | 2 | +| tst.ts:307:7:307:34 | [BinaryExpr] typeof ... string" | tst.ts:307:27:307:34 | [Literal] "string" | semmle.order | 2 | +| tst.ts:307:14:307:21 | [IndexExpr] obj[key] | tst.ts:307:14:307:16 | [VarRef] obj | semmle.label | 1 | +| tst.ts:307:14:307:21 | [IndexExpr] obj[key] | tst.ts:307:14:307:16 | [VarRef] obj | semmle.order | 1 | +| tst.ts:307:14:307:21 | [IndexExpr] obj[key] | tst.ts:307:18:307:20 | [VarRef] key | semmle.label | 2 | +| tst.ts:307:14:307:21 | [IndexExpr] obj[key] | tst.ts:307:18:307:20 | [VarRef] key | semmle.order | 2 | +| tst.ts:307:37:310:3 | [BlockStmt] { l ... (); } | tst.ts:308:5:308:23 | [DeclStmt] let str = ... | semmle.label | 1 | +| tst.ts:307:37:310:3 | [BlockStmt] { l ... (); } | tst.ts:308:5:308:23 | [DeclStmt] let str = ... | semmle.order | 1 | +| tst.ts:307:37:310:3 | [BlockStmt] { l ... (); } | tst.ts:309:5:309:22 | [ExprStmt] str.toUpperCase(); | semmle.label | 2 | +| tst.ts:307:37:310:3 | [BlockStmt] { l ... (); } | tst.ts:309:5:309:22 | [ExprStmt] str.toUpperCase(); | semmle.order | 2 | +| tst.ts:308:5:308:23 | [DeclStmt] let str = ... | tst.ts:308:9:308:22 | [VariableDeclarator] str = obj[key] | semmle.label | 1 | +| tst.ts:308:5:308:23 | [DeclStmt] let str = ... | tst.ts:308:9:308:22 | [VariableDeclarator] str = obj[key] | semmle.order | 1 | +| tst.ts:308:9:308:22 | [VariableDeclarator] str = obj[key] | tst.ts:308:9:308:11 | [VarDecl] str | semmle.label | 1 | +| tst.ts:308:9:308:22 | [VariableDeclarator] str = obj[key] | tst.ts:308:9:308:11 | [VarDecl] str | semmle.order | 1 | +| tst.ts:308:9:308:22 | [VariableDeclarator] str = obj[key] | tst.ts:308:15:308:22 | [IndexExpr] obj[key] | semmle.label | 2 | +| tst.ts:308:9:308:22 | [VariableDeclarator] str = obj[key] | tst.ts:308:15:308:22 | [IndexExpr] obj[key] | semmle.order | 2 | +| tst.ts:308:15:308:22 | [IndexExpr] obj[key] | tst.ts:308:15:308:17 | [VarRef] obj | semmle.label | 1 | +| tst.ts:308:15:308:22 | [IndexExpr] obj[key] | tst.ts:308:15:308:17 | [VarRef] obj | semmle.order | 1 | +| tst.ts:308:15:308:22 | [IndexExpr] obj[key] | tst.ts:308:19:308:21 | [VarRef] key | semmle.label | 2 | +| tst.ts:308:15:308:22 | [IndexExpr] obj[key] | tst.ts:308:19:308:21 | [VarRef] key | semmle.order | 2 | +| tst.ts:309:5:309:19 | [DotExpr] str.toUpperCase | tst.ts:309:5:309:7 | [VarRef] str | semmle.label | 1 | +| tst.ts:309:5:309:19 | [DotExpr] str.toUpperCase | tst.ts:309:5:309:7 | [VarRef] str | semmle.order | 1 | +| tst.ts:309:5:309:19 | [DotExpr] str.toUpperCase | tst.ts:309:9:309:19 | [Label] toUpperCase | semmle.label | 2 | +| tst.ts:309:5:309:19 | [DotExpr] str.toUpperCase | tst.ts:309:9:309:19 | [Label] toUpperCase | semmle.order | 2 | +| tst.ts:309:5:309:21 | [MethodCallExpr] str.toUpperCase() | tst.ts:309:5:309:19 | [DotExpr] str.toUpperCase | semmle.label | 0 | +| tst.ts:309:5:309:21 | [MethodCallExpr] str.toUpperCase() | tst.ts:309:5:309:19 | [DotExpr] str.toUpperCase | semmle.order | 0 | +| tst.ts:309:5:309:22 | [ExprStmt] str.toUpperCase(); | tst.ts:309:5:309:21 | [MethodCallExpr] str.toUpperCase() | semmle.label | 1 | +| tst.ts:309:5:309:22 | [ExprStmt] str.toUpperCase(); | tst.ts:309:5:309:21 | [MethodCallExpr] str.toUpperCase() | semmle.order | 1 | +| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | file://:0:0:0:0 | (TypeParameters) | semmle.label | 2 | +| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | file://:0:0:0:0 | (TypeParameters) | semmle.order | 2 | +| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | tst.ts:314:12:314:12 | [VarDecl] f | semmle.label | 0 | +| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | tst.ts:314:12:314:12 | [VarDecl] f | semmle.order | 0 | +| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | tst.ts:317:6:317:9 | [KeywordTypeExpr] void | semmle.label | 4 | +| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | tst.ts:317:6:317:9 | [KeywordTypeExpr] void | semmle.order | 4 | +| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | tst.ts:317:11:317:12 | [BlockStmt] {} | semmle.label | 5 | +| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | tst.ts:317:11:317:12 | [BlockStmt] {} | semmle.order | 5 | +| tst.ts:314:14:314:14 | [TypeParameter] T | tst.ts:314:14:314:14 | [Identifier] T | semmle.label | 1 | +| tst.ts:314:14:314:14 | [TypeParameter] T | tst.ts:314:14:314:14 | [Identifier] T | semmle.order | 1 | +| tst.ts:314:17:314:19 | [SimpleParameter] arg | tst.ts:314:22:316:29 | [InterfaceTypeExpr] { p ... void } | semmle.label | 0 | +| tst.ts:314:17:314:19 | [SimpleParameter] arg | tst.ts:314:22:316:29 | [InterfaceTypeExpr] { p ... void } | semmle.order | 0 | +| tst.ts:314:22:316:29 | [InterfaceTypeExpr] { p ... void } | tst.ts:315:5:315:30 | [FieldDeclaration] produce ... ) => T, | semmle.label | 1 | +| tst.ts:314:22:316:29 | [InterfaceTypeExpr] { p ... void } | tst.ts:315:5:315:30 | [FieldDeclaration] produce ... ) => T, | semmle.order | 1 | +| tst.ts:314:22:316:29 | [InterfaceTypeExpr] { p ... void } | tst.ts:316:5:316:27 | [FieldDeclaration] consume ... => void | semmle.label | 2 | +| tst.ts:314:22:316:29 | [InterfaceTypeExpr] { p ... void } | tst.ts:316:5:316:27 | [FieldDeclaration] consume ... => void | semmle.order | 2 | +| tst.ts:315:5:315:30 | [FieldDeclaration] produce ... ) => T, | tst.ts:315:5:315:11 | [Label] produce | semmle.label | 1 | +| tst.ts:315:5:315:30 | [FieldDeclaration] produce ... ) => T, | tst.ts:315:5:315:11 | [Label] produce | semmle.order | 1 | +| tst.ts:315:5:315:30 | [FieldDeclaration] produce ... ) => T, | tst.ts:315:14:315:29 | [FunctionTypeExpr] (n: string) => T | semmle.label | 2 | +| tst.ts:315:5:315:30 | [FieldDeclaration] produce ... ) => T, | tst.ts:315:14:315:29 | [FunctionTypeExpr] (n: string) => T | semmle.order | 2 | +| tst.ts:315:14:315:29 | [FunctionExpr] (n: string) => T | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| tst.ts:315:14:315:29 | [FunctionExpr] (n: string) => T | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| tst.ts:315:14:315:29 | [FunctionExpr] (n: string) => T | tst.ts:315:29:315:29 | [LocalTypeAccess] T | semmle.label | 4 | +| tst.ts:315:14:315:29 | [FunctionExpr] (n: string) => T | tst.ts:315:29:315:29 | [LocalTypeAccess] T | semmle.order | 4 | +| tst.ts:315:14:315:29 | [FunctionTypeExpr] (n: string) => T | tst.ts:315:14:315:29 | [FunctionExpr] (n: string) => T | semmle.label | 1 | +| tst.ts:315:14:315:29 | [FunctionTypeExpr] (n: string) => T | tst.ts:315:14:315:29 | [FunctionExpr] (n: string) => T | semmle.order | 1 | +| tst.ts:315:15:315:15 | [SimpleParameter] n | tst.ts:315:18:315:23 | [KeywordTypeExpr] string | semmle.label | 0 | +| tst.ts:315:15:315:15 | [SimpleParameter] n | tst.ts:315:18:315:23 | [KeywordTypeExpr] string | semmle.order | 0 | +| tst.ts:316:5:316:27 | [FieldDeclaration] consume ... => void | tst.ts:316:5:316:11 | [Label] consume | semmle.label | 1 | +| tst.ts:316:5:316:27 | [FieldDeclaration] consume ... => void | tst.ts:316:5:316:11 | [Label] consume | semmle.order | 1 | +| tst.ts:316:5:316:27 | [FieldDeclaration] consume ... => void | tst.ts:316:14:316:27 | [FunctionTypeExpr] (x: T) => void | semmle.label | 2 | +| tst.ts:316:5:316:27 | [FieldDeclaration] consume ... => void | tst.ts:316:14:316:27 | [FunctionTypeExpr] (x: T) => void | semmle.order | 2 | +| tst.ts:316:14:316:27 | [FunctionExpr] (x: T) => void | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| tst.ts:316:14:316:27 | [FunctionExpr] (x: T) => void | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| tst.ts:316:14:316:27 | [FunctionExpr] (x: T) => void | tst.ts:316:24:316:27 | [KeywordTypeExpr] void | semmle.label | 4 | +| tst.ts:316:14:316:27 | [FunctionExpr] (x: T) => void | tst.ts:316:24:316:27 | [KeywordTypeExpr] void | semmle.order | 4 | +| tst.ts:316:14:316:27 | [FunctionTypeExpr] (x: T) => void | tst.ts:316:14:316:27 | [FunctionExpr] (x: T) => void | semmle.label | 1 | +| tst.ts:316:14:316:27 | [FunctionTypeExpr] (x: T) => void | tst.ts:316:14:316:27 | [FunctionExpr] (x: T) => void | semmle.order | 1 | +| tst.ts:316:15:316:15 | [SimpleParameter] x | tst.ts:316:18:316:18 | [LocalTypeAccess] T | semmle.label | 0 | +| tst.ts:316:15:316:15 | [SimpleParameter] x | tst.ts:316:18:316:18 | [LocalTypeAccess] T | semmle.order | 0 | +| tst.ts:319:3:322:4 | [CallExpr] f({ ... () }) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | +| tst.ts:319:3:322:4 | [CallExpr] f({ ... () }) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | +| tst.ts:319:3:322:4 | [CallExpr] f({ ... () }) | tst.ts:319:3:319:3 | [VarRef] f | semmle.label | 0 | +| tst.ts:319:3:322:4 | [CallExpr] f({ ... () }) | tst.ts:319:3:319:3 | [VarRef] f | semmle.order | 0 | +| tst.ts:319:3:322:5 | [ExprStmt] f({ ... ) }); | tst.ts:319:3:322:4 | [CallExpr] f({ ... () }) | semmle.label | 1 | +| tst.ts:319:3:322:5 | [ExprStmt] f({ ... ) }); | tst.ts:319:3:322:4 | [CallExpr] f({ ... () }) | semmle.order | 1 | +| tst.ts:319:5:322:3 | [ObjectExpr] {produce: ...} | tst.ts:320:5:320:19 | [Property] produce: n => n | semmle.label | 1 | +| tst.ts:319:5:322:3 | [ObjectExpr] {produce: ...} | tst.ts:320:5:320:19 | [Property] produce: n => n | semmle.order | 1 | +| tst.ts:319:5:322:3 | [ObjectExpr] {produce: ...} | tst.ts:321:5:321:33 | [Property] consume ... rCase() | semmle.label | 2 | +| tst.ts:319:5:322:3 | [ObjectExpr] {produce: ...} | tst.ts:321:5:321:33 | [Property] consume ... rCase() | semmle.order | 2 | +| tst.ts:320:5:320:19 | [Property] produce: n => n | tst.ts:320:5:320:11 | [Label] produce | semmle.label | 1 | +| tst.ts:320:5:320:19 | [Property] produce: n => n | tst.ts:320:5:320:11 | [Label] produce | semmle.order | 1 | +| tst.ts:320:5:320:19 | [Property] produce: n => n | tst.ts:320:14:320:19 | [ArrowFunctionExpr] n => n | semmle.label | 2 | +| tst.ts:320:5:320:19 | [Property] produce: n => n | tst.ts:320:14:320:19 | [ArrowFunctionExpr] n => n | semmle.order | 2 | +| tst.ts:320:14:320:19 | [ArrowFunctionExpr] n => n | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| tst.ts:320:14:320:19 | [ArrowFunctionExpr] n => n | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| tst.ts:320:14:320:19 | [ArrowFunctionExpr] n => n | tst.ts:320:19:320:19 | [VarRef] n | semmle.label | 5 | +| tst.ts:320:14:320:19 | [ArrowFunctionExpr] n => n | tst.ts:320:19:320:19 | [VarRef] n | semmle.order | 5 | +| tst.ts:321:5:321:33 | [Property] consume ... rCase() | tst.ts:321:5:321:11 | [Label] consume | semmle.label | 1 | +| tst.ts:321:5:321:33 | [Property] consume ... rCase() | tst.ts:321:5:321:11 | [Label] consume | semmle.order | 1 | +| tst.ts:321:5:321:33 | [Property] consume ... rCase() | tst.ts:321:14:321:33 | [ArrowFunctionExpr] x => x.toLowerCase() | semmle.label | 2 | +| tst.ts:321:5:321:33 | [Property] consume ... rCase() | tst.ts:321:14:321:33 | [ArrowFunctionExpr] x => x.toLowerCase() | semmle.order | 2 | +| tst.ts:321:14:321:33 | [ArrowFunctionExpr] x => x.toLowerCase() | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| tst.ts:321:14:321:33 | [ArrowFunctionExpr] x => x.toLowerCase() | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| tst.ts:321:14:321:33 | [ArrowFunctionExpr] x => x.toLowerCase() | tst.ts:321:19:321:33 | [MethodCallExpr] x.toLowerCase() | semmle.label | 5 | +| tst.ts:321:14:321:33 | [ArrowFunctionExpr] x => x.toLowerCase() | tst.ts:321:19:321:33 | [MethodCallExpr] x.toLowerCase() | semmle.order | 5 | +| tst.ts:321:19:321:31 | [DotExpr] x.toLowerCase | tst.ts:321:19:321:19 | [VarRef] x | semmle.label | 1 | +| tst.ts:321:19:321:31 | [DotExpr] x.toLowerCase | tst.ts:321:19:321:19 | [VarRef] x | semmle.order | 1 | +| tst.ts:321:19:321:31 | [DotExpr] x.toLowerCase | tst.ts:321:21:321:31 | [Label] toLowerCase | semmle.label | 2 | +| tst.ts:321:19:321:31 | [DotExpr] x.toLowerCase | tst.ts:321:21:321:31 | [Label] toLowerCase | semmle.order | 2 | +| tst.ts:321:19:321:33 | [MethodCallExpr] x.toLowerCase() | tst.ts:321:19:321:31 | [DotExpr] x.toLowerCase | semmle.label | 0 | +| tst.ts:321:19:321:33 | [MethodCallExpr] x.toLowerCase() | tst.ts:321:19:321:31 | [DotExpr] x.toLowerCase | semmle.order | 0 | +| tst.ts:326:3:326:38 | [DeclStmt] const ErrorMap = ... | tst.ts:326:9:326:37 | [VariableDeclarator] ErrorMa ... Error> | semmle.label | 1 | +| tst.ts:326:3:326:38 | [DeclStmt] const ErrorMap = ... | tst.ts:326:9:326:37 | [VariableDeclarator] ErrorMa ... Error> | semmle.order | 1 | +| tst.ts:326:9:326:37 | [VariableDeclarator] ErrorMa ... Error> | tst.ts:326:9:326:16 | [VarDecl] ErrorMap | semmle.label | 1 | +| tst.ts:326:9:326:37 | [VariableDeclarator] ErrorMa ... Error> | tst.ts:326:9:326:16 | [VarDecl] ErrorMap | semmle.order | 1 | +| tst.ts:326:9:326:37 | [VariableDeclarator] ErrorMa ... Error> | tst.ts:326:20:326:37 | [???] Map | semmle.label | 2 | +| tst.ts:326:9:326:37 | [VariableDeclarator] ErrorMa ... Error> | tst.ts:326:20:326:37 | [???] Map | semmle.order | 2 | +| tst.ts:326:20:326:37 | [???] Map | tst.ts:326:20:326:22 | [VarRef] Map | semmle.label | 1 | +| tst.ts:326:20:326:37 | [???] Map | tst.ts:326:20:326:22 | [VarRef] Map | semmle.order | 1 | +| tst.ts:326:20:326:37 | [???] Map | tst.ts:326:24:326:29 | [KeywordTypeExpr] string | semmle.label | 2 | +| tst.ts:326:20:326:37 | [???] Map | tst.ts:326:24:326:29 | [KeywordTypeExpr] string | semmle.order | 2 | +| tst.ts:326:20:326:37 | [???] Map | tst.ts:326:32:326:36 | [LocalTypeAccess] Error | semmle.label | 3 | +| tst.ts:326:20:326:37 | [???] Map | tst.ts:326:32:326:36 | [LocalTypeAccess] Error | semmle.order | 3 | +| tst.ts:328:3:328:34 | [DeclStmt] const errorMap = ... | tst.ts:328:9:328:33 | [VariableDeclarator] errorMa ... orMap() | semmle.label | 1 | +| tst.ts:328:3:328:34 | [DeclStmt] const errorMap = ... | tst.ts:328:9:328:33 | [VariableDeclarator] errorMa ... orMap() | semmle.order | 1 | +| tst.ts:328:9:328:33 | [VariableDeclarator] errorMa ... orMap() | tst.ts:328:9:328:16 | [VarDecl] errorMap | semmle.label | 1 | +| tst.ts:328:9:328:33 | [VariableDeclarator] errorMa ... orMap() | tst.ts:328:9:328:16 | [VarDecl] errorMap | semmle.order | 1 | +| tst.ts:328:9:328:33 | [VariableDeclarator] errorMa ... orMap() | tst.ts:328:20:328:33 | [NewExpr] new ErrorMap() | semmle.label | 2 | +| tst.ts:328:9:328:33 | [VariableDeclarator] errorMa ... orMap() | tst.ts:328:20:328:33 | [NewExpr] new ErrorMap() | semmle.order | 2 | +| tst.ts:328:20:328:33 | [NewExpr] new ErrorMap() | tst.ts:328:24:328:31 | [VarRef] ErrorMap | semmle.label | 0 | +| tst.ts:328:20:328:33 | [NewExpr] new ErrorMap() | tst.ts:328:24:328:31 | [VarRef] ErrorMap | semmle.order | 0 | +| tst.ts:332:3:335:16 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | +| tst.ts:332:3:335:16 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | +| tst.ts:332:3:335:16 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | tst.ts:332:8:332:18 | [Identifier] FirstString | semmle.label | 1 | +| tst.ts:332:3:335:16 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | tst.ts:332:8:332:18 | [Identifier] FirstString | semmle.order | 1 | +| tst.ts:332:3:335:16 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | semmle.label | 3 | +| tst.ts:332:3:335:16 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | semmle.order | 3 | +| tst.ts:332:20:332:20 | [TypeParameter] T | tst.ts:332:20:332:20 | [Identifier] T | semmle.label | 1 | +| tst.ts:332:20:332:20 | [TypeParameter] T | tst.ts:332:20:332:20 | [Identifier] T | semmle.order | 1 | +| tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | tst.ts:333:5:333:5 | [LocalTypeAccess] T | semmle.label | 1 | +| tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | tst.ts:333:5:333:5 | [LocalTypeAccess] T | semmle.order | 1 | +| tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | tst.ts:333:15:333:52 | [TupleTypeExpr] [infer ... nown[]] | semmle.label | 2 | +| tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | tst.ts:333:15:333:52 | [TupleTypeExpr] [infer ... nown[]] | semmle.order | 2 | +| tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | tst.ts:334:11:334:11 | [LocalTypeAccess] S | semmle.label | 3 | +| tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | tst.ts:334:11:334:11 | [LocalTypeAccess] S | semmle.order | 3 | +| tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | tst.ts:335:11:335:15 | [KeywordTypeExpr] never | semmle.label | 4 | +| tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | tst.ts:335:11:335:15 | [KeywordTypeExpr] never | semmle.order | 4 | +| tst.ts:333:15:333:52 | [TupleTypeExpr] [infer ... nown[]] | tst.ts:333:16:333:37 | [InferTypeExpr] infer S ... string | semmle.label | 1 | +| tst.ts:333:15:333:52 | [TupleTypeExpr] [infer ... nown[]] | tst.ts:333:16:333:37 | [InferTypeExpr] infer S ... string | semmle.order | 1 | +| tst.ts:333:15:333:52 | [TupleTypeExpr] [infer ... nown[]] | tst.ts:333:40:333:51 | [RestTypeExpr] ...unknown[] | semmle.label | 2 | +| tst.ts:333:15:333:52 | [TupleTypeExpr] [infer ... nown[]] | tst.ts:333:40:333:51 | [RestTypeExpr] ...unknown[] | semmle.order | 2 | +| tst.ts:333:16:333:37 | [InferTypeExpr] infer S ... string | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | +| tst.ts:333:16:333:37 | [InferTypeExpr] infer S ... string | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | +| tst.ts:333:22:333:37 | [TypeParameter] S extends string | tst.ts:333:22:333:22 | [Identifier] S | semmle.label | 1 | +| tst.ts:333:22:333:37 | [TypeParameter] S extends string | tst.ts:333:22:333:22 | [Identifier] S | semmle.order | 1 | +| tst.ts:333:22:333:37 | [TypeParameter] S extends string | tst.ts:333:32:333:37 | [KeywordTypeExpr] string | semmle.label | 2 | +| tst.ts:333:22:333:37 | [TypeParameter] S extends string | tst.ts:333:32:333:37 | [KeywordTypeExpr] string | semmle.order | 2 | +| tst.ts:333:40:333:51 | [RestTypeExpr] ...unknown[] | tst.ts:333:43:333:51 | [ArrayTypeExpr] unknown[] | semmle.label | 1 | +| tst.ts:333:40:333:51 | [RestTypeExpr] ...unknown[] | tst.ts:333:43:333:51 | [ArrayTypeExpr] unknown[] | semmle.order | 1 | +| tst.ts:333:43:333:51 | [ArrayTypeExpr] unknown[] | tst.ts:333:43:333:49 | [KeywordTypeExpr] unknown | semmle.label | 1 | +| tst.ts:333:43:333:51 | [ArrayTypeExpr] unknown[] | tst.ts:333:43:333:49 | [KeywordTypeExpr] unknown | semmle.order | 1 | +| tst.ts:337:3:337:53 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | tst.ts:337:8:337:8 | [Identifier] F | semmle.label | 1 | +| tst.ts:337:3:337:53 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | tst.ts:337:8:337:8 | [Identifier] F | semmle.order | 1 | +| tst.ts:337:3:337:53 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | tst.ts:337:12:337:52 | [GenericTypeExpr] FirstSt ... olean]> | semmle.label | 2 | +| tst.ts:337:3:337:53 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | tst.ts:337:12:337:52 | [GenericTypeExpr] FirstSt ... olean]> | semmle.order | 2 | +| tst.ts:337:12:337:52 | [GenericTypeExpr] FirstSt ... olean]> | tst.ts:337:12:337:22 | [LocalTypeAccess] FirstString | semmle.label | 1 | +| tst.ts:337:12:337:52 | [GenericTypeExpr] FirstSt ... olean]> | tst.ts:337:12:337:22 | [LocalTypeAccess] FirstString | semmle.order | 1 | +| tst.ts:337:12:337:52 | [GenericTypeExpr] FirstSt ... olean]> | tst.ts:337:24:337:51 | [TupleTypeExpr] ['a' \| ... oolean] | semmle.label | 2 | +| tst.ts:337:12:337:52 | [GenericTypeExpr] FirstSt ... olean]> | tst.ts:337:24:337:51 | [TupleTypeExpr] ['a' \| ... oolean] | semmle.order | 2 | +| tst.ts:337:24:337:51 | [TupleTypeExpr] ['a' \| ... oolean] | tst.ts:337:25:337:33 | [UnionTypeExpr] 'a' \| 'b' | semmle.label | 1 | +| tst.ts:337:24:337:51 | [TupleTypeExpr] ['a' \| ... oolean] | tst.ts:337:25:337:33 | [UnionTypeExpr] 'a' \| 'b' | semmle.order | 1 | +| tst.ts:337:24:337:51 | [TupleTypeExpr] ['a' \| ... oolean] | tst.ts:337:36:337:41 | [KeywordTypeExpr] number | semmle.label | 2 | +| tst.ts:337:24:337:51 | [TupleTypeExpr] ['a' \| ... oolean] | tst.ts:337:36:337:41 | [KeywordTypeExpr] number | semmle.order | 2 | +| tst.ts:337:24:337:51 | [TupleTypeExpr] ['a' \| ... oolean] | tst.ts:337:44:337:50 | [KeywordTypeExpr] boolean | semmle.label | 3 | +| tst.ts:337:24:337:51 | [TupleTypeExpr] ['a' \| ... oolean] | tst.ts:337:44:337:50 | [KeywordTypeExpr] boolean | semmle.order | 3 | +| tst.ts:337:25:337:33 | [UnionTypeExpr] 'a' \| 'b' | tst.ts:337:25:337:27 | [LiteralTypeExpr] 'a' | semmle.label | 1 | +| tst.ts:337:25:337:33 | [UnionTypeExpr] 'a' \| 'b' | tst.ts:337:25:337:27 | [LiteralTypeExpr] 'a' | semmle.order | 1 | +| tst.ts:337:25:337:33 | [UnionTypeExpr] 'a' \| 'b' | tst.ts:337:31:337:33 | [LiteralTypeExpr] 'b' | semmle.label | 2 | +| tst.ts:337:25:337:33 | [UnionTypeExpr] 'a' \| 'b' | tst.ts:337:31:337:33 | [LiteralTypeExpr] 'b' | semmle.order | 2 | +| tst.ts:339:3:339:19 | [DeclStmt] const a = ... | tst.ts:339:9:339:18 | [VariableDeclarator] a: F = 'a' | semmle.label | 1 | +| tst.ts:339:3:339:19 | [DeclStmt] const a = ... | tst.ts:339:9:339:18 | [VariableDeclarator] a: F = 'a' | semmle.order | 1 | +| tst.ts:339:9:339:18 | [VariableDeclarator] a: F = 'a' | tst.ts:339:9:339:9 | [VarDecl] a | semmle.label | 1 | +| tst.ts:339:9:339:18 | [VariableDeclarator] a: F = 'a' | tst.ts:339:9:339:9 | [VarDecl] a | semmle.order | 1 | +| tst.ts:339:9:339:18 | [VariableDeclarator] a: F = 'a' | tst.ts:339:12:339:12 | [LocalTypeAccess] F | semmle.label | 2 | +| tst.ts:339:9:339:18 | [VariableDeclarator] a: F = 'a' | tst.ts:339:12:339:12 | [LocalTypeAccess] F | semmle.order | 2 | +| tst.ts:339:9:339:18 | [VariableDeclarator] a: F = 'a' | tst.ts:339:16:339:18 | [Literal] 'a' | semmle.label | 3 | +| tst.ts:339:9:339:18 | [VariableDeclarator] a: F = 'a' | tst.ts:339:16:339:18 | [Literal] 'a' | semmle.order | 3 | +| tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | +| tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | +| tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | tst.ts:343:13:343:17 | [Identifier] State | semmle.label | 1 | +| tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | tst.ts:343:13:343:17 | [Identifier] State | semmle.order | 1 | +| tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | tst.ts:344:5:344:17 | [FieldDeclaration] get: () => T; | semmle.label | 3 | +| tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | tst.ts:344:5:344:17 | [FieldDeclaration] get: () => T; | semmle.order | 3 | +| tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | tst.ts:345:5:345:28 | [FieldDeclaration] set: (v ... > void; | semmle.label | 4 | +| tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | tst.ts:345:5:345:28 | [FieldDeclaration] set: (v ... > void; | semmle.order | 4 | +| tst.ts:343:19:343:26 | [TypeParameter] in out T | tst.ts:343:26:343:26 | [Identifier] T | semmle.label | 1 | +| tst.ts:343:19:343:26 | [TypeParameter] in out T | tst.ts:343:26:343:26 | [Identifier] T | semmle.order | 1 | +| tst.ts:344:5:344:17 | [FieldDeclaration] get: () => T; | tst.ts:344:5:344:7 | [Label] get | semmle.label | 1 | +| tst.ts:344:5:344:17 | [FieldDeclaration] get: () => T; | tst.ts:344:5:344:7 | [Label] get | semmle.order | 1 | +| tst.ts:344:5:344:17 | [FieldDeclaration] get: () => T; | tst.ts:344:10:344:16 | [FunctionTypeExpr] () => T | semmle.label | 2 | +| tst.ts:344:5:344:17 | [FieldDeclaration] get: () => T; | tst.ts:344:10:344:16 | [FunctionTypeExpr] () => T | semmle.order | 2 | +| tst.ts:344:10:344:16 | [FunctionExpr] () => T | tst.ts:344:16:344:16 | [LocalTypeAccess] T | semmle.label | 4 | +| tst.ts:344:10:344:16 | [FunctionExpr] () => T | tst.ts:344:16:344:16 | [LocalTypeAccess] T | semmle.order | 4 | +| tst.ts:344:10:344:16 | [FunctionTypeExpr] () => T | tst.ts:344:10:344:16 | [FunctionExpr] () => T | semmle.label | 1 | +| tst.ts:344:10:344:16 | [FunctionTypeExpr] () => T | tst.ts:344:10:344:16 | [FunctionExpr] () => T | semmle.order | 1 | +| tst.ts:345:5:345:28 | [FieldDeclaration] set: (v ... > void; | tst.ts:345:5:345:7 | [Label] set | semmle.label | 1 | +| tst.ts:345:5:345:28 | [FieldDeclaration] set: (v ... > void; | tst.ts:345:5:345:7 | [Label] set | semmle.order | 1 | +| tst.ts:345:5:345:28 | [FieldDeclaration] set: (v ... > void; | tst.ts:345:10:345:27 | [FunctionTypeExpr] (value: T) => void | semmle.label | 2 | +| tst.ts:345:5:345:28 | [FieldDeclaration] set: (v ... > void; | tst.ts:345:10:345:27 | [FunctionTypeExpr] (value: T) => void | semmle.order | 2 | +| tst.ts:345:10:345:27 | [FunctionExpr] (value: T) => void | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| tst.ts:345:10:345:27 | [FunctionExpr] (value: T) => void | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| tst.ts:345:10:345:27 | [FunctionExpr] (value: T) => void | tst.ts:345:24:345:27 | [KeywordTypeExpr] void | semmle.label | 4 | +| tst.ts:345:10:345:27 | [FunctionExpr] (value: T) => void | tst.ts:345:24:345:27 | [KeywordTypeExpr] void | semmle.order | 4 | +| tst.ts:345:10:345:27 | [FunctionTypeExpr] (value: T) => void | tst.ts:345:10:345:27 | [FunctionExpr] (value: T) => void | semmle.label | 1 | +| tst.ts:345:10:345:27 | [FunctionTypeExpr] (value: T) => void | tst.ts:345:10:345:27 | [FunctionExpr] (value: T) => void | semmle.order | 1 | +| tst.ts:345:11:345:15 | [SimpleParameter] value | tst.ts:345:18:345:18 | [LocalTypeAccess] T | semmle.label | 0 | +| tst.ts:345:11:345:15 | [SimpleParameter] value | tst.ts:345:18:345:18 | [LocalTypeAccess] T | semmle.order | 0 | +| tst.ts:348:3:351:3 | [DeclStmt] const state = ... | tst.ts:348:9:351:3 | [VariableDeclarator] state: ... { } } | semmle.label | 1 | +| tst.ts:348:3:351:3 | [DeclStmt] const state = ... | tst.ts:348:9:351:3 | [VariableDeclarator] state: ... { } } | semmle.order | 1 | +| tst.ts:348:9:351:3 | [VariableDeclarator] state: ... { } } | tst.ts:348:9:348:13 | [VarDecl] state | semmle.label | 1 | +| tst.ts:348:9:351:3 | [VariableDeclarator] state: ... { } } | tst.ts:348:9:348:13 | [VarDecl] state | semmle.order | 1 | +| tst.ts:348:9:351:3 | [VariableDeclarator] state: ... { } } | tst.ts:348:16:348:28 | [GenericTypeExpr] State | semmle.label | 2 | +| tst.ts:348:9:351:3 | [VariableDeclarator] state: ... { } } | tst.ts:348:16:348:28 | [GenericTypeExpr] State | semmle.order | 2 | +| tst.ts:348:9:351:3 | [VariableDeclarator] state: ... { } } | tst.ts:348:32:351:3 | [ObjectExpr] {get: ...} | semmle.label | 3 | +| tst.ts:348:9:351:3 | [VariableDeclarator] state: ... { } } | tst.ts:348:32:351:3 | [ObjectExpr] {get: ...} | semmle.order | 3 | +| tst.ts:348:16:348:28 | [GenericTypeExpr] State | tst.ts:348:16:348:20 | [LocalTypeAccess] State | semmle.label | 1 | +| tst.ts:348:16:348:28 | [GenericTypeExpr] State | tst.ts:348:16:348:20 | [LocalTypeAccess] State | semmle.order | 1 | +| tst.ts:348:16:348:28 | [GenericTypeExpr] State | tst.ts:348:22:348:27 | [KeywordTypeExpr] number | semmle.label | 2 | +| tst.ts:348:16:348:28 | [GenericTypeExpr] State | tst.ts:348:22:348:27 | [KeywordTypeExpr] number | semmle.order | 2 | +| tst.ts:348:32:351:3 | [ObjectExpr] {get: ...} | tst.ts:349:5:349:17 | [Property] get: () => 42 | semmle.label | 1 | +| tst.ts:348:32:351:3 | [ObjectExpr] {get: ...} | tst.ts:349:5:349:17 | [Property] get: () => 42 | semmle.order | 1 | +| tst.ts:348:32:351:3 | [ObjectExpr] {get: ...} | tst.ts:350:5:350:23 | [Property] set: (value) => { } | semmle.label | 2 | +| tst.ts:348:32:351:3 | [ObjectExpr] {get: ...} | tst.ts:350:5:350:23 | [Property] set: (value) => { } | semmle.order | 2 | +| tst.ts:349:5:349:17 | [Property] get: () => 42 | tst.ts:349:5:349:7 | [Label] get | semmle.label | 1 | +| tst.ts:349:5:349:17 | [Property] get: () => 42 | tst.ts:349:5:349:7 | [Label] get | semmle.order | 1 | +| tst.ts:349:5:349:17 | [Property] get: () => 42 | tst.ts:349:10:349:17 | [ArrowFunctionExpr] () => 42 | semmle.label | 2 | +| tst.ts:349:5:349:17 | [Property] get: () => 42 | tst.ts:349:10:349:17 | [ArrowFunctionExpr] () => 42 | semmle.order | 2 | +| tst.ts:349:10:349:17 | [ArrowFunctionExpr] () => 42 | tst.ts:349:16:349:17 | [Literal] 42 | semmle.label | 5 | +| tst.ts:349:10:349:17 | [ArrowFunctionExpr] () => 42 | tst.ts:349:16:349:17 | [Literal] 42 | semmle.order | 5 | +| tst.ts:350:5:350:23 | [Property] set: (value) => { } | tst.ts:350:5:350:7 | [Label] set | semmle.label | 1 | +| tst.ts:350:5:350:23 | [Property] set: (value) => { } | tst.ts:350:5:350:7 | [Label] set | semmle.order | 1 | +| tst.ts:350:5:350:23 | [Property] set: (value) => { } | tst.ts:350:10:350:23 | [ArrowFunctionExpr] (value) => { } | semmle.label | 2 | +| tst.ts:350:5:350:23 | [Property] set: (value) => { } | tst.ts:350:10:350:23 | [ArrowFunctionExpr] (value) => { } | semmle.order | 2 | +| tst.ts:350:10:350:23 | [ArrowFunctionExpr] (value) => { } | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| tst.ts:350:10:350:23 | [ArrowFunctionExpr] (value) => { } | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| tst.ts:350:10:350:23 | [ArrowFunctionExpr] (value) => { } | tst.ts:350:21:350:23 | [BlockStmt] { } | semmle.label | 5 | +| tst.ts:350:10:350:23 | [ArrowFunctionExpr] (value) => { } | tst.ts:350:21:350:23 | [BlockStmt] { } | semmle.order | 5 | +| tst.ts:353:3:353:31 | [DeclStmt] const fortyTwo = ... | tst.ts:353:9:353:30 | [VariableDeclarator] fortyTw ... e.get() | semmle.label | 1 | +| tst.ts:353:3:353:31 | [DeclStmt] const fortyTwo = ... | tst.ts:353:9:353:30 | [VariableDeclarator] fortyTw ... e.get() | semmle.order | 1 | +| tst.ts:353:9:353:30 | [VariableDeclarator] fortyTw ... e.get() | tst.ts:353:9:353:16 | [VarDecl] fortyTwo | semmle.label | 1 | +| tst.ts:353:9:353:30 | [VariableDeclarator] fortyTw ... e.get() | tst.ts:353:9:353:16 | [VarDecl] fortyTwo | semmle.order | 1 | +| tst.ts:353:9:353:30 | [VariableDeclarator] fortyTw ... e.get() | tst.ts:353:20:353:30 | [MethodCallExpr] state.get() | semmle.label | 2 | +| tst.ts:353:9:353:30 | [VariableDeclarator] fortyTw ... e.get() | tst.ts:353:20:353:30 | [MethodCallExpr] state.get() | semmle.order | 2 | +| tst.ts:353:20:353:28 | [DotExpr] state.get | tst.ts:353:20:353:24 | [VarRef] state | semmle.label | 1 | +| tst.ts:353:20:353:28 | [DotExpr] state.get | tst.ts:353:20:353:24 | [VarRef] state | semmle.order | 1 | +| tst.ts:353:20:353:28 | [DotExpr] state.get | tst.ts:353:26:353:28 | [Label] get | semmle.label | 2 | +| tst.ts:353:20:353:28 | [DotExpr] state.get | tst.ts:353:26:353:28 | [Label] get | semmle.order | 2 | +| tst.ts:353:20:353:30 | [MethodCallExpr] state.get() | tst.ts:353:20:353:28 | [DotExpr] state.get | semmle.label | 0 | +| tst.ts:353:20:353:30 | [MethodCallExpr] state.get() | tst.ts:353:20:353:28 | [DotExpr] state.get | semmle.order | 0 | | type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | type_alias.ts:1:6:1:6 | [Identifier] B | semmle.label | 1 | | type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | type_alias.ts:1:6:1:6 | [Identifier] B | semmle.order | 1 | | type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | type_alias.ts:1:10:1:16 | [KeywordTypeExpr] boolean | semmle.label | 2 | diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tests.expected b/javascript/ql/test/library-tests/TypeScript/Types/tests.expected index 4e85a6fe95c..1c6d0182dcb 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/tests.expected +++ b/javascript/ql/test/library-tests/TypeScript/Types/tests.expected @@ -386,6 +386,86 @@ getExprType | tst.ts:293:7:293:21 | payload.toFixed | (fractionDigits?: number) => string | | tst.ts:293:7:293:23 | payload.toFixed() | string | | tst.ts:293:15:293:21 | toFixed | (fractionDigits?: number) => string | +| tst.ts:298:10:298:15 | foo_47 | () => void | +| tst.ts:299:9:299:11 | key | typeof key | +| tst.ts:299:15:299:20 | Symbol | SymbolConstructor | +| tst.ts:299:15:299:22 | Symbol() | typeof key | +| tst.ts:301:9:301:22 | numberOrString | "hello" \| 42 | +| tst.ts:301:26:301:29 | Math | Math | +| tst.ts:301:26:301:36 | Math.random | () => number | +| tst.ts:301:26:301:38 | Math.random() | number | +| tst.ts:301:26:301:44 | Math.random() < 0.5 | boolean | +| tst.ts:301:26:301:59 | Math.ra ... "hello" | "hello" \| 42 | +| tst.ts:301:31:301:36 | random | () => number | +| tst.ts:301:42:301:44 | 0.5 | 0.5 | +| tst.ts:301:48:301:49 | 42 | 42 | +| tst.ts:301:53:301:59 | "hello" | "hello" | +| tst.ts:303:7:303:9 | obj | { [key]: string \| number; } | +| tst.ts:303:13:305:3 | {\\n [ ... ng,\\n } | { [key]: string \| number; } | +| tst.ts:304:6:304:8 | key | typeof key | +| tst.ts:304:12:304:25 | numberOrString | "hello" \| 42 | +| tst.ts:307:7:307:21 | typeof obj[key] | "string" \| "number" \| "bigint" \| "boolean" \| "s... | +| tst.ts:307:7:307:34 | typeof ... string" | boolean | +| tst.ts:307:14:307:16 | obj | { [key]: string \| number; } | +| tst.ts:307:14:307:21 | obj[key] | string \| number | +| tst.ts:307:18:307:20 | key | typeof key | +| tst.ts:307:27:307:34 | "string" | "string" | +| tst.ts:308:9:308:11 | str | string | +| tst.ts:308:15:308:17 | obj | { [key]: string \| number; } | +| tst.ts:308:15:308:22 | obj[key] | string | +| tst.ts:308:19:308:21 | key | typeof key | +| tst.ts:309:5:309:7 | str | string | +| tst.ts:309:5:309:19 | str.toUpperCase | () => string | +| tst.ts:309:5:309:21 | str.toUpperCase() | string | +| tst.ts:309:9:309:19 | toUpperCase | () => string | +| tst.ts:314:12:314:12 | f | (arg: { produce: (n: string) => T; consume: ... | +| tst.ts:314:17:314:19 | arg | { produce: (n: string) => T; consume: (x: T) =>... | +| tst.ts:315:5:315:11 | produce | (n: string) => T | +| tst.ts:315:14:315:29 | (n: string) => T | (n: string) => T | +| tst.ts:315:15:315:15 | n | string | +| tst.ts:316:5:316:11 | consume | (x: T) => void | +| tst.ts:316:14:316:27 | (x: T) => void | (x: T) => void | +| tst.ts:316:15:316:15 | x | T | +| tst.ts:319:3:319:3 | f | (arg: { produce: (n: string) => T; consume: ... | +| tst.ts:319:3:322:4 | f({\\n ... ()\\n }) | void | +| tst.ts:319:5:322:3 | {\\n p ... e()\\n } | { produce: (n: string) => string; consume: (x: ... | +| tst.ts:320:5:320:11 | produce | (n: string) => string | +| tst.ts:320:14:320:14 | n | string | +| tst.ts:320:14:320:19 | n => n | (n: string) => string | +| tst.ts:320:19:320:19 | n | string | +| tst.ts:321:5:321:11 | consume | (x: string) => string | +| tst.ts:321:14:321:14 | x | string | +| tst.ts:321:14:321:33 | x => x.toLowerCase() | (x: string) => string | +| tst.ts:321:19:321:19 | x | string | +| tst.ts:321:19:321:31 | x.toLowerCase | () => string | +| tst.ts:321:19:321:33 | x.toLowerCase() | string | +| tst.ts:321:21:321:31 | toLowerCase | () => string | +| tst.ts:326:9:326:16 | ErrorMap | { new (entries?: readonly (readonly [string, Er... | +| tst.ts:326:20:326:22 | Map | MapConstructor | +| tst.ts:326:20:326:37 | Map | any | +| tst.ts:328:9:328:16 | errorMap | Map | +| tst.ts:328:20:328:33 | new ErrorMap() | Map | +| tst.ts:328:24:328:31 | ErrorMap | { new (entries?: readonly (readonly [string, Er... | +| tst.ts:339:9:339:9 | a | "a" \| "b" | +| tst.ts:339:16:339:18 | 'a' | "a" | +| tst.ts:344:5:344:7 | get | () => T | +| tst.ts:344:10:344:16 | () => T | () => T | +| tst.ts:345:5:345:7 | set | (value: T) => void | +| tst.ts:345:10:345:27 | (value: T) => void | (value: T) => void | +| tst.ts:345:11:345:15 | value | T | +| tst.ts:348:9:348:13 | state | State | +| tst.ts:348:32:351:3 | {\\n g ... { }\\n } | State | +| tst.ts:349:5:349:7 | get | () => number | +| tst.ts:349:10:349:17 | () => 42 | () => number | +| tst.ts:349:16:349:17 | 42 | 42 | +| tst.ts:350:5:350:7 | set | (value: number) => void | +| tst.ts:350:10:350:23 | (value) => { } | (value: number) => void | +| tst.ts:350:11:350:15 | value | number | +| tst.ts:353:9:353:16 | fortyTwo | number | +| tst.ts:353:20:353:24 | state | State | +| tst.ts:353:20:353:28 | state.get | () => number | +| tst.ts:353:20:353:30 | state.get() | number | +| tst.ts:353:26:353:28 | get | () => number | | type_alias.ts:3:5:3:5 | b | boolean | | type_alias.ts:7:5:7:5 | c | ValueOrArray | | type_alias.ts:14:9:14:32 | [proper ... ]: Json | any | @@ -461,6 +541,9 @@ getTypeDefinitionType | tst.ts:265:3:269:3 | interfa ... an;\\n } | TypeMap | | tst.ts:271:3:276:7 | type Un ... }[P]; | UnionRecord

    | | tst.ts:289:3:289:63 | type Fu ... > void; | Func | +| tst.ts:332:3:335:16 | type Fi ... never; | FirstString | +| tst.ts:337:3:337:53 | type F ... lean]>; | "a" \| "b" | +| tst.ts:343:3:346:3 | interfa ... id;\\n } | State | | 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 | @@ -703,6 +786,39 @@ getTypeExprType | tst.ts:289:47:289:52 | string | string | | tst.ts:289:59:289:62 | void | void | | tst.ts:291:13:291:16 | Func | Func | +| tst.ts:314:14:314:14 | T | T | +| tst.ts:314:22:316:29 | {\\n p ... void } | { produce: (n: string) => T; consume: (x: T) =>... | +| tst.ts:315:14:315:29 | (n: string) => T | (n: string) => T | +| tst.ts:315:18:315:23 | string | string | +| tst.ts:315:29:315:29 | T | T | +| tst.ts:316:14:316:27 | (x: T) => void | (x: T) => void | +| tst.ts:316:18:316:18 | T | T | +| tst.ts:316:24:316:27 | void | void | +| tst.ts:317:6:317:9 | void | void | +| tst.ts:326:24:326:29 | string | string | +| tst.ts:326:32:326:36 | Error | Error | +| tst.ts:332:8:332:18 | FirstString | FirstString | +| tst.ts:332:20:332:20 | T | T | +| tst.ts:337:8:337:8 | F | "a" \| "b" | +| tst.ts:337:12:337:22 | FirstString | FirstString | +| tst.ts:337:12:337:52 | FirstSt ... olean]> | "a" \| "b" | +| tst.ts:337:24:337:51 | ['a' \| ... oolean] | ["a" \| "b", number, boolean] | +| tst.ts:337:25:337:27 | 'a' | "a" | +| tst.ts:337:25:337:33 | 'a' \| 'b' | "a" \| "b" | +| tst.ts:337:31:337:33 | 'b' | "b" | +| tst.ts:337:36:337:41 | number | number | +| tst.ts:337:44:337:50 | boolean | boolean | +| tst.ts:339:12:339:12 | F | "a" \| "b" | +| tst.ts:343:13:343:17 | State | State | +| tst.ts:343:26:343:26 | T | T | +| tst.ts:344:10:344:16 | () => T | () => T | +| tst.ts:344:16:344:16 | T | T | +| tst.ts:345:10:345:27 | (value: T) => void | (value: T) => void | +| tst.ts:345:18:345:18 | T | T | +| tst.ts:345:24:345:27 | void | void | +| tst.ts:348:16:348:20 | State | State | +| tst.ts:348:16:348:28 | State | State | +| tst.ts:348:22:348:27 | number | number | | type_alias.ts:1:6:1:6 | B | boolean | | type_alias.ts:1:10:1:16 | boolean | boolean | | type_alias.ts:3:8:3:8 | B | boolean | @@ -783,6 +899,7 @@ referenceDefinition | E | type_definition_objects.ts:6:8:6:16 | enum E {} | | EnumWithOneMember | type_definitions.ts:18:26:18:31 | member | | Error | tst.ts:210:10:213:3 | interfa ... ng;\\n } | +| FirstString | tst.ts:332:3:335:16 | type Fi ... never; | | Foo | tst.ts:116:3:129:3 | class F ... }\\n } | | Foo | tst.ts:165:5:167:5 | interfa ... ;\\n } | | Foo | tst.ts:179:3:192:3 | class F ... \\n } | @@ -796,6 +913,8 @@ referenceDefinition | NonAbstractDummy | tst.ts:54:1:56:1 | interfa ... mber;\\n} | | Person | tst.ts:222:3:234:3 | class P ... }\\n } | | Shape | tst.ts:140:3:142:47 | type Sh ... mber }; | +| State | tst.ts:343:3:346:3 | interfa ... id;\\n } | +| State | tst.ts:343:3:346:3 | interfa ... id;\\n } | | Sub | tst.ts:97:3:101:3 | class S ... }\\n } | | Success | tst.ts:205:10:208:3 | interfa ... ng;\\n } | | Super | tst.ts:91:3:95:3 | class S ... }\\n } | @@ -852,16 +971,20 @@ abstractSignature unionIndex | 1 | 0 | 1 \| 2 | | 2 | 1 | 1 \| 2 | +| 42 | 1 | "hello" \| 42 | | "NumberContents" | 0 | "NumberContents" \| "StringContents" | | "StringContents" | 1 | "NumberContents" \| "StringContents" | | "a" | 0 | "a" \| "b" | | "a" | 1 | number \| "a" | +| "a" | 3 | number \| boolean \| "a" \| "b" | | "b" | 1 | "a" \| "b" | +| "b" | 4 | number \| boolean \| "a" \| "b" | | "bigint" | 2 | "string" \| "number" \| "bigint" \| "boolean" \| "s... | | "boolean" | 2 | keyof TypeMap | | "boolean" | 3 | "string" \| "number" \| "bigint" \| "boolean" \| "s... | | "circle" | 0 | "circle" \| "square" | | "function" | 7 | "string" \| "number" \| "bigint" \| "boolean" \| "s... | +| "hello" | 0 | "hello" \| 42 | | "number" | 1 | "string" \| "number" \| "bigint" \| "boolean" \| "s... | | "number" | 1 | keyof TypeMap | | "object" | 6 | "string" \| "number" \| "bigint" \| "boolean" \| "s... | @@ -871,6 +994,7 @@ unionIndex | "symbol" | 4 | "string" \| "number" \| "bigint" \| "boolean" \| "s... | | "undefined" | 5 | "string" \| "number" \| "bigint" \| "boolean" \| "s... | | Error | 1 | Success \| Error | +| Error | 1 | string \| Error | | Json[] | 5 | string \| number \| boolean \| { [property: string... | | Promise | 2 | boolean \| Promise | | PromiseLike | 1 | TResult1 \| PromiseLike | @@ -890,16 +1014,19 @@ unionIndex | false | 0 | boolean | | false | 0 | boolean \| Promise | | false | 1 | number \| boolean | +| false | 1 | number \| boolean \| "a" \| "b" | | false | 2 | string \| number \| boolean | | false | 2 | string \| number \| boolean \| { [property: string... | | number | 0 | number \| "a" | | number | 0 | number \| ValueOrArray[] | | number | 0 | number \| boolean | +| number | 0 | number \| boolean \| "a" \| "b" | | number | 1 | string \| number | | number | 1 | string \| number \| boolean | | number | 1 | string \| number \| boolean \| { [property: string... | | number | 1 | string \| number \| true | | string | 0 | VirtualNode \| { [key: string]: any; } | +| string | 0 | string \| Error | | string | 0 | string \| [string, { [key: string]: any; }, ...V... | | string | 0 | string \| number | | string | 0 | string \| number \| boolean | @@ -911,6 +1038,7 @@ unionIndex | true | 1 | boolean | | true | 1 | boolean \| Promise | | true | 2 | number \| boolean | +| true | 2 | number \| boolean \| "a" \| "b" | | true | 2 | string \| number \| true | | true | 3 | string \| number \| boolean | | true | 3 | string \| number \| boolean \| { [property: string... | diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tst.ts b/javascript/ql/test/library-tests/TypeScript/Types/tst.ts index 9fdb0d950c4..8d67f28ad5d 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/tst.ts +++ b/javascript/ql/test/library-tests/TypeScript/Types/tst.ts @@ -293,4 +293,84 @@ module TS46 { payload.toFixed(); // <- number } }; -} \ No newline at end of file +} + +function foo_47() { + const key = Symbol(); + + const numberOrString = Math.random() < 0.5 ? 42 : "hello"; + + let obj = { + [key]: numberOrString, + }; + + if (typeof obj[key] === "string") { + let str = obj[key]; // <- string + str.toUpperCase(); + } + + ////////// + + function f(arg: { + produce: (n: string) => T, + consume: (x: T) => void } + ): void {}; + + f({ + produce: n => n, // <- (n: string) => string + consume: x => x.toLowerCase() + }); + + /////////// + + const ErrorMap = Map; + + const errorMap = new ErrorMap(); // <- Map + + //////////// + + type FirstString = + T extends [infer S extends string, ...unknown[]] + ? S + : never; + + type F = FirstString<['a' | 'b', number, boolean]>; + + const a: F = 'a'; // <- 'a' | 'b' + + //////////// + + interface State { + get: () => T; + set: (value: T) => void; + } + + const state: State = { + get: () => 42, + set: (value) => { } + } + + const fortyTwo = state.get(); // <- number + + //////////// + + // This does not compile as of 2022-05-19 with typescript@4.7.1-rc, but it might + // work with the actually released version: + + // class Container { + // #data = "hello!"; + + // get data(): typeof this.#data { + // return this.#data; + // } + + // set data(value: typeof this.#data) { + // this.#data = value; + // } + // } + + // const c = new Container(); + + // const hello = c.data(); // <- string + +} From 67697e106643972d6debadcb46e69b127096e38f Mon Sep 17 00:00:00 2001 From: Stephan Brandauer Date: Thu, 19 May 2022 15:10:22 +0200 Subject: [PATCH 054/125] update meta information and release note for typescript 4.7 upgrade --- docs/codeql/support/reusables/versions-compilers.rst | 2 +- javascript/extractor/src/com/semmle/js/extractor/Main.java | 2 +- javascript/ql/lib/change-notes/2022-05-24-typescript-4-7.md | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 javascript/ql/lib/change-notes/2022-05-24-typescript-4-7.md diff --git a/docs/codeql/support/reusables/versions-compilers.rst b/docs/codeql/support/reusables/versions-compilers.rst index a5f68cb64e1..bc175c4e711 100644 --- a/docs/codeql/support/reusables/versions-compilers.rst +++ b/docs/codeql/support/reusables/versions-compilers.rst @@ -23,7 +23,7 @@ JavaScript,ECMAScript 2021 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [6]_" Python,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10",Not applicable,``.py`` Ruby [7]_,"up to 3.0.2",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``" - TypeScript [8]_,"2.6-4.6",Standard TypeScript compiler,"``.ts``, ``.tsx``" + TypeScript [8]_,"2.6-4.7",Standard TypeScript compiler,"``.ts``, ``.tsx``" .. container:: footnote-group diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java index 5b425ab8af9..e5196ae1181 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/Main.java +++ b/javascript/extractor/src/com/semmle/js/extractor/Main.java @@ -43,7 +43,7 @@ public class Main { * A version identifier that should be updated every time the extractor changes in such a way that * it may produce different tuples for the same file under the same {@link ExtractorConfig}. */ - public static final String EXTRACTOR_VERSION = "2022-02-22"; + public static final String EXTRACTOR_VERSION = "2022-05-24"; public static final Pattern NEWLINE = Pattern.compile("\n"); diff --git a/javascript/ql/lib/change-notes/2022-05-24-typescript-4-7.md b/javascript/ql/lib/change-notes/2022-05-24-typescript-4-7.md new file mode 100644 index 00000000000..16fe46c675f --- /dev/null +++ b/javascript/ql/lib/change-notes/2022-05-24-typescript-4-7.md @@ -0,0 +1,4 @@ +--- +category: majorAnalysis +--- +* Added support for TypeScript 4.7. From a5b11e88b42d3b46ac84709a3bc31423176ee428 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 19 May 2022 20:00:43 +0200 Subject: [PATCH 055/125] update doc to make it clear that moduleImport(..) does not refer to PyPI names Co-authored-by: Rasmus Wriedt Larsen --- python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll b/python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll index 1ced4d15ee9..2af91a69432 100644 --- a/python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll +++ b/python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll @@ -5,7 +5,8 @@ * - Use the `ModelInput` module to contribute new models. * - Use the `ModelOutput` module to access the model results in terms of API nodes. * - * The package name refers to a Pip package name. + * The package name refers to the top-level module the import comes from, and not a PyPI package. + * So for `from foo.bar import baz`, the package will be `foo`. */ private import python From 204e01fc2438854ff44691eb461108ad364d25e8 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 19 May 2022 21:03:59 +0200 Subject: [PATCH 056/125] change getNumArgument to only count positional arguments --- python/ql/lib/semmle/python/ApiGraphs.qll | 2 +- python/ql/test/library-tests/frameworks/data/test.expected | 3 ++- python/ql/test/library-tests/frameworks/data/test.py | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/python/ql/lib/semmle/python/ApiGraphs.qll b/python/ql/lib/semmle/python/ApiGraphs.qll index 33aabe35383..43bba85ba79 100644 --- a/python/ql/lib/semmle/python/ApiGraphs.qll +++ b/python/ql/lib/semmle/python/ApiGraphs.qll @@ -356,7 +356,7 @@ module API { } /** Gets the number of arguments of this call. Both positional and named arguments are counted. */ - int getNumArgument() { result = count([this.getArg(_), this.getArgByName(_)]) } + int getNumArgument() { result = count(this.getArg(_)) } } /** diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 08300f4d905..fcf94bb784e 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -47,7 +47,8 @@ isSink | test.py:86:8:86:60 | ControlFlowNode for Attribute() | test-sink | | test.py:87:8:87:67 | ControlFlowNode for Attribute() | test-sink | | test.py:89:21:89:23 | ControlFlowNode for one | test-sink | -| test.py:90:25:90:27 | ControlFlowNode for one | test-sink | +| test.py:91:21:91:23 | ControlFlowNode for one | test-sink | +| test.py:91:30:91:32 | ControlFlowNode for two | test-sink | | test.py:98:6:98:9 | ControlFlowNode for baz2 | test-sink | isSource | test.py:3:5:3:15 | ControlFlowNode for getSource() | test-source | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index f01b613ce40..6ff4271dca2 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -87,8 +87,8 @@ mySink(Steps.preserveAllButFirstArgument("foo", getSource())) # FLOW mySink(Steps.preserveAllButFirstArgument("foo", "bar", getSource())) # FLOW CallFilter.arityOne(one) # match -CallFilter.arityOne(one=one) # match -CallFilter.arityOne(one, two=two) # NO match +CallFilter.arityOne(one=one) # NO match +CallFilter.arityOne(one, two=two) # match - on both the named and positional arguments CallFilter.arityOne(one=one, two=two) # NO match from foo1.bar import baz1 From 813fbf27de510cca61cd65edc6ec277be34fff54 Mon Sep 17 00:00:00 2001 From: Stephan Brandauer Date: Fri, 20 May 2022 10:16:45 +0200 Subject: [PATCH 057/125] support for .mts and .cts file extensions --- .../com/semmle/js/dependencies/Fetcher.java | 5 +- .../semmle/js/extractor/FileExtractor.java | 2 +- .../TypeScript/Types/tests.expected | 278 ++++++++++-------- .../library-tests/TypeScript/Types/tst.ts | 160 +++++----- .../TypeScript/Types/tstModuleCJS.cts | 3 + .../TypeScript/Types/tstModuleES.mts | 3 + 6 files changed, 255 insertions(+), 196 deletions(-) create mode 100644 javascript/ql/test/library-tests/TypeScript/Types/tstModuleCJS.cts create mode 100644 javascript/ql/test/library-tests/TypeScript/Types/tstModuleES.mts diff --git a/javascript/extractor/src/com/semmle/js/dependencies/Fetcher.java b/javascript/extractor/src/com/semmle/js/dependencies/Fetcher.java index fa996f1b34e..d094f05653d 100644 --- a/javascript/extractor/src/com/semmle/js/dependencies/Fetcher.java +++ b/javascript/extractor/src/com/semmle/js/dependencies/Fetcher.java @@ -141,8 +141,9 @@ public class Fetcher { entryPath = entryPath.subpath(1, entryPath.getNameCount()); String filename = entryPath.getFileName().toString(); - if (!filename.endsWith(".d.ts") && !filename.equals("package.json")) { - continue; // Only extract .d.ts files and package.json + if (!filename.endsWith(".d.ts") && !filename.endsWith(".d.mts") && !filename.endsWith(".d.cts") + && !filename.equals("package.json")) { + continue; // Only extract .d.ts, .d.mts, .d.cts files, and package.json } relativePaths.add(entryPath); Path outputFile = destDir.resolve(entryPath); diff --git a/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java index 1e9b7d6ad84..bc30d83690d 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java @@ -203,7 +203,7 @@ public class FileExtractor { } }, - TYPESCRIPT(".ts", ".tsx") { + TYPESCRIPT(".ts", ".tsx", ".mts", ".cts") { @Override protected boolean contains(File f, String lcExt, ExtractorConfig config) { if (config.getTypeScriptMode() == TypeScriptMode.NONE) return false; diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tests.expected b/javascript/ql/test/library-tests/TypeScript/Types/tests.expected index 1c6d0182dcb..9a0585b9436 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/tests.expected +++ b/javascript/ql/test/library-tests/TypeScript/Types/tests.expected @@ -386,86 +386,122 @@ getExprType | tst.ts:293:7:293:21 | payload.toFixed | (fractionDigits?: number) => string | | tst.ts:293:7:293:23 | payload.toFixed() | string | | tst.ts:293:15:293:21 | toFixed | (fractionDigits?: number) => string | -| tst.ts:298:10:298:15 | foo_47 | () => void | -| tst.ts:299:9:299:11 | key | typeof key | -| tst.ts:299:15:299:20 | Symbol | SymbolConstructor | -| tst.ts:299:15:299:22 | Symbol() | typeof key | -| tst.ts:301:9:301:22 | numberOrString | "hello" \| 42 | -| tst.ts:301:26:301:29 | Math | Math | -| tst.ts:301:26:301:36 | Math.random | () => number | -| tst.ts:301:26:301:38 | Math.random() | number | -| tst.ts:301:26:301:44 | Math.random() < 0.5 | boolean | -| tst.ts:301:26:301:59 | Math.ra ... "hello" | "hello" \| 42 | -| tst.ts:301:31:301:36 | random | () => number | -| tst.ts:301:42:301:44 | 0.5 | 0.5 | -| tst.ts:301:48:301:49 | 42 | 42 | -| tst.ts:301:53:301:59 | "hello" | "hello" | -| tst.ts:303:7:303:9 | obj | { [key]: string \| number; } | -| tst.ts:303:13:305:3 | {\\n [ ... ng,\\n } | { [key]: string \| number; } | -| tst.ts:304:6:304:8 | key | typeof key | -| tst.ts:304:12:304:25 | numberOrString | "hello" \| 42 | -| tst.ts:307:7:307:21 | typeof obj[key] | "string" \| "number" \| "bigint" \| "boolean" \| "s... | -| tst.ts:307:7:307:34 | typeof ... string" | boolean | -| tst.ts:307:14:307:16 | obj | { [key]: string \| number; } | -| tst.ts:307:14:307:21 | obj[key] | string \| number | -| tst.ts:307:18:307:20 | key | typeof key | -| tst.ts:307:27:307:34 | "string" | "string" | -| tst.ts:308:9:308:11 | str | string | -| tst.ts:308:15:308:17 | obj | { [key]: string \| number; } | -| tst.ts:308:15:308:22 | obj[key] | string | -| tst.ts:308:19:308:21 | key | typeof key | -| tst.ts:309:5:309:7 | str | string | -| tst.ts:309:5:309:19 | str.toUpperCase | () => string | -| tst.ts:309:5:309:21 | str.toUpperCase() | string | -| tst.ts:309:9:309:19 | toUpperCase | () => string | -| tst.ts:314:12:314:12 | f | (arg: { produce: (n: string) => T; consume: ... | -| tst.ts:314:17:314:19 | arg | { produce: (n: string) => T; consume: (x: T) =>... | -| tst.ts:315:5:315:11 | produce | (n: string) => T | -| tst.ts:315:14:315:29 | (n: string) => T | (n: string) => T | -| tst.ts:315:15:315:15 | n | string | -| tst.ts:316:5:316:11 | consume | (x: T) => void | -| tst.ts:316:14:316:27 | (x: T) => void | (x: T) => void | -| tst.ts:316:15:316:15 | x | T | -| tst.ts:319:3:319:3 | f | (arg: { produce: (n: string) => T; consume: ... | -| tst.ts:319:3:322:4 | f({\\n ... ()\\n }) | void | -| tst.ts:319:5:322:3 | {\\n p ... e()\\n } | { produce: (n: string) => string; consume: (x: ... | -| tst.ts:320:5:320:11 | produce | (n: string) => string | -| tst.ts:320:14:320:14 | n | string | -| tst.ts:320:14:320:19 | n => n | (n: string) => string | -| tst.ts:320:19:320:19 | n | string | -| tst.ts:321:5:321:11 | consume | (x: string) => string | -| tst.ts:321:14:321:14 | x | string | -| tst.ts:321:14:321:33 | x => x.toLowerCase() | (x: string) => string | -| tst.ts:321:19:321:19 | x | string | -| tst.ts:321:19:321:31 | x.toLowerCase | () => string | -| tst.ts:321:19:321:33 | x.toLowerCase() | string | -| tst.ts:321:21:321:31 | toLowerCase | () => string | -| tst.ts:326:9:326:16 | ErrorMap | { new (entries?: readonly (readonly [string, Er... | -| tst.ts:326:20:326:22 | Map | MapConstructor | -| tst.ts:326:20:326:37 | Map | any | -| tst.ts:328:9:328:16 | errorMap | Map | -| tst.ts:328:20:328:33 | new ErrorMap() | Map | -| tst.ts:328:24:328:31 | ErrorMap | { new (entries?: readonly (readonly [string, Er... | -| tst.ts:339:9:339:9 | a | "a" \| "b" | -| tst.ts:339:16:339:18 | 'a' | "a" | -| tst.ts:344:5:344:7 | get | () => T | -| tst.ts:344:10:344:16 | () => T | () => T | -| tst.ts:345:5:345:7 | set | (value: T) => void | -| tst.ts:345:10:345:27 | (value: T) => void | (value: T) => void | -| tst.ts:345:11:345:15 | value | T | -| tst.ts:348:9:348:13 | state | State | -| tst.ts:348:32:351:3 | {\\n g ... { }\\n } | State | -| tst.ts:349:5:349:7 | get | () => number | -| tst.ts:349:10:349:17 | () => 42 | () => number | -| tst.ts:349:16:349:17 | 42 | 42 | -| tst.ts:350:5:350:7 | set | (value: number) => void | -| tst.ts:350:10:350:23 | (value) => { } | (value: number) => void | -| tst.ts:350:11:350:15 | value | number | -| tst.ts:353:9:353:16 | fortyTwo | number | -| tst.ts:353:20:353:24 | state | State | -| tst.ts:353:20:353:28 | state.get | () => number | -| tst.ts:353:20:353:30 | state.get() | number | -| tst.ts:353:26:353:28 | get | () => number | +| tst.ts:298:7:298:9 | key | typeof key | +| tst.ts:298:13:298:18 | Symbol | SymbolConstructor | +| tst.ts:298:13:298:20 | Symbol() | typeof key | +| tst.ts:300:7:300:20 | numberOrString | "hello" \| 42 | +| tst.ts:300:24:300:27 | Math | Math | +| tst.ts:300:24:300:34 | Math.random | () => number | +| tst.ts:300:24:300:36 | Math.random() | number | +| tst.ts:300:24:300:42 | Math.random() < 0.5 | boolean | +| tst.ts:300:24:300:57 | Math.ra ... "hello" | "hello" \| 42 | +| tst.ts:300:29:300:34 | random | () => number | +| tst.ts:300:40:300:42 | 0.5 | 0.5 | +| tst.ts:300:46:300:47 | 42 | 42 | +| tst.ts:300:51:300:57 | "hello" | "hello" | +| tst.ts:302:5:302:7 | obj | { [key]: string \| number; } | +| tst.ts:302:11:304:1 | {\\n [ke ... ring,\\n} | { [key]: string \| number; } | +| tst.ts:303:4:303:6 | key | typeof key | +| tst.ts:303:10:303:23 | numberOrString | "hello" \| 42 | +| tst.ts:306:5:306:19 | typeof obj[key] | "string" \| "number" \| "bigint" \| "boolean" \| "s... | +| tst.ts:306:5:306:32 | typeof ... string" | boolean | +| tst.ts:306:12:306:14 | obj | { [key]: string \| number; } | +| tst.ts:306:12:306:19 | obj[key] | string \| number | +| tst.ts:306:16:306:18 | key | typeof key | +| tst.ts:306:25:306:32 | "string" | "string" | +| tst.ts:307:7:307:9 | str | string | +| tst.ts:307:13:307:15 | obj | { [key]: string \| number; } | +| tst.ts:307:13:307:20 | obj[key] | string | +| tst.ts:307:17:307:19 | key | typeof key | +| tst.ts:308:3:308:5 | str | string | +| tst.ts:308:3:308:17 | str.toUpperCase | () => string | +| tst.ts:308:3:308:19 | str.toUpperCase() | string | +| tst.ts:308:7:308:17 | toUpperCase | () => string | +| tst.ts:313:10:313:10 | f | (arg: { produce: (n: string) => T; consume: ... | +| tst.ts:313:15:313:17 | arg | { produce: (n: string) => T; consume: (x: T) =>... | +| tst.ts:314:3:314:9 | produce | (n: string) => T | +| tst.ts:314:12:314:27 | (n: string) => T | (n: string) => T | +| tst.ts:314:13:314:13 | n | string | +| tst.ts:315:3:315:9 | consume | (x: T) => void | +| tst.ts:315:12:315:25 | (x: T) => void | (x: T) => void | +| tst.ts:315:13:315:13 | x | T | +| tst.ts:318:1:318:1 | f | (arg: { produce: (n: string) => T; consume: ... | +| tst.ts:318:1:321:2 | f({\\n p ... se()\\n}) | void | +| tst.ts:318:3:321:1 | {\\n pro ... ase()\\n} | { produce: (n: string) => string; consume: (x: ... | +| tst.ts:319:3:319:9 | produce | (n: string) => string | +| tst.ts:319:12:319:12 | n | string | +| tst.ts:319:12:319:17 | n => n | (n: string) => string | +| tst.ts:319:17:319:17 | n | string | +| tst.ts:320:3:320:9 | consume | (x: string) => string | +| tst.ts:320:12:320:12 | x | string | +| tst.ts:320:12:320:31 | x => x.toLowerCase() | (x: string) => string | +| tst.ts:320:17:320:17 | x | string | +| tst.ts:320:17:320:29 | x.toLowerCase | () => string | +| tst.ts:320:17:320:31 | x.toLowerCase() | string | +| tst.ts:320:19:320:29 | toLowerCase | () => string | +| tst.ts:325:7:325:14 | ErrorMap | { new (entries?: readonly (readonly [string, Er... | +| tst.ts:325:18:325:20 | Map | MapConstructor | +| tst.ts:325:18:325:35 | Map | any | +| tst.ts:327:7:327:14 | errorMap | Map | +| tst.ts:327:18:327:31 | new ErrorMap() | Map | +| tst.ts:327:22:327:29 | ErrorMap | { new (entries?: readonly (readonly [string, Er... | +| tst.ts:338:7:338:7 | a | "a" \| "b" | +| tst.ts:338:14:338:16 | 'a' | "a" | +| tst.ts:343:3:343:5 | get | () => T | +| tst.ts:343:8:343:14 | () => T | () => T | +| tst.ts:344:3:344:5 | set | (value: T) => void | +| tst.ts:344:8:344:25 | (value: T) => void | (value: T) => void | +| tst.ts:344:9:344:13 | value | T | +| tst.ts:347:7:347:11 | state | State | +| tst.ts:347:30:350:1 | {\\n get ... > { }\\n} | State | +| tst.ts:348:3:348:5 | get | () => number | +| tst.ts:348:8:348:15 | () => 42 | () => number | +| tst.ts:348:14:348:15 | 42 | 42 | +| tst.ts:349:3:349:5 | set | (value: number) => void | +| tst.ts:349:8:349:21 | (value) => { } | (value: number) => void | +| tst.ts:349:9:349:13 | value | number | +| tst.ts:352:7:352:14 | fortyTwo | number | +| tst.ts:352:18:352:22 | state | State | +| tst.ts:352:18:352:26 | state.get | () => number | +| tst.ts:352:18:352:28 | state.get() | number | +| tst.ts:352:24:352:26 | get | () => number | +| tst.ts:377:8:377:18 | tstModuleES | () => "a" \| "b" | +| tst.ts:377:25:377:43 | './tstModuleES.mjs' | any | +| tst.ts:379:1:379:7 | console | Console | +| tst.ts:379:1:379:11 | console.log | (...data: any[]) => void | +| tst.ts:379:1:379:26 | console ... leES()) | void | +| tst.ts:379:9:379:11 | log | (...data: any[]) => void | +| tst.ts:379:13:379:23 | tstModuleES | () => "a" \| "b" | +| tst.ts:379:13:379:25 | tstModuleES() | "a" \| "b" | +| tst.ts:381:10:381:21 | tstModuleCJS | () => "a" \| "b" | +| tst.ts:381:10:381:21 | tstModuleCJS | () => "a" \| "b" | +| tst.ts:381:30:381:49 | './tstModuleCJS.cjs' | any | +| tst.ts:383:1:383:7 | console | Console | +| tst.ts:383:1:383:11 | console.log | (...data: any[]) => void | +| tst.ts:383:1:383:27 | console ... eCJS()) | void | +| tst.ts:383:9:383:11 | log | (...data: any[]) => void | +| tst.ts:383:13:383:24 | tstModuleCJS | () => "a" \| "b" | +| tst.ts:383:13:383:26 | tstModuleCJS() | "a" \| "b" | +| 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 | +| tstModuleCJS.cts:2:12:2:24 | Math.random() | number | +| tstModuleCJS.cts:2:12:2:30 | Math.random() > 0.5 | boolean | +| tstModuleCJS.cts:2:12:2:42 | Math.ra ... ' : 'b' | "a" \| "b" | +| tstModuleCJS.cts:2:17:2:22 | random | () => number | +| tstModuleCJS.cts:2:28:2:30 | 0.5 | 0.5 | +| tstModuleCJS.cts:2:34:2:36 | 'a' | "a" | +| tstModuleCJS.cts:2:40:2:42 | 'b' | "b" | +| tstModuleES.mts:1:25:1:35 | tstModuleES | () => "a" \| "b" | +| tstModuleES.mts:2:12:2:15 | Math | Math | +| tstModuleES.mts:2:12:2:22 | Math.random | () => number | +| tstModuleES.mts:2:12:2:24 | Math.random() | number | +| tstModuleES.mts:2:12:2:30 | Math.random() > 0.5 | boolean | +| tstModuleES.mts:2:12:2:42 | Math.ra ... ' : 'b' | "a" \| "b" | +| tstModuleES.mts:2:17:2:22 | random | () => number | +| tstModuleES.mts:2:28:2:30 | 0.5 | 0.5 | +| tstModuleES.mts:2:34:2:36 | 'a' | "a" | +| tstModuleES.mts:2:40:2:42 | 'b' | "b" | | type_alias.ts:3:5:3:5 | b | boolean | | type_alias.ts:7:5:7:5 | c | ValueOrArray | | type_alias.ts:14:9:14:32 | [proper ... ]: Json | any | @@ -541,9 +577,9 @@ getTypeDefinitionType | tst.ts:265:3:269:3 | interfa ... an;\\n } | TypeMap | | tst.ts:271:3:276:7 | type Un ... }[P]; | UnionRecord

    | | tst.ts:289:3:289:63 | type Fu ... > void; | Func | -| tst.ts:332:3:335:16 | type Fi ... never; | FirstString | -| tst.ts:337:3:337:53 | type F ... lean]>; | "a" \| "b" | -| tst.ts:343:3:346:3 | interfa ... id;\\n } | State | +| tst.ts:331:1:334:14 | type Fi ... never; | FirstString | +| tst.ts:336:1:336:51 | type F ... lean]>; | "a" \| "b" | +| tst.ts:342:1:345:1 | interfa ... void;\\n} | State | | 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 | @@ -786,39 +822,45 @@ getTypeExprType | tst.ts:289:47:289:52 | string | string | | tst.ts:289:59:289:62 | void | void | | tst.ts:291:13:291:16 | Func | Func | -| tst.ts:314:14:314:14 | T | T | -| tst.ts:314:22:316:29 | {\\n p ... void } | { produce: (n: string) => T; consume: (x: T) =>... | -| tst.ts:315:14:315:29 | (n: string) => T | (n: string) => T | -| tst.ts:315:18:315:23 | string | string | -| tst.ts:315:29:315:29 | T | T | -| tst.ts:316:14:316:27 | (x: T) => void | (x: T) => void | -| tst.ts:316:18:316:18 | T | T | -| tst.ts:316:24:316:27 | void | void | -| tst.ts:317:6:317:9 | void | void | -| tst.ts:326:24:326:29 | string | string | -| tst.ts:326:32:326:36 | Error | Error | -| tst.ts:332:8:332:18 | FirstString | FirstString | -| tst.ts:332:20:332:20 | T | T | -| tst.ts:337:8:337:8 | F | "a" \| "b" | -| tst.ts:337:12:337:22 | FirstString | FirstString | -| tst.ts:337:12:337:52 | FirstSt ... olean]> | "a" \| "b" | -| tst.ts:337:24:337:51 | ['a' \| ... oolean] | ["a" \| "b", number, boolean] | -| tst.ts:337:25:337:27 | 'a' | "a" | -| tst.ts:337:25:337:33 | 'a' \| 'b' | "a" \| "b" | -| tst.ts:337:31:337:33 | 'b' | "b" | -| tst.ts:337:36:337:41 | number | number | -| tst.ts:337:44:337:50 | boolean | boolean | -| tst.ts:339:12:339:12 | F | "a" \| "b" | -| tst.ts:343:13:343:17 | State | State | -| tst.ts:343:26:343:26 | T | T | -| tst.ts:344:10:344:16 | () => T | () => T | +| tst.ts:313:12:313:12 | T | T | +| tst.ts:313:20:315:27 | {\\n pro ... void } | { produce: (n: string) => T; consume: (x: T) =>... | +| tst.ts:314:12:314:27 | (n: string) => T | (n: string) => T | +| tst.ts:314:16:314:21 | string | string | +| tst.ts:314:27:314:27 | T | T | +| tst.ts:315:12:315:25 | (x: T) => void | (x: T) => void | +| tst.ts:315:16:315:16 | T | T | +| tst.ts:315:22:315:25 | void | void | +| tst.ts:316:4:316:7 | void | void | +| tst.ts:325:22:325:27 | string | string | +| tst.ts:325:30:325:34 | Error | Error | +| tst.ts:331:6:331:16 | FirstString | FirstString | +| tst.ts:331:18:331:18 | T | T | +| tst.ts:336:6:336:6 | F | "a" \| "b" | +| tst.ts:336:10:336:20 | FirstString | FirstString | +| tst.ts:336:10:336:50 | FirstSt ... olean]> | "a" \| "b" | +| tst.ts:336:22:336:49 | ['a' \| ... oolean] | ["a" \| "b", number, boolean] | +| tst.ts:336:23:336:25 | 'a' | "a" | +| tst.ts:336:23:336:31 | 'a' \| 'b' | "a" \| "b" | +| tst.ts:336:29:336:31 | 'b' | "b" | +| tst.ts:336:34:336:39 | number | number | +| tst.ts:336:42:336:48 | boolean | boolean | +| tst.ts:338:10:338:10 | F | "a" \| "b" | +| tst.ts:342:11:342:15 | State | State | +| tst.ts:342:24:342:24 | T | T | +| tst.ts:343:8:343:14 | () => T | () => T | +| tst.ts:343:14:343:14 | T | T | +| tst.ts:344:8:344:25 | (value: T) => void | (value: T) => void | | tst.ts:344:16:344:16 | T | T | -| tst.ts:345:10:345:27 | (value: T) => void | (value: T) => void | -| tst.ts:345:18:345:18 | T | T | -| tst.ts:345:24:345:27 | void | void | -| tst.ts:348:16:348:20 | State | State | -| tst.ts:348:16:348:28 | State | State | -| tst.ts:348:22:348:27 | number | number | +| tst.ts:344:22:344:25 | void | void | +| tst.ts:347:14:347:18 | State | State | +| tst.ts:347:14:347:26 | State | State | +| tst.ts:347:20:347:25 | number | number | +| 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" | +| tstModuleES.mts:1:40:1:42 | 'a' | "a" | +| tstModuleES.mts:1:40:1:48 | 'a' \| 'b' | "a" \| "b" | +| tstModuleES.mts:1:46:1:48 | 'b' | "b" | | type_alias.ts:1:6:1:6 | B | boolean | | type_alias.ts:1:10:1:16 | boolean | boolean | | type_alias.ts:3:8:3:8 | B | boolean | @@ -899,7 +941,7 @@ referenceDefinition | E | type_definition_objects.ts:6:8:6:16 | enum E {} | | EnumWithOneMember | type_definitions.ts:18:26:18:31 | member | | Error | tst.ts:210:10:213:3 | interfa ... ng;\\n } | -| FirstString | tst.ts:332:3:335:16 | type Fi ... never; | +| FirstString | tst.ts:331:1:334:14 | type Fi ... never; | | Foo | tst.ts:116:3:129:3 | class F ... }\\n } | | Foo | tst.ts:165:5:167:5 | interfa ... ;\\n } | | Foo | tst.ts:179:3:192:3 | class F ... \\n } | @@ -913,8 +955,8 @@ referenceDefinition | NonAbstractDummy | tst.ts:54:1:56:1 | interfa ... mber;\\n} | | Person | tst.ts:222:3:234:3 | class P ... }\\n } | | Shape | tst.ts:140:3:142:47 | type Sh ... mber }; | -| State | tst.ts:343:3:346:3 | interfa ... id;\\n } | -| State | tst.ts:343:3:346:3 | interfa ... id;\\n } | +| State | tst.ts:342:1:345:1 | interfa ... void;\\n} | +| State | tst.ts:342:1:345:1 | interfa ... void;\\n} | | Sub | tst.ts:97:3:101:3 | class S ... }\\n } | | Success | tst.ts:205:10:208:3 | interfa ... ng;\\n } | | Super | tst.ts:91:3:95:3 | class S ... }\\n } | diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tst.ts b/javascript/ql/test/library-tests/TypeScript/Types/tst.ts index 8d67f28ad5d..278c8e08bac 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/tst.ts +++ b/javascript/ql/test/library-tests/TypeScript/Types/tst.ts @@ -295,82 +295,92 @@ module TS46 { }; } -function foo_47() { - const key = Symbol(); +const key = Symbol(); - const numberOrString = Math.random() < 0.5 ? 42 : "hello"; +const numberOrString = Math.random() < 0.5 ? 42 : "hello"; - let obj = { - [key]: numberOrString, - }; - - if (typeof obj[key] === "string") { - let str = obj[key]; // <- string - str.toUpperCase(); - } - - ////////// - - function f(arg: { - produce: (n: string) => T, - consume: (x: T) => void } - ): void {}; - - f({ - produce: n => n, // <- (n: string) => string - consume: x => x.toLowerCase() - }); - - /////////// - - const ErrorMap = Map; - - const errorMap = new ErrorMap(); // <- Map - - //////////// - - type FirstString = - T extends [infer S extends string, ...unknown[]] - ? S - : never; - - type F = FirstString<['a' | 'b', number, boolean]>; - - const a: F = 'a'; // <- 'a' | 'b' - - //////////// - - interface State { - get: () => T; - set: (value: T) => void; - } - - const state: State = { - get: () => 42, - set: (value) => { } - } - - const fortyTwo = state.get(); // <- number - - //////////// - - // This does not compile as of 2022-05-19 with typescript@4.7.1-rc, but it might - // work with the actually released version: - - // class Container { - // #data = "hello!"; - - // get data(): typeof this.#data { - // return this.#data; - // } - - // set data(value: typeof this.#data) { - // this.#data = value; - // } - // } - - // const c = new Container(); - - // const hello = c.data(); // <- string +let obj = { + [key]: numberOrString, +}; +if (typeof obj[key] === "string") { + let str = obj[key]; // <- string + str.toUpperCase(); } + +////////// + +function f(arg: { + produce: (n: string) => T, + consume: (x: T) => void } +): void {}; + +f({ + produce: n => n, // <- (n: string) => string + consume: x => x.toLowerCase() +}); + +/////////// + +const ErrorMap = Map; + +const errorMap = new ErrorMap(); // <- Map + +//////////// + +type FirstString = + T extends [infer S extends string, ...unknown[]] + ? S + : never; + +type F = FirstString<['a' | 'b', number, boolean]>; + +const a: F = 'a'; // <- 'a' | 'b' + +//////////// + +interface State { + get: () => T; + set: (value: T) => void; +} + +const state: State = { + get: () => 42, + set: (value) => { } +} + +const fortyTwo = state.get(); // <- number + +//////////// + +// This does not compile as of 2022-05-19 with typescript@4.7.1-rc, but it might +// work with the actually released version: + +// class Container { +// #data = "hello!"; + +// get data(): typeof this.#data { +// return this.#data; +// } + +// set data(value: typeof this.#data) { +// this.#data = value; +// } +// } + +// const c = new Container(); + +// const hello = c.data(); // <- string + +///////////////// + +import tstModuleES from './tstModuleES.mjs'; + +console.log(tstModuleES()); + +import { tstModuleCJS } from './tstModuleCJS.cjs'; + +console.log(tstModuleCJS()); + +///////////////// + diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tstModuleCJS.cts b/javascript/ql/test/library-tests/TypeScript/Types/tstModuleCJS.cts new file mode 100644 index 00000000000..c764c1e1243 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/Types/tstModuleCJS.cts @@ -0,0 +1,3 @@ +export function tstModuleCJS(): 'a' | 'b' { + return Math.random() > 0.5 ? 'a' : 'b'; +} diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tstModuleES.mts b/javascript/ql/test/library-tests/TypeScript/Types/tstModuleES.mts new file mode 100644 index 00000000000..cf735d1bb49 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/Types/tstModuleES.mts @@ -0,0 +1,3 @@ +export default function tstModuleES(): 'a' | 'b' { + return Math.random() > 0.5 ? 'a' : 'b'; +} From cb4b2e983b7dbf4ef322bc8774bb1b6bcf8f68b0 Mon Sep 17 00:00:00 2001 From: Stephan Brandauer Date: Fri, 20 May 2022 14:33:07 +0200 Subject: [PATCH 058/125] delete test of removed feature --- .../library-tests/TypeScript/Types/tst.ts | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tst.ts b/javascript/ql/test/library-tests/TypeScript/Types/tst.ts index 278c8e08bac..aa6024ad088 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/tst.ts +++ b/javascript/ql/test/library-tests/TypeScript/Types/tst.ts @@ -351,27 +351,6 @@ const state: State = { const fortyTwo = state.get(); // <- number -//////////// - -// This does not compile as of 2022-05-19 with typescript@4.7.1-rc, but it might -// work with the actually released version: - -// class Container { -// #data = "hello!"; - -// get data(): typeof this.#data { -// return this.#data; -// } - -// set data(value: typeof this.#data) { -// this.#data = value; -// } -// } - -// const c = new Container(); - -// const hello = c.data(); // <- string - ///////////////// import tstModuleES from './tstModuleES.mjs'; From d6abb2e6bd83e46392001dac143b6473af9e55f7 Mon Sep 17 00:00:00 2001 From: Stephan Brandauer Date: Fri, 20 May 2022 14:34:53 +0200 Subject: [PATCH 059/125] add new supported file types to versions-compilers.rst Co-authored-by: Erik Krogh Kristensen --- docs/codeql/support/reusables/versions-compilers.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/support/reusables/versions-compilers.rst b/docs/codeql/support/reusables/versions-compilers.rst index bc175c4e711..c524b16d459 100644 --- a/docs/codeql/support/reusables/versions-compilers.rst +++ b/docs/codeql/support/reusables/versions-compilers.rst @@ -23,7 +23,7 @@ JavaScript,ECMAScript 2021 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [6]_" Python,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10",Not applicable,``.py`` Ruby [7]_,"up to 3.0.2",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``" - TypeScript [8]_,"2.6-4.7",Standard TypeScript compiler,"``.ts``, ``.tsx``" + TypeScript [8]_,"2.6-4.7",Standard TypeScript compiler,"``.ts``, ``.tsx``, ``.mts``, ``.cts``" .. container:: footnote-group From cdceb66b07ededc3ad65b83e4edc3987e5794175 Mon Sep 17 00:00:00 2001 From: Stephan Brandauer Date: Fri, 20 May 2022 15:09:37 +0200 Subject: [PATCH 060/125] add test for moduleSuffixes --- .../test/library-tests/TypeScript/Types/tsconfig.json | 3 ++- .../ql/test/library-tests/TypeScript/Types/tst.ts | 10 ++++++++++ .../test/library-tests/TypeScript/Types/tstSuffixA.ts | 3 +++ .../library-tests/TypeScript/Types/tstSuffixB.ios.ts | 3 +++ .../test/library-tests/TypeScript/Types/tstSuffixB.ts | 3 +++ 5 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 javascript/ql/test/library-tests/TypeScript/Types/tstSuffixA.ts create mode 100644 javascript/ql/test/library-tests/TypeScript/Types/tstSuffixB.ios.ts create mode 100644 javascript/ql/test/library-tests/TypeScript/Types/tstSuffixB.ts diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tsconfig.json b/javascript/ql/test/library-tests/TypeScript/Types/tsconfig.json index f6aa3a1e752..2235cec7f7d 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/tsconfig.json +++ b/javascript/ql/test/library-tests/TypeScript/Types/tsconfig.json @@ -3,6 +3,7 @@ "module": "esnext", "target": "esnext", "lib": ["dom", "esnext"], - "resolveJsonModule": true + "resolveJsonModule": true, + "moduleSuffixes": [".ios", ""] } } diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tst.ts b/javascript/ql/test/library-tests/TypeScript/Types/tst.ts index aa6024ad088..ed8787112d3 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/tst.ts +++ b/javascript/ql/test/library-tests/TypeScript/Types/tst.ts @@ -363,3 +363,13 @@ console.log(tstModuleCJS()); ///////////////// +// test file resolution order (see tsconfig: moduleSuffixes setting) + +import * as A from './tstSuffixA'; + +console.log(A.resolvedFile()); // <- 'tstSuffixA.ts' + +import * as B from './tstSuffixB'; + +console.log(B.resolvedFile()); // <- 'tstSuffixB.ios.ts' + diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tstSuffixA.ts b/javascript/ql/test/library-tests/TypeScript/Types/tstSuffixA.ts new file mode 100644 index 00000000000..ffe0b811492 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/Types/tstSuffixA.ts @@ -0,0 +1,3 @@ +export function resolvedFile(): 'tstSuffixA.ts' { + return 'tstSuffixA.ts'; +} diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tstSuffixB.ios.ts b/javascript/ql/test/library-tests/TypeScript/Types/tstSuffixB.ios.ts new file mode 100644 index 00000000000..04463fc7699 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/Types/tstSuffixB.ios.ts @@ -0,0 +1,3 @@ +export function resolvedFile(): 'tstSuffixB.ios.ts' { + return 'tstSuffixB.ios.ts'; +} diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tstSuffixB.ts b/javascript/ql/test/library-tests/TypeScript/Types/tstSuffixB.ts new file mode 100644 index 00000000000..cdb26f8f614 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/Types/tstSuffixB.ts @@ -0,0 +1,3 @@ +export function resolvedFile(): 'tstSuffixB.ts' { + return 'tstSuffixB.ts'; +} From b6a4f4373730722f6f23cbd77cfb9478439b05bb Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 23 May 2022 18:51:33 +0200 Subject: [PATCH 061/125] expand qldoc for `getNumArgument` Co-authored-by: Rasmus Wriedt Larsen --- python/ql/lib/semmle/python/ApiGraphs.qll | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/python/ql/lib/semmle/python/ApiGraphs.qll b/python/ql/lib/semmle/python/ApiGraphs.qll index 43bba85ba79..11ad08f1341 100644 --- a/python/ql/lib/semmle/python/ApiGraphs.qll +++ b/python/ql/lib/semmle/python/ApiGraphs.qll @@ -355,7 +355,12 @@ module API { result.getAnImmediateUse() = this } - /** Gets the number of arguments of this call. Both positional and named arguments are counted. */ + /** + * Gets the number of positional arguments of this call. + * + * Note: This is used for `WithArity[]` in modeling-as-data, where we thought + * including keyword arguments didn't make much sense. + */ int getNumArgument() { result = count(this.getArg(_)) } } From f8281b43b16fd47909e1c6b66cda463b4b3ab28d Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 23 May 2022 19:58:48 +0200 Subject: [PATCH 062/125] autoformat --- python/ql/lib/semmle/python/ApiGraphs.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/lib/semmle/python/ApiGraphs.qll b/python/ql/lib/semmle/python/ApiGraphs.qll index 11ad08f1341..fcb89e5f866 100644 --- a/python/ql/lib/semmle/python/ApiGraphs.qll +++ b/python/ql/lib/semmle/python/ApiGraphs.qll @@ -358,7 +358,7 @@ module API { /** * Gets the number of positional arguments of this call. * - * Note: This is used for `WithArity[]` in modeling-as-data, where we thought + * Note: This is used for `WithArity[]` in modeling-as-data, where we thought * including keyword arguments didn't make much sense. */ int getNumArgument() { result = count(this.getArg(_)) } From 6a12864dab7d40cb32150cf9130c4a1f30638d78 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 29 Mar 2022 14:08:57 +0200 Subject: [PATCH 063/125] JS: Document how API graphs should be interpreted --- .../ql/lib/semmle/javascript/ApiGraphs.qll | 112 ++++++++++++++++-- 1 file changed, 104 insertions(+), 8 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index 51101e3182a..9aef10a64e4 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -2,11 +2,7 @@ * Provides an implementation of _API graphs_, which are an abstract representation of the API * surface used and/or defined by a code base. * - * The nodes of the API graph represent definitions and uses of API components. The edges are - * directed and labeled; they specify how the components represented by nodes relate to each other. - * For example, if one of the nodes represents a definition of an API function, then there - * will be nodes corresponding to the function's parameters, which are connected to the function - * node by edges labeled `parameter `. + * See `API::Node` for more in-depth documentation. */ import javascript @@ -14,12 +10,112 @@ private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps private import internal.CachedStages /** - * Provides classes and predicates for working with APIs defined or used in a database. + * Provides classes and predicates for working with the API boundary between the current + * codebase and external libraries. + * + * See `API::Node` for more in-depth documentation. */ module API { /** - * An abstract representation of a definition or use of an API component such as a function - * exported by an npm package, a parameter of such a function, or its result. + * A node in the API graph, representing a value that has crossed the boundary between this + * codebase and an external library. + * + * ### Basic usage + * + * API graphs are typically used to identify "API calls", that is, calls to an external function + * whose implementation is not necessarily part of the current codebase. + * + * The most basic use of API graphs is typically as follows: + * 1. Start with `API::moduleImport` for the relevant library. + * 2. Follow up with a chain of accessors such as `getMember` describing how to get to the relevant API function. + * 3. Map the resulting API graph nodes to data-flow nodes, using `getAnImmediateUse` or `getARhs`. + * + * For example, a simplified way to get arguments to `underscore.extend` would be + * ```codeql + * API::moduleImport("underscore").getMember("extend").getParameter(0).getARhs() + * ``` + * + * The most commonly used accessors are `getMember`, `getParameter`, and `getReturn`. + * + * ### API graph nodes + * + * There are two kinds of nodes in the API graphs, distinguished by who is "holding" the value: + * - **Use-nodes** represent values held by the current codebase, which came from an external library. + * (The current codebase is "using" a value that came from the library). + * - **Def-nodes** represent values held by the external library, which came from this codebase. + * (The current codebase "defines" the value seen by the library). + * + * API graph nodes are associated with data-flow nodes in the current codebase. + * (Since external libraries are not part of the database, there is no way to associate with concrete + * data-flow nodes from the external library). + * - **Use-nodes** are associated with data-flow nodes where a value enters the current codebase, + * such as the return value of a call to an external function. + * - **Def-nodes** are associated with data-flow nodes where a value leaves the current codebase, + * such as an argument passed in a call to an external function. + * + * + * ### Access paths and edge labels + * + * Nodes in the API graph nodes are associated with a set of access paths, describing a series of operations + * that may be performed to obtain that value. + * + * For example, the access path `API::moduleImport("lodash").getMember("extend")` represents the action of + * importing `lodash` and then accessing the member `extend` on the resulting object. + * It would be associated with an expression such as `require("lodash").extend`. + * + * Each edge in the graph is labelled by such an "operation". For an edge `A->B`, the type of the `A` node + * determines who is performing the operation, and the type of the `B` node determines who ends up holding + * the result: + * - An edge starting from a use-node describes what the current codebase is doing to a value that + * came from a library. + * - An edge starting from a def-node describes what the external library might do to a value that + * came from the current codebase. + * - An edge ending in a use-node means the result ends up in the current codebase (at its associated data-flow node). + * - An edge ending in a def-node means the result ends up in external code (its associated data-flow node is + * the place where it was "last seen" in the current codebase before flowing out) + * + * Because the implementation of the external library is not visible, it is not known exactly what operations + * it will perform on values that flow there. Instead, the edges starting from a def-node are operations that would + * lead to an observable effect within the current codebase; without knowing for certain if the library will actually perform + * those operations. (When constructing these edge, we assume the library is somewhat well-behaved). + * + * For example, given this snippet: + * ```js + * require('foo')(x => { doSomething(x) }) + * ``` + * A callback is passed to the external function `foo`. We can't know if `foo` will actually invoke this callback. + * But _if_ the library should decide to invoke the callback, then a value will flow into the current codebase via the `x` parameter. + * For that reason, an edge is generated representing the argument-passing operation that might be performed by `foo`. + * This edge is going from the def-node associated with the callback to the use-node associated with the parameter `x`. + * + * ### Thinking in operations versus code patterns + * + * Treating edges as "operations" helps avoid a pitfall in which library models become overly specific to certain code patterns. + * Consider the following two equivalent calls to `foo`: + * ```js + * const foo = require('foo'); + * + * foo({ + * myMethod(x) {...} + * }); + * + * foo({ + * get myMethod() { + * return function(x) {...} + * } + * }); + * ``` + * If `foo` calls `myMethod` on its first parameter, either of the `myMethod` implementations will be invoked. + * An indeed, the access path `API::moduleImport("foo").getParameter(0).getMember("myMethod").getParameter(0)` correctly + * identifies both `x` parameters. + * + * Observe how `getMember("myMethod")` behaves when the member is defined via a getter. When thinking in code patterns, + * it might seem obvious that `getMember` should have obtained a reference the getter method itself. + * But when seeing it as an access to `myMethod` performed by the library, we can deduce that the relevant expression + * on the client side is actually the return-value of the getter. + * + * Although one may think of API graphs as a tool to find certain program elements in the codebase, + * it can lead to some situations where intuition does not match what works best in practice. */ class Node extends Impl::TApiNode { /** From 82c35e6f659b33aa436d28dfedce891c8d812ea1 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Wed, 30 Mar 2022 16:01:53 +0200 Subject: [PATCH 064/125] Mention that the interaction and be with any external codebase --- javascript/ql/lib/semmle/javascript/ApiGraphs.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index 9aef10a64e4..654b62dd26b 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -18,7 +18,7 @@ private import internal.CachedStages module API { /** * A node in the API graph, representing a value that has crossed the boundary between this - * codebase and an external library. + * codebase and an external library (or in general, any external codebase). * * ### Basic usage * From 73baa49c5d69b4cb1ef78805a162ff7e7492e02d Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 30 Mar 2022 14:15:42 +0200 Subject: [PATCH 065/125] Update javascript/ql/lib/semmle/javascript/ApiGraphs.qll Co-authored-by: yoff --- javascript/ql/lib/semmle/javascript/ApiGraphs.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index 654b62dd26b..79aa8a2ee5c 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -110,7 +110,7 @@ module API { * identifies both `x` parameters. * * Observe how `getMember("myMethod")` behaves when the member is defined via a getter. When thinking in code patterns, - * it might seem obvious that `getMember` should have obtained a reference the getter method itself. + * it might seem obvious that `getMember` should have obtained a reference to the getter method itself. * But when seeing it as an access to `myMethod` performed by the library, we can deduce that the relevant expression * on the client side is actually the return-value of the getter. * From a7b73f44b2b5a65f093cc2a830605b7e37117ca9 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 30 Mar 2022 15:59:47 +0200 Subject: [PATCH 066/125] Update javascript/ql/lib/semmle/javascript/ApiGraphs.qll Co-authored-by: Calum Grant <42069085+calumgrant@users.noreply.github.com> --- javascript/ql/lib/semmle/javascript/ApiGraphs.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index 79aa8a2ee5c..89135485152 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -106,7 +106,7 @@ module API { * }); * ``` * If `foo` calls `myMethod` on its first parameter, either of the `myMethod` implementations will be invoked. - * An indeed, the access path `API::moduleImport("foo").getParameter(0).getMember("myMethod").getParameter(0)` correctly + * And indeed, the access path `API::moduleImport("foo").getParameter(0).getMember("myMethod").getParameter(0)` correctly * identifies both `x` parameters. * * Observe how `getMember("myMethod")` behaves when the member is defined via a getter. When thinking in code patterns, From 4c6192670efaaba9ecc9a9ac4d22df1062d6195d Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 5 Apr 2022 09:17:35 +0200 Subject: [PATCH 067/125] JS: Rename getAnImmediateUse -> getASource --- .../ql/lib/semmle/javascript/ApiGraphs.qll | 61 +++++++++++-------- .../ql/lib/semmle/javascript/JsonParsers.qll | 2 +- .../ql/lib/semmle/javascript/JsonSchema.qll | 2 +- .../semmle/javascript/JsonStringifiers.qll | 2 +- .../semmle/javascript/frameworks/Babel.qll | 2 +- .../semmle/javascript/frameworks/Cheerio.qll | 2 +- .../javascript/frameworks/ClassValidator.qll | 2 +- .../javascript/frameworks/ClientRequests.qll | 4 +- .../lib/semmle/javascript/frameworks/D3.qll | 6 +- .../semmle/javascript/frameworks/Electron.qll | 4 +- .../semmle/javascript/frameworks/Files.qll | 12 ++-- .../javascript/frameworks/FormParsers.qll | 6 +- .../semmle/javascript/frameworks/History.qll | 6 +- .../lib/semmle/javascript/frameworks/Knex.qll | 2 +- .../javascript/frameworks/LiveServer.qll | 2 +- .../semmle/javascript/frameworks/Logging.qll | 2 +- .../lib/semmle/javascript/frameworks/Nest.qll | 14 ++--- .../lib/semmle/javascript/frameworks/Next.qll | 2 +- .../javascript/frameworks/NodeJSLib.qll | 2 +- .../semmle/javascript/frameworks/Prettier.qll | 2 +- .../semmle/javascript/frameworks/Redux.qll | 28 ++++----- .../lib/semmle/javascript/frameworks/SQL.qll | 4 +- .../javascript/frameworks/Snapdragon.qll | 4 +- .../semmle/javascript/frameworks/SocketIO.qll | 8 +-- .../javascript/frameworks/Templating.qll | 2 +- .../frameworks/TorrentLibraries.qll | 6 +- .../javascript/frameworks/UriLibraries.qll | 2 +- .../lib/semmle/javascript/frameworks/Vue.qll | 18 +++--- .../lib/semmle/javascript/frameworks/Vuex.qll | 16 ++--- .../javascript/frameworks/XmlParsers.qll | 4 +- .../frameworks/data/ModelsAsData.qll | 4 +- .../heuristics/AdditionalSources.qll | 2 +- .../dataflow/DomBasedXssCustomizations.qll | 2 +- .../dataflow/ExceptionXssCustomizations.qll | 2 +- ...APIUsedWithUntrustedDataCustomizations.qll | 4 +- ...IndirectCommandInjectionCustomizations.qll | 2 +- .../security/dataflow/MissingRateLimiting.qll | 6 +- .../security/dataflow/RemoteFlowSources.qll | 2 +- .../dataflow/XssThroughDomCustomizations.qll | 2 +- .../dataflow/ZipSlipCustomizations.qll | 4 +- .../ql/src/meta/ApiGraphs/ApiGraphUseNodes.ql | 2 +- .../ql/test/ApiGraphs/VerifyAssertions.qll | 2 +- .../ql/test/ApiGraphs/typed/NodeOfType.ql | 2 +- .../ql/test/library-tests/Routing/test.ql | 2 +- .../frameworks/Express/MiddlewareFlow.qll | 2 +- .../library-tests/frameworks/data/test.ql | 2 +- 46 files changed, 141 insertions(+), 130 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index 89135485152..c4f2feaab67 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -119,41 +119,52 @@ module API { */ class Node extends Impl::TApiNode { /** - * Gets a data-flow node corresponding to a use of the API component represented by this node. + * Get a data-flow node where this value may flow after entering the current codebase. * - * For example, `require('fs').readFileSync` is a use of the function `readFileSync` from the - * `fs` module, and `require('fs').readFileSync(file)` is a use of the return of that function. - * - * This includes indirect uses found via data flow, meaning that in - * `f(obj.foo); function f(x) {};` both `obj.foo` and `x` are uses of the `foo` member from `obj`. - * - * As another example, in the assignment `exports.plusOne = (x) => x+1` the two references to - * `x` are uses of the first parameter of `plusOne`. + * This is similar to `getASource()` but additionally includes nodes that are transitively reachable by data flow. + * See `getASource()` for examples. */ pragma[inline] - DataFlow::Node getAUse() { + DataFlow::Node getAValueReachableFromSource() { exists(DataFlow::SourceNode src | Impl::use(this, src) | Impl::trackUseNode(src).flowsTo(result) ) } /** - * Gets an immediate use of the API component represented by this node. + * Get a data-flow node where this value enters the current codebase. * - * For example, `require('fs').readFileSync` is a an immediate use of the `readFileSync` member - * from the `fs` module. + * For example: + * ```js + * // API::moduleImport("fs").getASource() + * require('fs'); * - * Unlike `getAUse()`, this predicate only gets the immediate references, not the indirect uses - * found via data flow. This means that in `const x = fs.readFile` only `fs.readFile` is a reference - * to the `readFile` member of `fs`, neither `x` nor any node that `x` flows to is a reference to - * this API component. + * // API::moduleImport("fs").getMember("readFile").getASource() + * require('fs').readFile; + * + * // API::moduleImport("fs").getMember("readFile").getReturn().getASource() + * require('fs').readFile(); + * + * require('fs').readFile( + * filename, + * // 'y' matched by API::moduleImport("fs").getMember("readFile").getParameter(1).getParameter(0).getASource() + * y => { + * ... + * }); + * ``` */ - DataFlow::SourceNode getAnImmediateUse() { Impl::use(this, result) } + DataFlow::SourceNode getASource() { Impl::use(this, result) } + + /** DEPRECATED. This predicate has been renamed to `getASource`. */ + deprecated DataFlow::SourceNode getAnImmediateUse() { result = this.getASource() } + + /** DEPRECATED. This predicate has been renamed to `getAValueReachableFromSource`. */ + deprecated DataFlow::Node getAUse() { result = this.getAValueReachableFromSource() } /** * Gets a call to the function represented by this API component. */ - CallNode getACall() { result = this.getReturn().getAnImmediateUse() } + CallNode getACall() { result = this.getReturn().getASource() } /** * Gets a call to the function represented by this API component, @@ -168,7 +179,7 @@ module API { /** * Gets a `new` call to the function represented by this API component. */ - NewNode getAnInstantiation() { result = this.getInstance().getAnImmediateUse() } + NewNode getAnInstantiation() { result = this.getInstance().getASource() } /** * Gets an invocation (with our without `new`) to the function represented by this API component. @@ -430,7 +441,7 @@ module API { * In other words, the value of a use of `that` may flow into the right-hand side of a * definition of this node. */ - predicate refersTo(Node that) { this.getARhs() = that.getAUse() } + predicate refersTo(Node that) { this.getARhs() = that.getAValueReachableFromSource() } /** * Gets the data-flow node that gives rise to this node, if any. @@ -1274,8 +1285,8 @@ module API { API::Node callee; InvokeNode() { - this = callee.getReturn().getAnImmediateUse() or - this = callee.getInstance().getAnImmediateUse() or + this = callee.getReturn().getASource() or + this = callee.getInstance().getASource() or this = Impl::getAPromisifiedInvocation(callee, _, _) } @@ -1301,13 +1312,13 @@ module API { /** Gets the API node for the return value of this call. */ Node getReturn() { result = callee.getReturn() and - result.getAnImmediateUse() = this + result.getASource() = this } /** Gets the API node for the object constructed by this invocation. */ Node getInstance() { result = callee.getInstance() and - result.getAnImmediateUse() = this + result.getASource() = this } } diff --git a/javascript/ql/lib/semmle/javascript/JsonParsers.qll b/javascript/ql/lib/semmle/javascript/JsonParsers.qll index 35a227c3ad8..cd4a104679f 100644 --- a/javascript/ql/lib/semmle/javascript/JsonParsers.qll +++ b/javascript/ql/lib/semmle/javascript/JsonParsers.qll @@ -29,7 +29,7 @@ private class PlainJsonParserCall extends JsonParserCall { callee = DataFlow::moduleMember(["json3", "json5", "flatted", "teleport-javascript", "json-cycle"], "parse") or - callee = API::moduleImport("replicator").getInstance().getMember("decode").getAnImmediateUse() or + callee = API::moduleImport("replicator").getInstance().getMember("decode").getASource() or callee = DataFlow::moduleImport("parse-json") or callee = DataFlow::moduleImport("json-parse-better-errors") or callee = DataFlow::moduleImport("json-safe-parse") or diff --git a/javascript/ql/lib/semmle/javascript/JsonSchema.qll b/javascript/ql/lib/semmle/javascript/JsonSchema.qll index bf45bcdd7b4..b108bde91f6 100644 --- a/javascript/ql/lib/semmle/javascript/JsonSchema.qll +++ b/javascript/ql/lib/semmle/javascript/JsonSchema.qll @@ -184,7 +184,7 @@ module JsonSchema { override boolean getPolarity() { none() } override DataFlow::Node getAValidationResultAccess(boolean polarity) { - result = this.getReturn().getMember("error").getAnImmediateUse() and + result = this.getReturn().getMember("error").getASource() and polarity = false } } diff --git a/javascript/ql/lib/semmle/javascript/JsonStringifiers.qll b/javascript/ql/lib/semmle/javascript/JsonStringifiers.qll index 03ca152471f..fc6d1dfe1f5 100644 --- a/javascript/ql/lib/semmle/javascript/JsonStringifiers.qll +++ b/javascript/ql/lib/semmle/javascript/JsonStringifiers.qll @@ -14,7 +14,7 @@ class JsonStringifyCall extends DataFlow::CallNode { callee = DataFlow::moduleMember(["json3", "json5", "flatted", "teleport-javascript", "json-cycle"], "stringify") or - callee = API::moduleImport("replicator").getInstance().getMember("encode").getAnImmediateUse() or + callee = API::moduleImport("replicator").getInstance().getMember("encode").getASource() or callee = DataFlow::moduleImport([ "json-stringify-safe", "json-stable-stringify", "stringify-object", diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Babel.qll b/javascript/ql/lib/semmle/javascript/frameworks/Babel.qll index 3d061eb2aef..2134a84fb10 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Babel.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Babel.qll @@ -198,7 +198,7 @@ module Babel { .getMember(["transform", "transformSync", "transformAsync"]) .getACall() and pred = call.getArgument(0) and - succ = [call, call.getParameter(2).getParameter(0).getAnImmediateUse()] + succ = [call, call.getParameter(2).getParameter(0).getASource()] ) } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Cheerio.qll b/javascript/ql/lib/semmle/javascript/frameworks/Cheerio.qll index 7c8e4a040a4..fdc820861b1 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Cheerio.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Cheerio.qll @@ -14,7 +14,7 @@ module Cheerio { } /** Gets a reference to the `cheerio` function, possibly with a loaded DOM. */ - DataFlow::SourceNode cheerioRef() { result = cheerioApi().getAUse() } + DataFlow::SourceNode cheerioRef() { result = cheerioApi().getAValueReachableFromSource() } /** * A creation of `cheerio` object, a collection of virtual DOM elements diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ClassValidator.qll b/javascript/ql/lib/semmle/javascript/frameworks/ClassValidator.qll index 381451c393c..cff8da8aaf8 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/ClassValidator.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/ClassValidator.qll @@ -39,7 +39,7 @@ module ClassValidator { /** Holds if the given field has a decorator that sanitizes its value for the purpose of taint tracking. */ predicate isFieldSanitizedByDecorator(FieldDefinition field) { - field.getADecorator().getExpression().flow() = sanitizingDecorator().getReturn().getAUse() + field.getADecorator().getExpression().flow() = sanitizingDecorator().getReturn().getAValueReachableFromSource() } pragma[noinline] diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll b/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll index e86af94463f..b9054fdee41 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll @@ -265,7 +265,7 @@ module ClientRequest { or responseType = this.getResponseType() and promise = false and - result = this.getReturn().getPromisedError().getMember("response").getAnImmediateUse() + result = this.getReturn().getPromisedError().getMember("response").getASource() } } @@ -463,7 +463,7 @@ module ClientRequest { */ private API::Node netSocketInstantiation(DataFlow::NewNode socket) { result = API::moduleImport("net").getMember("Socket").getInstance() and - socket = result.getAnImmediateUse() + socket = result.getASource() } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/D3.qll b/javascript/ql/lib/semmle/javascript/frameworks/D3.qll index 1dda09dedb8..d8478002fc2 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/D3.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/D3.qll @@ -78,11 +78,11 @@ module D3 { private class D3DomValueSource extends DOM::DomValueSource::Range { D3DomValueSource() { - this = d3Selection().getMember("each").getReceiver().getAnImmediateUse() + this = d3Selection().getMember("each").getReceiver().getASource() or - this = d3Selection().getMember("node").getReturn().getAnImmediateUse() + this = d3Selection().getMember("node").getReturn().getASource() or - this = d3Selection().getMember("nodes").getReturn().getUnknownMember().getAnImmediateUse() + this = d3Selection().getMember("nodes").getReturn().getUnknownMember().getASource() } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Electron.qll b/javascript/ql/lib/semmle/javascript/frameworks/Electron.qll index 1b3ee3dfea5..f0ab6a75bc5 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Electron.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Electron.qll @@ -56,13 +56,13 @@ module Electron { } } - private API::Node browserObject() { result.getAnImmediateUse() instanceof NewBrowserObject } + private API::Node browserObject() { result.getASource() instanceof NewBrowserObject } /** * A data flow node whose value may originate from a browser object instantiation. */ private class BrowserObjectByFlow extends BrowserObject { - BrowserObjectByFlow() { browserObject().getAUse() = this } + BrowserObjectByFlow() { browserObject().getAValueReachableFromSource() = this } } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Files.qll b/javascript/ql/lib/semmle/javascript/frameworks/Files.qll index 0bad9367b7f..8420ade8039 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Files.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Files.qll @@ -89,7 +89,7 @@ private API::Node globbyFileNameSource() { * A file name or an array of file names from the `globby` library. */ private class GlobbyFileNameSource extends FileNameSource { - GlobbyFileNameSource() { this = globbyFileNameSource().getAnImmediateUse() } + GlobbyFileNameSource() { this = globbyFileNameSource().getASource() } } /** Gets a file name or an array of file names from the `fast-glob` library. */ @@ -116,7 +116,7 @@ private API::Node fastGlobFileName() { * A file name or an array of file names from the `fast-glob` library. */ private class FastGlobFileNameSource extends FileNameSource { - FastGlobFileNameSource() { this = fastGlobFileName().getAnImmediateUse() } + FastGlobFileNameSource() { this = fastGlobFileName().getASource() } } /** @@ -200,7 +200,7 @@ private class RecursiveReadDir extends FileSystemAccess, FileNameProducer, API:: override DataFlow::Node getAPathArgument() { result = this.getArgument(0) } - override DataFlow::Node getAFileName() { result = this.trackFileSource().getAnImmediateUse() } + override DataFlow::Node getAFileName() { result = this.trackFileSource().getASource() } private API::Node trackFileSource() { result = this.getParameter([1 .. 2]).getParameter(1) @@ -223,7 +223,7 @@ private module JsonFile { override DataFlow::Node getAPathArgument() { result = this.getArgument(0) } - override DataFlow::Node getADataNode() { result = this.trackRead().getAnImmediateUse() } + override DataFlow::Node getADataNode() { result = this.trackRead().getASource() } private API::Node trackRead() { this.getCalleeName() = "readFile" and @@ -272,7 +272,7 @@ private class LoadJsonFile extends FileSystemReadAccess, API::CallNode { override DataFlow::Node getAPathArgument() { result = this.getArgument(0) } - override DataFlow::Node getADataNode() { result = this.trackRead().getAnImmediateUse() } + override DataFlow::Node getADataNode() { result = this.trackRead().getASource() } private API::Node trackRead() { this.getCalleeName() = "sync" and result = this.getReturn() @@ -310,7 +310,7 @@ private class WalkDir extends FileNameProducer, FileSystemAccess, API::CallNode override DataFlow::Node getAPathArgument() { result = this.getArgument(0) } - override DataFlow::Node getAFileName() { result = this.trackFileSource().getAnImmediateUse() } + override DataFlow::Node getAFileName() { result = this.trackFileSource().getASource() } private API::Node trackFileSource() { not this.getCalleeName() = ["sync", "async"] and diff --git a/javascript/ql/lib/semmle/javascript/frameworks/FormParsers.qll b/javascript/ql/lib/semmle/javascript/frameworks/FormParsers.qll index c5fa208406f..37d20219ab8 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/FormParsers.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/FormParsers.qll @@ -15,7 +15,7 @@ private class BusBoyRemoteFlow extends RemoteFlowSource { .getMember("on") .getParameter(1) .getAParameter() - .getAnImmediateUse() + .getASource() } override string getSourceType() { result = "parsed user value from Busbuy" } @@ -49,12 +49,12 @@ private class MultipartyRemoteFlow extends RemoteFlowSource { MultipartyRemoteFlow() { exists(API::Node form | form = API::moduleImport("multiparty").getMember("Form").getInstance() | exists(API::CallNode parse | parse = form.getMember("parse").getACall() | - this = parse.getParameter(1).getAParameter().getAnImmediateUse() + this = parse.getParameter(1).getAParameter().getASource() ) or exists(API::CallNode on | on = form.getMember("on").getACall() | on.getArgument(0).mayHaveStringValue(["part", "file", "field"]) and - this = on.getParameter(1).getAParameter().getAnImmediateUse() + this = on.getParameter(1).getAParameter().getASource() ) ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/History.qll b/javascript/ql/lib/semmle/javascript/frameworks/History.qll index 200c111c6b0..3907b80af1d 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/History.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/History.qll @@ -40,11 +40,11 @@ module History { HistoryLibaryRemoteFlow() { exists(API::Node loc | loc = [getBrowserHistory(), getHashHistory()].getMember("location") | - this = loc.getMember("hash").getAnImmediateUse() and kind.isFragment() + this = loc.getMember("hash").getASource() and kind.isFragment() or - this = loc.getMember("pathname").getAnImmediateUse() and kind.isPath() + this = loc.getMember("pathname").getASource() and kind.isPath() or - this = loc.getMember("search").getAnImmediateUse() and kind.isQuery() + this = loc.getMember("search").getASource() and kind.isQuery() ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Knex.qll b/javascript/ql/lib/semmle/javascript/frameworks/Knex.qll index 6f2098f27c9..e768a9feaff 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Knex.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Knex.qll @@ -69,7 +69,7 @@ module Knex { private class KnexDatabaseAwait extends DatabaseAccess, DataFlow::ValueNode { KnexDatabaseAwait() { exists(AwaitExpr enclosingAwait | this = enclosingAwait.flow() | - enclosingAwait.getOperand() = knexObject().getAUse().asExpr() + enclosingAwait.getOperand() = knexObject().getAValueReachableFromSource().asExpr() ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/LiveServer.qll b/javascript/ql/lib/semmle/javascript/frameworks/LiveServer.qll index aa365680cd2..f842cf86628 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/LiveServer.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/LiveServer.qll @@ -12,7 +12,7 @@ private module LiveServer { class ServerDefinition extends HTTP::Servers::StandardServerDefinition { ServerDefinition() { this = DataFlow::moduleImport("live-server").asExpr() } - API::Node getImportNode() { result.getAnImmediateUse().asExpr() = this } + API::Node getImportNode() { result.getASource().asExpr() = this } } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll b/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll index aa79a35785b..993df7ff10a 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll @@ -352,7 +352,7 @@ private module Pino { // `pino` is installed as the "log" property on the request object in `Express` and similar libraries. // in `Hapi` the property is "logger". exists(HTTP::RequestExpr req, API::Node reqNode | - reqNode.getAnImmediateUse() = req.flow().getALocalSource() and + reqNode.getASource() = req.flow().getALocalSource() and result = reqNode.getMember(["log", "logger"]) ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll b/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll index bc2bf109fdf..02e2b4fbe9b 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll @@ -181,7 +181,7 @@ module NestJS { predicate hasGlobalValidationPipe(Folder folder) { exists(DataFlow::CallNode call | call.getCalleeName() = "useGlobalPipes" and - call.getArgument(0) = validationPipe().getInstance().getAUse() and + call.getArgument(0) = validationPipe().getInstance().getAValueReachableFromSource() and folder = call.getFile().getParentContainer() ) or @@ -193,7 +193,7 @@ module NestJS { .getAMember() .getMember("useFactory") .getReturn() - .getARhs() = validationPipe().getInstance().getAUse() and + .getARhs() = validationPipe().getInstance().getAValueReachableFromSource() and folder = decorator.getFile().getParentContainer() ) or @@ -204,7 +204,7 @@ module NestJS { * Holds if `param` is affected by a pipe that sanitizes inputs. */ private predicate hasSanitizingPipe(NestJSRequestInput param, boolean dependsOnType) { - param.getAPipe() = sanitizingPipe(dependsOnType).getAUse() + param.getAPipe() = sanitizingPipe(dependsOnType).getAValueReachableFromSource() or hasGlobalValidationPipe(param.getFile().getParentContainer()) and dependsOnType = true @@ -395,7 +395,7 @@ module NestJS { /** Gets a parameter with this decorator applied. */ DataFlow::ParameterNode getADecoratedParameter() { - result.getADecorator() = getReturn().getReturn().getAUse() + result.getADecorator() = getReturn().getReturn().getAValueReachableFromSource() } /** Gets a value returned by the decorator's callback, which becomes the value of the decorated parameter. */ @@ -427,7 +427,7 @@ module NestJS { private class ExpressRequestSource extends Express::RequestSource { ExpressRequestSource() { this.(DataFlow::ParameterNode).getADecorator() = - nestjs().getMember(["Req", "Request"]).getReturn().getAnImmediateUse() + nestjs().getMember(["Req", "Request"]).getReturn().getASource() or this = executionContext() @@ -435,7 +435,7 @@ module NestJS { .getReturn() .getMember("getRequest") .getReturn() - .getAnImmediateUse() + .getASource() } /** @@ -452,7 +452,7 @@ module NestJS { private class ExpressResponseSource extends Express::ResponseSource { ExpressResponseSource() { this.(DataFlow::ParameterNode).getADecorator() = - nestjs().getMember(["Res", "Response"]).getReturn().getAnImmediateUse() + nestjs().getMember(["Res", "Response"]).getReturn().getASource() } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Next.qll b/javascript/ql/lib/semmle/javascript/frameworks/Next.qll index 644754ce75e..d131e6b3674 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Next.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Next.qll @@ -252,6 +252,6 @@ module NextJS { .getParameter(0) .getParameter(0) .getMember("router") - .getAnImmediateUse() + .getASource() } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll b/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll index 2adc68f907a..3174cae8d2d 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll @@ -1070,7 +1070,7 @@ module NodeJSLib { */ private class EventEmitterSubClass extends DataFlow::ClassNode { EventEmitterSubClass() { - this.getASuperClassNode() = getAnEventEmitterImport().getAUse() or + this.getASuperClassNode() = getAnEventEmitterImport().getAValueReachableFromSource() or this.getADirectSuperClass() instanceof EventEmitterSubClass } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Prettier.qll b/javascript/ql/lib/semmle/javascript/frameworks/Prettier.qll index ec9e490159e..3b7accdaa26 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Prettier.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Prettier.qll @@ -22,7 +22,7 @@ private module Prettier { call = API::moduleImport("prettier").getMember("formatWithCursor").getACall() | pred = call.getArgument(0) and - succ = call.getReturn().getMember("formatted").getAnImmediateUse() + succ = call.getReturn().getMember("formatted").getASource() ) } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll b/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll index fae5a1c76d7..2ea6e564ed1 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll @@ -58,10 +58,10 @@ module Redux { */ class StoreCreation extends DataFlow::SourceNode instanceof StoreCreation::Range { /** Gets a reference to the store. */ - DataFlow::SourceNode ref() { result = asApiNode().getAUse() } + DataFlow::SourceNode ref() { result = asApiNode().getAValueReachableFromSource() } /** Gets an API node that refers to this store creation. */ - API::Node asApiNode() { result.getAnImmediateUse() = this } + API::Node asApiNode() { result.getASource() = this } /** Gets the data flow node holding the root reducer for this store. */ DataFlow::Node getReducerArg() { result = super.getReducerArg() } @@ -106,7 +106,7 @@ module Redux { private API::Node rootState() { result instanceof RootStateSource or - stateStep(rootState().getAUse(), result.getAnImmediateUse()) + stateStep(rootState().getAValueReachableFromSource(), result.getASource()) } /** @@ -120,7 +120,7 @@ module Redux { accessPath = joinAccessPaths(base, prop) ) or - stateStep(rootStateAccessPath(accessPath).getAUse(), result.getAnImmediateUse()) + stateStep(rootStateAccessPath(accessPath).getAValueReachableFromSource(), result.getASource()) } /** @@ -374,7 +374,7 @@ module Redux { CreateSliceReducer() { call = API::moduleImport("@reduxjs/toolkit").getMember("createSlice").getACall() and - this = call.getReturn().getMember("reducer").getAnImmediateUse() + this = call.getReturn().getMember("reducer").getASource() } private API::Node getABuilderRef() { @@ -386,7 +386,7 @@ module Redux { override DataFlow::Node getActionHandlerArg(DataFlow::Node actionType) { exists(string name | result = call.getParameter(0).getMember("reducers").getMember(name).getARhs() and - actionType = call.getReturn().getMember("actions").getMember(name).getAnImmediateUse() + actionType = call.getReturn().getMember("actions").getMember(name).getASource() ) or // Properties of 'extraReducers': @@ -445,7 +445,7 @@ module Redux { // x -> bindActionCreators({ x, ... }) exists(BindActionCreatorsCall bind, string prop | ref(t.continue()).flowsTo(bind.getParameter(0).getMember(prop).getARhs()) and - result = bind.getReturn().getMember(prop).getAnImmediateUse() + result = bind.getReturn().getMember(prop).getASource() ) or // x -> combineActions(x, ...) @@ -580,7 +580,7 @@ module Redux { MultiAction() { createActions = API::moduleImport("redux-actions").getMember("createActions").getACall() and - this = createActions.getReturn().getMember(name).getAnImmediateUse() + this = createActions.getReturn().getMember(name).getASource() } override DataFlow::FunctionNode getMiddlewareFunction(boolean async) { @@ -614,7 +614,7 @@ module Redux { CreateSliceAction() { call = API::moduleImport("@reduxjs/toolkit").getMember("createSlice").getACall() and - this = call.getReturn().getMember("actions").getMember(actionName).getAnImmediateUse() + this = call.getReturn().getMember("actions").getMember(actionName).getASource() } override string getTypeTag() { @@ -885,12 +885,12 @@ module Redux { accessPath = getAffectedStateAccessPath(reducer) | pred = function.getReturnNode() and - succ = rootStateAccessPath(accessPath).getAnImmediateUse() + succ = rootStateAccessPath(accessPath).getASource() or exists(string suffix, DataFlow::SourceNode base | base = [function.getParameter(0), function.getReturnNode().getALocalSource()] and pred = AccessPath::getAnAssignmentTo(base, suffix) and - succ = rootStateAccessPath(accessPath + "." + suffix).getAnImmediateUse() + succ = rootStateAccessPath(accessPath + "." + suffix).getASource() ) ) or @@ -901,7 +901,7 @@ module Redux { reducer.isRootStateHandler() and base = [function.getParameter(0), function.getReturnNode().getALocalSource()] and pred = AccessPath::getAnAssignmentTo(base, suffix) and - succ = rootStateAccessPath(suffix).getAnImmediateUse() + succ = rootStateAccessPath(suffix).getASource() ) } @@ -1205,7 +1205,7 @@ module Redux { // Selector functions may be given as an array exists(DataFlow::ArrayCreationNode array | array.flowsTo(getArgument(0)) and - result.getAUse() = array.getElement(i) + result.getAValueReachableFromSource() = array.getElement(i) ) } } @@ -1222,7 +1222,7 @@ module Redux { exists(CreateSelectorCall call, int index | call.getNumArgument() > 1 and pred = call.getSelectorFunction(index).getReturn().getARhs() and - succ = call.getLastParameter().getParameter(index).getAnImmediateUse() + succ = call.getLastParameter().getParameter(index).getASource() ) or // The result of the last callback is the final result diff --git a/javascript/ql/lib/semmle/javascript/frameworks/SQL.qll b/javascript/ql/lib/semmle/javascript/frameworks/SQL.qll index 03c78c2561f..f8f3fb08ff0 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/SQL.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/SQL.qll @@ -383,7 +383,7 @@ private module Sqlite { /** A call to a Sqlite query method. */ private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode { QueryCall() { - this = getAChainingQueryCall().getAnImmediateUse() + this = getAChainingQueryCall().getASource() or this = database().getMember("prepare").getACall() } @@ -440,7 +440,7 @@ private module MsSql { override TaggedTemplateExpr astNode; QueryTemplateExpr() { - mssql().getMember("query").getAUse() = DataFlow::valueNode(astNode.getTag()) + mssql().getMember("query").getAValueReachableFromSource() = DataFlow::valueNode(astNode.getTag()) } override DataFlow::Node getAResult() { diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Snapdragon.qll b/javascript/ql/lib/semmle/javascript/frameworks/Snapdragon.qll index d5692c3d2c2..2569d205473 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Snapdragon.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Snapdragon.qll @@ -27,7 +27,7 @@ private module Snapdragon { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { exists(string methodName, API::CallNode set, API::CallNode call, API::Node base | // the handler, registered with a call to `.set`. - set = getSetCall+(base.getMember(methodName + "r")).getAnImmediateUse() and + set = getSetCall+(base.getMember(methodName + "r")).getASource() and // the snapdragon instance. The API is chaining, you can also use the instance directly. base = API::moduleImport("snapdragon").getInstance() and methodName = ["parse", "compile"] and @@ -47,7 +47,7 @@ private module Snapdragon { or // for compiler handlers the input is the first parameter. methodName = "compile" and - succ = set.getParameter(1).getParameter(0).getAnImmediateUse() + succ = set.getParameter(1).getParameter(0).getASource() ) ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/SocketIO.qll b/javascript/ql/lib/semmle/javascript/frameworks/SocketIO.qll index df761420e29..7e0cc61c3ca 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/SocketIO.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/SocketIO.qll @@ -41,7 +41,7 @@ module SocketIO { class ServerObject extends SocketIOObject { API::Node node; - ServerObject() { node = newServer() and this = node.getAnImmediateUse() } + ServerObject() { node = newServer() and this = node.getASource() } /** Gets the Api node for this server. */ API::Node asApiNode() { result = node } @@ -81,7 +81,7 @@ module SocketIO { ) } - override DataFlow::SourceNode ref() { result = this.server().getAUse() } + override DataFlow::SourceNode ref() { result = this.server().getAValueReachableFromSource() } } /** A data flow node that may produce (that is, create or return) a socket.io server. */ @@ -119,7 +119,7 @@ module SocketIO { API::Node node; NamespaceBase() { - this = node.getAnImmediateUse() and + this = node.getASource() and exists(ServerObject srv | // namespace lookup on `srv` node = srv.asApiNode().getMember("sockets") and @@ -158,7 +158,7 @@ module SocketIO { ) } - override DataFlow::SourceNode ref() { result = this.namespace().getAUse() } + override DataFlow::SourceNode ref() { result = this.namespace().getAValueReachableFromSource() } } /** A data flow node that may produce a namespace object. */ diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll b/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll index c1e6cb342f1..b26d4e56339 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll @@ -718,7 +718,7 @@ module Templating { override TemplateSyntax getTemplateSyntax() { result.getAPackageName() = engine } override DataFlow::SourceNode getOutput() { - result = this.getParameter([1, 2]).getParameter(1).getAnImmediateUse() + result = this.getParameter([1, 2]).getParameter(1).getASource() or not exists(this.getParameter([1, 2]).getParameter(1)) and result = this diff --git a/javascript/ql/lib/semmle/javascript/frameworks/TorrentLibraries.qll b/javascript/ql/lib/semmle/javascript/frameworks/TorrentLibraries.qll index 1724e1174aa..a9ab45ba821 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/TorrentLibraries.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/TorrentLibraries.qll @@ -21,7 +21,7 @@ module ParseTorrent { node = mod().getReturn() or node = mod().getMember("remote").getParameter(1).getParameter(1) ) and - this = node.getAnImmediateUse() + this = node.getASource() } /** Gets the API node for this torrent object. */ @@ -29,7 +29,7 @@ module ParseTorrent { } /** Gets a data flow node referring to a parsed torrent. */ - DataFlow::SourceNode parsedTorrentRef() { result = any(ParsedTorrent t).asApiNode().getAUse() } + DataFlow::SourceNode parsedTorrentRef() { result = any(ParsedTorrent t).asApiNode().getAValueReachableFromSource() } /** * An access to user-controlled torrent information. @@ -38,7 +38,7 @@ module ParseTorrent { UserControlledTorrentInfo() { exists(API::Node read | read = any(ParsedTorrent t).asApiNode().getAMember() and - this = read.getAnImmediateUse() + this = read.getASource() | exists(string prop | not ( diff --git a/javascript/ql/lib/semmle/javascript/frameworks/UriLibraries.qll b/javascript/ql/lib/semmle/javascript/frameworks/UriLibraries.qll index 5357259b9eb..6e59884237d 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/UriLibraries.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/UriLibraries.qll @@ -190,7 +190,7 @@ module Querystringify { * Gets a data flow source node for member `name` of the querystringify library. */ DataFlow::SourceNode querystringifyMember(string name) { - result = querystringify().getMember(name).getAnImmediateUse() + result = querystringify().getMember(name).getASource() } /** Gets an API node referring to the `querystringify` module. */ diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll index d32db710fa8..0e7031bbe9a 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll @@ -41,7 +41,7 @@ module Vue { /** * Gets a reference to the 'Vue' object. */ - DataFlow::SourceNode vue() { result = vueLibrary().getAnImmediateUse() } + DataFlow::SourceNode vue() { result = vueLibrary().getASource() } /** Gets an API node referring to a component or `Vue`. */ private API::Node component() { @@ -176,7 +176,7 @@ module Vue { /** Gets a component which is extended by this one. */ Component getABaseComponent() { - result.getComponentRef().getAUse() = + result.getComponentRef().getAValueReachableFromSource() = getOwnOptions().getMember(["extends", "mixins"]).getARhs() } @@ -328,10 +328,10 @@ module Vue { } /** Gets an API node referring to an instance of this component. */ - API::Node getInstance() { result.getAnImmediateUse() = getABoundFunction().getReceiver() } + API::Node getInstance() { result.getASource() = getABoundFunction().getReceiver() } /** Gets a data flow node referring to an instance of this component. */ - DataFlow::SourceNode getAnInstanceRef() { result = getInstance().getAnImmediateUse() } + DataFlow::SourceNode getAnInstanceRef() { result = getInstance().getASource() } pragma[noinline] private DataFlow::PropWrite getAPropertyValueWrite(string name) { @@ -533,7 +533,7 @@ module Vue { // of the .vue file. exists(Import imprt | imprt.getImportedPath().resolve() = file and - result.getAnImmediateUse() = imprt.getImportedModuleNode() + result.getASource() = imprt.getImportedModuleNode() ) } @@ -695,7 +695,7 @@ module Vue { t.start() and ( exists(API::Node router | router = API::moduleImport("vue-router") | - result = router.getInstance().getMember("currentRoute").getAnImmediateUse() + result = router.getInstance().getMember("currentRoute").getASource() or result = router @@ -703,17 +703,17 @@ module Vue { .getMember(["beforeEach", "beforeResolve", "afterEach"]) .getParameter(0) .getParameter([0, 1]) - .getAnImmediateUse() + .getASource() or result = router .getParameter(0) .getMember("scrollBehavior") .getParameter([0, 1]) - .getAnImmediateUse() + .getASource() ) or - result = routeConfig().getMember("beforeEnter").getParameter([0, 1]).getAnImmediateUse() + result = routeConfig().getMember("beforeEnter").getParameter([0, 1]).getASource() or exists(Component c | result = c.getABoundFunction().getAFunctionValue().getReceiver().getAPropertyRead("$route") diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Vuex.qll b/javascript/ql/lib/semmle/javascript/frameworks/Vuex.qll index 8d062a447aa..5efef1271e7 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Vuex.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Vuex.qll @@ -116,7 +116,7 @@ module Vuex { /** Gets the Vue component in which the generated functions are installed. */ Vue::Component getVueComponent() { exists(DataFlow::ObjectLiteralNode obj | - obj.getASpreadProperty() = getReturn().getAUse() and + obj.getASpreadProperty() = getReturn().getAValueReachableFromSource() and result.getOwnOptions().getAMember().getARhs() = obj ) or @@ -154,12 +154,12 @@ module Vuex { /** Gets a property access that may receive the produced by a getter of the given name. */ private DataFlow::Node getterSucc(string name) { exists(string prefix, string prop | - result = storeRef(prefix).getMember("getters").getMember(prop).getAnImmediateUse() and + result = storeRef(prefix).getMember("getters").getMember(prop).getASource() and prop != "*" and name = prefix + prop ) or - result = getAMappedAccess("mapGetters", name).getAnImmediateUse() + result = getAMappedAccess("mapGetters", name).getASource() } /** Holds if `pred -> succ` is a step from a getter function to a relevant property access. */ @@ -238,7 +238,7 @@ module Vuex { .getMember(getStorePropForCommitKind(kind)) .getMember(prop) .getParameter(1) - .getAnImmediateUse() and + .getASource() and prop != "*" and name = prefix + prop ) @@ -296,7 +296,7 @@ module Vuex { result = stateRefByAccessPath(path).getARhs() or exists(ExtendCall call, string base, string prop | - call.getDestinationOperand() = stateRefByAccessPath(base).getAUse() and + call.getDestinationOperand() = stateRefByAccessPath(base).getAValueReachableFromSource() and result = call.getASourceOperand().getALocalSource().getAPropertyWrite(prop).getRhs() and path = appendToNamespace(base, prop) ) @@ -304,7 +304,7 @@ module Vuex { /** Gets a value that refers to the given access path of the state. */ DataFlow::Node stateMutationSucc(string path) { - result = stateRefByAccessPath(path).getAnImmediateUse() + result = stateRefByAccessPath(path).getASource() } /** Holds if `pred -> succ` is a step from state mutation to state access. */ @@ -336,7 +336,7 @@ module Vuex { predicate mapStateHelperStep(DataFlow::Node pred, DataFlow::Node succ) { exists(Vue::Component component, string name | pred = mapStateHelperPred(component, name) and - succ = pragma[only_bind_out](component).getInstance().getMember(name).getAnImmediateUse() + succ = pragma[only_bind_out](component).getInstance().getMember(name).getASource() ) } @@ -378,7 +378,7 @@ module Vuex { /** Gets a package that can be considered an entry point for a Vuex app. */ private PackageJson entryPointPackage() { - result = getPackageJson(storeRef().getAnImmediateUse().getFile()) + result = getPackageJson(storeRef().getASource().getFile()) or // Any package that imports a store-creating package is considered a potential entry point. packageDependsOn(result, entryPointPackage()) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/XmlParsers.qll b/javascript/ql/lib/semmle/javascript/frameworks/XmlParsers.qll index 41cecb36941..bd57a4ab1f8 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/XmlParsers.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/XmlParsers.qll @@ -100,7 +100,7 @@ module XML { } override DataFlow::Node getAResult() { - result = [doc(), element(), attr()].getAnImmediateUse() + result = [doc(), element(), attr()].getASource() or result = element().getMember(["name", "text"]).getACall() or @@ -286,7 +286,7 @@ module XML { .getReturn() .getMember(any(string s | s.matches("on%"))) .getAParameter() - .getAnImmediateUse() + .getASource() } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/ModelsAsData.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/ModelsAsData.qll index f8b2118a55f..06059ec2680 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/ModelsAsData.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/ModelsAsData.qll @@ -26,7 +26,7 @@ import Shared::ModelOutput as ModelOutput * A remote flow source originating from a CSV source row. */ private class RemoteFlowSourceFromCsv extends RemoteFlowSource { - RemoteFlowSourceFromCsv() { this = ModelOutput::getASourceNode("remote").getAnImmediateUse() } + RemoteFlowSourceFromCsv() { this = ModelOutput::getASourceNode("remote").getASource() } override string getSourceType() { result = "Remote flow" } } @@ -38,7 +38,7 @@ private predicate summaryStepNodes(DataFlow::Node pred, DataFlow::Node succ, str exists(API::Node predNode, API::Node succNode | Specific::summaryStep(predNode, succNode, kind) and pred = predNode.getARhs() and - succ = succNode.getAnImmediateUse() + succ = succNode.getASource() ) } diff --git a/javascript/ql/lib/semmle/javascript/heuristics/AdditionalSources.qll b/javascript/ql/lib/semmle/javascript/heuristics/AdditionalSources.qll index 35b1ac83e2d..40c86ed4855 100644 --- a/javascript/ql/lib/semmle/javascript/heuristics/AdditionalSources.qll +++ b/javascript/ql/lib/semmle/javascript/heuristics/AdditionalSources.qll @@ -58,7 +58,7 @@ class RemoteServerResponse extends HeuristicSource, RemoteFlowSource { */ private class RemoteFlowSourceFromDBAccess extends RemoteFlowSource, HeuristicSource { RemoteFlowSourceFromDBAccess() { - this = ModelOutput::getASourceNode("database-access-result").getAUse() or + this = ModelOutput::getASourceNode("database-access-result").getAValueReachableFromSource() or exists(DatabaseAccess dba | this = dba.getAResult()) } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/DomBasedXssCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/DomBasedXssCustomizations.qll index 93c9fa63a59..879a03ca029 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/DomBasedXssCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/DomBasedXssCustomizations.qll @@ -49,7 +49,7 @@ module DomBasedXss { or // A construction of a JSDOM object (server side DOM), where scripts are allowed. exists(DataFlow::NewNode instance | - instance = API::moduleImport("jsdom").getMember("JSDOM").getInstance().getAnImmediateUse() and + instance = API::moduleImport("jsdom").getMember("JSDOM").getInstance().getASource() and this = instance.getArgument(0) and instance.getOptionArgument(1, "runScripts").mayHaveStringValue("dangerously") ) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/ExceptionXssCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/ExceptionXssCustomizations.qll index 36fed7c2106..26c5ded744f 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/ExceptionXssCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/ExceptionXssCustomizations.qll @@ -62,7 +62,7 @@ module ExceptionXss { */ private class JsonSchemaValidationError extends Source { JsonSchemaValidationError() { - this = any(JsonSchema::Ajv::Instance i).getAValidationError().getAnImmediateUse() + this = any(JsonSchema::Ajv::Instance i).getAValidationError().getASource() or this = any(JsonSchema::Joi::JoiValidationErrorRead r).getAValidationResultAccess(_) } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll index a061fd5bc6f..fb1e4a903db 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll @@ -165,9 +165,9 @@ module ExternalApiUsedWithUntrustedData { not param = base.getReceiver() | result = param and - name = param.getAnImmediateUse().asExpr().(Parameter).getName() + name = param.getASource().asExpr().(Parameter).getName() or - param.getAnImmediateUse().asExpr() instanceof DestructuringPattern and + param.getASource().asExpr() instanceof DestructuringPattern and result = param.getMember(name) ) } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/IndirectCommandInjectionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/IndirectCommandInjectionCustomizations.qll index a410bda46b5..9a0fb9c4e2c 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/IndirectCommandInjectionCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/IndirectCommandInjectionCustomizations.qll @@ -74,7 +74,7 @@ module IndirectCommandInjection { ].getMember("parse").getACall() or // `require('commander').myCmdArgumentName` - this = commander().getAMember().getAnImmediateUse() + this = commander().getAMember().getASource() or // `require('commander').opt()` => `{a: ..., b: ...}` this = commander().getMember("opts").getACall() diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll index fc30c91018a..d5b2f11f1c1 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll @@ -153,7 +153,7 @@ abstract class RateLimitingMiddleware extends DataFlow::SourceNode { */ class ExpressRateLimit extends RateLimitingMiddleware { ExpressRateLimit() { - this = API::moduleImport("express-rate-limit").getReturn().getAnImmediateUse() + this = API::moduleImport("express-rate-limit").getReturn().getASource() } } @@ -162,7 +162,7 @@ class ExpressRateLimit extends RateLimitingMiddleware { */ class BruteForceRateLimit extends RateLimitingMiddleware { BruteForceRateLimit() { - this = API::moduleImport("express-brute").getInstance().getMember("prevent").getAnImmediateUse() + this = API::moduleImport("express-brute").getInstance().getMember("prevent").getASource() } } @@ -174,7 +174,7 @@ class BruteForceRateLimit extends RateLimitingMiddleware { */ class RouteHandlerLimitedByExpressLimiter extends RateLimitingMiddleware { RouteHandlerLimitedByExpressLimiter() { - this = API::moduleImport("express-limiter").getReturn().getReturn().getAnImmediateUse() + this = API::moduleImport("express-limiter").getReturn().getReturn().getASource() } override Routing::Node getRoutingNode() { diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/RemoteFlowSources.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/RemoteFlowSources.qll index ff026f3a3a4..5f5cf99bdd2 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/RemoteFlowSources.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/RemoteFlowSources.qll @@ -175,7 +175,7 @@ private class ExternalRemoteFlowSourceSpecEntryPoint extends API::EntryPoint { private class ExternalRemoteFlowSource extends RemoteFlowSource { RemoteFlowSourceAccessPath ap; - ExternalRemoteFlowSource() { Stages::Taint::ref() and this = ap.resolve().getAnImmediateUse() } + ExternalRemoteFlowSource() { Stages::Taint::ref() and this = ap.resolve().getASource() } override string getSourceType() { result = ap.getSourceType() } } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/XssThroughDomCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/XssThroughDomCustomizations.qll index b869b028902..924176d99c5 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/XssThroughDomCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/XssThroughDomCustomizations.qll @@ -209,7 +209,7 @@ module XssThroughDom { useForm = API::moduleImport("react-hook-form").getMember("useForm").getReturn() | this = - useForm.getMember("handleSubmit").getParameter(0).getParameter(0).getAnImmediateUse() + useForm.getMember("handleSubmit").getParameter(0).getParameter(0).getASource() or this = useForm.getMember("getValues").getACall() ) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/ZipSlipCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/ZipSlipCustomizations.qll index 3c4c5f66b75..2e417d018db 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/ZipSlipCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/ZipSlipCustomizations.qll @@ -103,7 +103,7 @@ module ZipSlip { class JSZipFilesSource extends Source instanceof DynamicPropertyAccess::EnumeratedPropName { JSZipFilesSource() { super.getSourceObject() = - API::moduleImport("jszip").getInstance().getMember("files").getAnImmediateUse() + API::moduleImport("jszip").getInstance().getMember("files").getASource() } } @@ -116,7 +116,7 @@ module ZipSlip { .getMember(["forEach", "filter"]) .getParameter(0) .getParameter(0) - .getAnImmediateUse() + .getASource() } } diff --git a/javascript/ql/src/meta/ApiGraphs/ApiGraphUseNodes.ql b/javascript/ql/src/meta/ApiGraphs/ApiGraphUseNodes.ql index 446df101364..8f6629cc624 100644 --- a/javascript/ql/src/meta/ApiGraphs/ApiGraphUseNodes.ql +++ b/javascript/ql/src/meta/ApiGraphs/ApiGraphUseNodes.ql @@ -11,4 +11,4 @@ import javascript import meta.MetaMetrics -select projectRoot(), count(any(API::Node nd).getAUse()) +select projectRoot(), count(any(API::Node nd).getAValueReachableFromSource()) diff --git a/javascript/ql/test/ApiGraphs/VerifyAssertions.qll b/javascript/ql/test/ApiGraphs/VerifyAssertions.qll index 9b5c0594a72..57e7c3d3a99 100644 --- a/javascript/ql/test/ApiGraphs/VerifyAssertions.qll +++ b/javascript/ql/test/ApiGraphs/VerifyAssertions.qll @@ -24,7 +24,7 @@ private DataFlow::Node getNode(API::Node nd, string kind) { result = nd.getARhs() or kind = "use" and - result = nd.getAUse() + result = nd.getAValueReachableFromSource() } private string getLoc(DataFlow::Node nd) { diff --git a/javascript/ql/test/ApiGraphs/typed/NodeOfType.ql b/javascript/ql/test/ApiGraphs/typed/NodeOfType.ql index 582385c802a..b703739ff04 100644 --- a/javascript/ql/test/ApiGraphs/typed/NodeOfType.ql +++ b/javascript/ql/test/ApiGraphs/typed/NodeOfType.ql @@ -1,4 +1,4 @@ import javascript from string mod, string tp -select mod, tp, API::Node::ofType(mod, tp).getAnImmediateUse() +select mod, tp, API::Node::ofType(mod, tp).getASource() diff --git a/javascript/ql/test/library-tests/Routing/test.ql b/javascript/ql/test/library-tests/Routing/test.ql index aa89c7212d2..2593f8263b3 100644 --- a/javascript/ql/test/library-tests/Routing/test.ql +++ b/javascript/ql/test/library-tests/Routing/test.ql @@ -9,7 +9,7 @@ class Taint extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node node) { node.(DataFlow::CallNode).getCalleeName() = "source" or - node = testInstance().getMember("getSource").getReturn().getAnImmediateUse() + node = testInstance().getMember("getSource").getReturn().getASource() } override predicate isSink(DataFlow::Node node) { diff --git a/javascript/ql/test/library-tests/frameworks/Express/MiddlewareFlow.qll b/javascript/ql/test/library-tests/frameworks/Express/MiddlewareFlow.qll index 76a21469f6b..e1a8e825099 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/MiddlewareFlow.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/MiddlewareFlow.qll @@ -1,3 +1,3 @@ import javascript -query DataFlow::Node dbUse() { result = API::moduleImport("@example/db").getInstance().getAUse() } +query DataFlow::Node dbUse() { result = API::moduleImport("@example/db").getInstance().getAValueReachableFromSource() } diff --git a/javascript/ql/test/library-tests/frameworks/data/test.ql b/javascript/ql/test/library-tests/frameworks/data/test.ql index bdc08e3706f..a73cccfd94c 100644 --- a/javascript/ql/test/library-tests/frameworks/data/test.ql +++ b/javascript/ql/test/library-tests/frameworks/data/test.ql @@ -62,7 +62,7 @@ class BasicTaintTracking extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source.(DataFlow::CallNode).getCalleeName() = "source" or - source = ModelOutput::getASourceNode("test-source").getAnImmediateUse() + source = ModelOutput::getASourceNode("test-source").getASource() } override predicate isSink(DataFlow::Node sink) { From 19a5db9f897dae8e8cbd2716943a2212678b4923 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 5 Apr 2022 09:33:16 +0200 Subject: [PATCH 068/125] JS: Rename getARhs -> getASink --- .../ql/lib/semmle/javascript/ApiGraphs.qll | 38 +++++++++++------- .../ql/lib/semmle/javascript/JsonSchema.qll | 2 +- .../javascript/frameworks/ClientRequests.qll | 6 +-- .../javascript/frameworks/Credentials.qll | 2 +- .../lib/semmle/javascript/frameworks/D3.qll | 2 +- .../javascript/frameworks/HttpProxy.qll | 12 +++--- .../semmle/javascript/frameworks/LdapJS.qll | 4 +- .../javascript/frameworks/LiveServer.qll | 2 +- .../semmle/javascript/frameworks/Markdown.qll | 4 +- .../lib/semmle/javascript/frameworks/Nest.qll | 4 +- .../semmle/javascript/frameworks/NoSQL.qll | 10 ++--- .../javascript/frameworks/NodeJSLib.qll | 8 ++-- .../javascript/frameworks/Puppeteer.qll | 2 +- .../semmle/javascript/frameworks/Redux.qll | 40 +++++++++---------- .../lib/semmle/javascript/frameworks/SQL.qll | 12 +++--- .../javascript/frameworks/Templating.qll | 10 ++--- .../javascript/frameworks/TrustedTypes.qll | 2 +- .../lib/semmle/javascript/frameworks/Vue.qll | 18 ++++----- .../lib/semmle/javascript/frameworks/Vuex.qll | 26 ++++++------ .../frameworks/data/ModelsAsData.qll | 2 +- .../data/internal/ApiGraphModelsSpecific.qll | 2 +- ...APIUsedWithUntrustedDataCustomizations.qll | 2 +- .../security/dataflow/MissingRateLimiting.qll | 2 +- .../dataflow/SqlInjectionCustomizations.qll | 2 +- .../dataflow/TaintedPathCustomizations.qll | 10 ++--- .../CWE-295/DisablingCertificateValidation.ql | 2 +- .../Security/CWE-352/MissingCsrfMiddleware.ql | 2 +- .../ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql | 2 +- .../ql/test/ApiGraphs/VerifyAssertions.qll | 2 +- .../ql/test/ApiGraphs/call-nodes/test.ql | 4 +- .../ql/test/library-tests/Routing/test.ql | 2 +- .../library-tests/frameworks/data/test.ql | 4 +- 32 files changed, 126 insertions(+), 116 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index c4f2feaab67..45864ec8ccc 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -187,26 +187,36 @@ module API { InvokeNode getAnInvocation() { result = this.getACall() or result = this.getAnInstantiation() } /** - * Gets a data-flow node corresponding to the right-hand side of a definition of the API - * component represented by this node. + * Get a data-flow node where this value leaves the current codebase and flows into an + * external library (or in general, any external codebase). * - * For example, in the assignment `exports.plusOne = (x) => x+1`, the function expression - * `(x) => x+1` is the right-hand side of the definition of the member `plusOne` of - * the enclosing module, and the expression `x+1` is the right-had side of the definition of - * its result. + * Concretely, this is either an argument passed to a call to external code, + * or the right-hand side of a property write on an object flows into such a call. * - * Note that for parameters, it is the arguments flowing into that parameter that count as - * right-hand sides of the definition, not the declaration of the parameter itself. - * Consequently, in `require('fs').readFileSync(file)`, `file` is the right-hand - * side of a definition of the first parameter of `readFileSync` from the `fs` module. + * For example: + * ```js + * // 'x' is matched by API::moduleImport("foo").getParameter(0).getASink() + * require('foo')(x); + * + * // 'x' is matched by API::moduleImport("foo").getParameter(0).getMember("prop").getASink() + * require('foo')({ + * prop: x + * }); + * ``` */ - DataFlow::Node getARhs() { Impl::rhs(this, result) } + DataFlow::Node getASink() { Impl::rhs(this, result) } /** * Gets a data-flow node that may interprocedurally flow to the right-hand side of a definition * of the API component represented by this node. */ - DataFlow::Node getAValueReachingRhs() { result = Impl::trackDefNode(this.getARhs()) } + DataFlow::Node getAValueReachingSink() { result = Impl::trackDefNode(this.getASink()) } + + /** DEPRECATED. This predicate has been renamed to `getASink`. */ + deprecated DataFlow::Node getARhs() { result = this.getASink() } + + /** DEPRECATED. This predicate has been renamed to `getAValueReachingSink`. */ + deprecated DataFlow::Node getAValueReachingRhs() { result = this.getAValueReachingSink() } /** * Gets a node representing member `m` of this API component. @@ -441,7 +451,7 @@ module API { * In other words, the value of a use of `that` may flow into the right-hand side of a * definition of this node. */ - predicate refersTo(Node that) { this.getARhs() = that.getAValueReachableFromSource() } + predicate refersTo(Node that) { this.getASink() = that.getAValueReachableFromSource() } /** * Gets the data-flow node that gives rise to this node, if any. @@ -1301,7 +1311,7 @@ module API { * Gets an API node where a RHS of the node is the `i`th argument to this call. */ pragma[noinline] - private Node getAParameterCandidate(int i) { result.getARhs() = this.getArgument(i) } + private Node getAParameterCandidate(int i) { result.getASink() = this.getArgument(i) } /** Gets the API node for a parameter of this invocation. */ Node getAParameter() { result = this.getParameter(_) } diff --git a/javascript/ql/lib/semmle/javascript/JsonSchema.qll b/javascript/ql/lib/semmle/javascript/JsonSchema.qll index b108bde91f6..4c6540efc13 100644 --- a/javascript/ql/lib/semmle/javascript/JsonSchema.qll +++ b/javascript/ql/lib/semmle/javascript/JsonSchema.qll @@ -134,7 +134,7 @@ module JsonSchema { .ref() .getMember(["addSchema", "validate", "compile", "compileAsync"]) .getParameter(0) - .getARhs() + .getASink() } } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll b/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll index b9054fdee41..c14d4047602 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll @@ -827,7 +827,7 @@ module ClientRequest { class ApolloClientRequest extends ClientRequest::Range, API::InvokeNode { ApolloClientRequest() { this = apolloUriCallee().getAnInvocation() } - override DataFlow::Node getUrl() { result = this.getParameter(0).getMember("uri").getARhs() } + override DataFlow::Node getUrl() { result = this.getParameter(0).getMember("uri").getASink() } override DataFlow::Node getHost() { none() } @@ -848,10 +848,10 @@ module ClientRequest { override DataFlow::Node getUrl() { result = this.getArgument(0) } - override DataFlow::Node getHost() { result = this.getParameter(0).getMember("host").getARhs() } + override DataFlow::Node getHost() { result = this.getParameter(0).getMember("host").getASink() } override DataFlow::Node getADataNode() { - result = form.getMember("append").getACall().getParameter(1).getARhs() + result = form.getMember("append").getACall().getParameter(1).getASink() } } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Credentials.qll b/javascript/ql/lib/semmle/javascript/frameworks/Credentials.qll index b6892b5aa49..925dba2305b 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Credentials.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Credentials.qll @@ -21,7 +21,7 @@ private class CredentialsFromModel extends CredentialsExpr { string kind; CredentialsFromModel() { - this = ModelOutput::getASinkNode("credentials[" + kind + "]").getARhs().asExpr() + this = ModelOutput::getASinkNode("credentials[" + kind + "]").getASink().asExpr() } override string getCredentialsKind() { result = kind } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/D3.qll b/javascript/ql/lib/semmle/javascript/frameworks/D3.qll index d8478002fc2..7ec3f0d95fe 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/D3.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/D3.qll @@ -71,7 +71,7 @@ module D3 { D3XssSink() { exists(API::Node htmlArg | htmlArg = d3Selection().getMember("html").getParameter(0) and - this = [htmlArg, htmlArg.getReturn()].getARhs() + this = [htmlArg, htmlArg.getReturn()].getASink() ) } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/HttpProxy.qll b/javascript/ql/lib/semmle/javascript/frameworks/HttpProxy.qll index 2467ca0973b..b879207c602 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/HttpProxy.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/HttpProxy.qll @@ -19,10 +19,10 @@ private module HttpProxy { .getACall() } - override DataFlow::Node getUrl() { result = getParameter(0).getMember("target").getARhs() } + override DataFlow::Node getUrl() { result = getParameter(0).getMember("target").getASink() } override DataFlow::Node getHost() { - result = getParameter(0).getMember("target").getMember("host").getARhs() + result = getParameter(0).getMember("target").getMember("host").getASink() } override DataFlow::Node getADataNode() { none() } @@ -49,10 +49,10 @@ private module HttpProxy { ) } - override DataFlow::Node getUrl() { result = getOptionsObject().getMember("target").getARhs() } + override DataFlow::Node getUrl() { result = getOptionsObject().getMember("target").getASink() } override DataFlow::Node getHost() { - result = getOptionsObject().getMember("target").getMember("host").getARhs() + result = getOptionsObject().getMember("target").getMember("host").getASink() } override DataFlow::Node getADataNode() { none() } @@ -78,8 +78,8 @@ private module HttpProxy { ProxyListenerCallback() { exists(API::CallNode call | call = any(CreateServerCall server).getReturn().getMember(["on", "once"]).getACall() and - call.getParameter(0).getARhs().mayHaveStringValue(event) and - this = call.getParameter(1).getARhs().getAFunctionValue() + call.getParameter(0).getASink().mayHaveStringValue(event) and + this = call.getParameter(1).getASink().getAFunctionValue() ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/LdapJS.qll b/javascript/ql/lib/semmle/javascript/frameworks/LdapJS.qll index 853b76afdda..9a50f2315fe 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/LdapJS.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/LdapJS.qll @@ -61,10 +61,10 @@ module LdapJS { SearchFilter() { options = ldapClient().getMember("search").getACall().getParameter(1) and - this = options.getARhs() + this = options.getASink() } - override DataFlow::Node getInput() { result = options.getMember("filter").getARhs() } + override DataFlow::Node getInput() { result = options.getMember("filter").getASink() } override DataFlow::Node getOutput() { result = this } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/LiveServer.qll b/javascript/ql/lib/semmle/javascript/frameworks/LiveServer.qll index f842cf86628..5272881c7d8 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/LiveServer.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/LiveServer.qll @@ -41,7 +41,7 @@ private module LiveServer { override DataFlow::SourceNode getARouteHandler() { exists(DataFlow::SourceNode middleware | - middleware = call.getParameter(0).getMember("middleware").getAValueReachingRhs() + middleware = call.getParameter(0).getMember("middleware").getAValueReachingSink() | result = middleware.getAMemberCall(["push", "unshift"]).getArgument(0).getAFunctionValue() or diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Markdown.qll b/javascript/ql/lib/semmle/javascript/frameworks/Markdown.qll index d131c70773a..d22d64b1adc 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Markdown.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Markdown.qll @@ -163,14 +163,14 @@ module Markdown { or call = API::moduleImport("markdown-it").getMember("Markdown").getAnInvocation() | - call.getParameter(0).getMember("html").getARhs().mayHaveBooleanValue(true) and + call.getParameter(0).getMember("html").getASink().mayHaveBooleanValue(true) and result = call.getReturn() ) or exists(API::CallNode call | call = markdownIt().getMember(["use", "set", "configure", "enable", "disable"]).getACall() and result = call.getReturn() and - not call.getParameter(0).getAValueReachingRhs() = + not call.getParameter(0).getAValueReachingSink() = DataFlow::moduleImport("markdown-it-sanitizer") ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll b/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll index 02e2b4fbe9b..0a8ba5ca632 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll @@ -193,7 +193,7 @@ module NestJS { .getAMember() .getMember("useFactory") .getReturn() - .getARhs() = validationPipe().getInstance().getAValueReachableFromSource() and + .getASink() = validationPipe().getInstance().getAValueReachableFromSource() and folder = decorator.getFile().getParentContainer() ) or @@ -399,7 +399,7 @@ module NestJS { } /** Gets a value returned by the decorator's callback, which becomes the value of the decorated parameter. */ - DataFlow::Node getResult() { result = getParameter(0).getReturn().getARhs() } + DataFlow::Node getResult() { result = getParameter(0).getReturn().getASink() } } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll b/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll index b04467a93ec..122d3b994be 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll @@ -20,7 +20,7 @@ deprecated module NoSQL = NoSql; * Gets a value that has been assigned to the "$where" property of an object that flows to `queryArg`. */ private DataFlow::Node getADollarWhereProperty(API::Node queryArg) { - result = queryArg.getMember("$where").getARhs() + result = queryArg.getMember("$where").getASink() } /** @@ -501,7 +501,7 @@ private module Mongoose { Credentials() { exists(string prop | - this = createConnection().getParameter(3).getMember(prop).getARhs().asExpr() + this = createConnection().getParameter(3).getMember(prop).getASink().asExpr() | prop = "user" and kind = "user name" or @@ -518,7 +518,7 @@ private module Mongoose { class MongoDBQueryPart extends NoSql::Query { MongooseFunction f; - MongoDBQueryPart() { this = f.getQueryArgument().getARhs().asExpr() } + MongoDBQueryPart() { this = f.getQueryArgument().getASink().asExpr() } override DataFlow::Node getACodeOperator() { result = getADollarWhereProperty(f.getQueryArgument()) @@ -540,7 +540,7 @@ private module Mongoose { override DataFlow::Node getAQueryArgument() { // NB: the complete information is not easily accessible for deeply chained calls - f.getQueryArgument().getARhs() = result + f.getQueryArgument().getASink() = result } override DataFlow::Node getAResult() { @@ -770,7 +770,7 @@ private module Redis { RedisKeyArgument() { exists(string method, int argIndex | QuerySignatures::argumentIsAmbiguousKey(method, argIndex) and - this = redis().getMember(method).getParameter(argIndex).getARhs().asExpr() + this = redis().getMember(method).getParameter(argIndex).getASink().asExpr() ) } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll b/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll index 3174cae8d2d..580109b6cc4 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll @@ -739,7 +739,7 @@ module NodeJSLib { methodName = ["execFile", "execFileSync", "spawn", "spawnSync", "fork"] ) and // all of the above methods take the command as their first argument - result = this.getParameter(0).getARhs() + result = this.getParameter(0).getASink() } override DataFlow::Node getACommandArgument() { result = this.getACommandArgument(_) } @@ -751,7 +751,7 @@ module NodeJSLib { override DataFlow::Node getArgumentList() { methodName = ["execFile", "execFileSync", "fork", "spawn", "spawnSync"] and // all of the above methods take the argument list as their second argument - result = this.getParameter(1).getARhs() + result = this.getParameter(1).getASink() } override predicate isSync() { methodName.matches("%Sync") } @@ -759,7 +759,7 @@ module NodeJSLib { override DataFlow::Node getOptionsArg() { not result.getALocalSource() instanceof DataFlow::FunctionNode and // looks like callback not result.getALocalSource() instanceof DataFlow::ArrayCreationNode and // looks like argumentlist - not result = this.getParameter(0).getARhs() and + not result = this.getParameter(0).getASink() and // fork/spawn and all sync methos always has options as the last argument if methodName.matches("fork%") or @@ -768,7 +768,7 @@ module NodeJSLib { then result = this.getLastArgument() else // the rest (exec/execFile) has the options argument as their second last. - result = this.getParameter(this.getNumArgument() - 2).getARhs() + result = this.getParameter(this.getNumArgument() - 2).getASink() } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Puppeteer.qll b/javascript/ql/lib/semmle/javascript/frameworks/Puppeteer.qll index b7bef025d23..94e98a5bdee 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Puppeteer.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Puppeteer.qll @@ -86,7 +86,7 @@ module Puppeteer { this = page().getMember(["addStyleTag", "addScriptTag"]).getACall() } - override DataFlow::Node getUrl() { result = getParameter(0).getMember("url").getARhs() } + override DataFlow::Node getUrl() { result = getParameter(0).getMember("url").getASink() } override DataFlow::Node getHost() { none() } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll b/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll index 2ea6e564ed1..fe05080c25c 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll @@ -94,7 +94,7 @@ module Redux { } override DataFlow::Node getReducerArg() { - result = getParameter(0).getMember("reducer").getARhs() + result = getParameter(0).getMember("reducer").getASink() } } } @@ -193,7 +193,7 @@ module Redux { CombineReducers() { this = combineReducers().getACall() } override DataFlow::Node getStateHandlerArg(string prop) { - result = getParameter(0).getMember(prop).getARhs() + result = getParameter(0).getMember(prop).getASink() } } @@ -207,7 +207,7 @@ module Redux { */ private class NestedCombineReducers extends DelegatingReducer, DataFlow::ObjectLiteralNode { NestedCombineReducers() { - this = combineReducers().getParameter(0).getAMember+().getAValueReachingRhs() + this = combineReducers().getParameter(0).getAMember+().getAValueReachingSink() } override DataFlow::Node getStateHandlerArg(string prop) { @@ -235,7 +235,7 @@ module Redux { override DataFlow::Node getActionHandlerArg(DataFlow::Node actionType) { exists(DataFlow::PropWrite write | - result = getParameter(0).getAMember().getARhs() and + result = getParameter(0).getAMember().getASink() and write.getRhs() = result and actionType = write.getPropertyNameExpr().flow() ) @@ -385,14 +385,14 @@ module Redux { override DataFlow::Node getActionHandlerArg(DataFlow::Node actionType) { exists(string name | - result = call.getParameter(0).getMember("reducers").getMember(name).getARhs() and + result = call.getParameter(0).getMember("reducers").getMember(name).getASink() and actionType = call.getReturn().getMember("actions").getMember(name).getASource() ) or // Properties of 'extraReducers': // { extraReducers: { [action]: reducer }} exists(DataFlow::PropWrite write | - result = call.getParameter(0).getMember("extraReducers").getAMember().getARhs() and + result = call.getParameter(0).getMember("extraReducers").getAMember().getASink() and write.getRhs() = result and actionType = write.getPropertyNameExpr().flow() ) @@ -444,7 +444,7 @@ module Redux { or // x -> bindActionCreators({ x, ... }) exists(BindActionCreatorsCall bind, string prop | - ref(t.continue()).flowsTo(bind.getParameter(0).getMember(prop).getARhs()) and + ref(t.continue()).flowsTo(bind.getParameter(0).getMember(prop).getASink()) and result = bind.getReturn().getMember(prop).getASource() ) or @@ -584,7 +584,7 @@ module Redux { } override DataFlow::FunctionNode getMiddlewareFunction(boolean async) { - result.flowsTo(createActions.getParameter(0).getMember(getTypeTag()).getARhs()) and + result.flowsTo(createActions.getParameter(0).getMember(getTypeTag()).getASink()) and async = false } @@ -619,7 +619,7 @@ module Redux { override string getTypeTag() { exists(string prefix | - call.getParameter(0).getMember("name").getARhs().mayHaveStringValue(prefix) and + call.getParameter(0).getMember("name").getASink().mayHaveStringValue(prefix) and result = prefix + "/" + actionName ) } @@ -640,7 +640,7 @@ module Redux { override DataFlow::FunctionNode getMiddlewareFunction(boolean async) { async = true and - result = getParameter(1).getAValueReachingRhs() + result = getParameter(1).getAValueReachingSink() } override string getTypeTag() { getArgument(0).mayHaveStringValue(result) } @@ -916,7 +916,7 @@ module Redux { */ private DataFlow::ObjectLiteralNode getAManuallyDispatchedValue(string actionType) { result.getAPropertyWrite("type").getRhs().mayHaveStringValue(actionType) and - result = getADispatchedValueNode().getAValueReachingRhs() + result = getADispatchedValueNode().getAValueReachingSink() } /** @@ -994,7 +994,7 @@ module Redux { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { exists(API::CallNode call | call = useSelector().getACall() and - pred = call.getParameter(0).getReturn().getARhs() and + pred = call.getParameter(0).getReturn().getASink() and succ = call ) } @@ -1046,19 +1046,19 @@ module Redux { // // const mapDispatchToProps = { foo } // - result = getMapDispatchToProps().getMember(name).getARhs() + result = getMapDispatchToProps().getMember(name).getASink() or // // const mapDispatchToProps = dispatch => ( { foo } ) // - result = getMapDispatchToProps().getReturn().getMember(name).getARhs() + result = getMapDispatchToProps().getReturn().getMember(name).getASink() or // Explicitly bound by bindActionCreators: // // const mapDispatchToProps = dispatch => bindActionCreators({ foo }, dispatch); // exists(BindActionCreatorsCall bind | - bind.flowsTo(getMapDispatchToProps().getReturn().getARhs()) and + bind.flowsTo(getMapDispatchToProps().getReturn().getASink()) and result = bind.getOptionArgument(0, name) ) } @@ -1115,12 +1115,12 @@ module Redux { override API::Node getMapStateToProps() { result = getAParameter() and - result.getARhs().asExpr().(Identifier).getName() = "mapStateToProps" + result.getASink().asExpr().(Identifier).getName() = "mapStateToProps" } override API::Node getMapDispatchToProps() { result = getAParameter() and - result.getARhs().asExpr().(Identifier).getName() = "mapDispatchToProps" + result.getASink().asExpr().(Identifier).getName() = "mapDispatchToProps" } } @@ -1130,7 +1130,7 @@ module Redux { private class StateToPropsStep extends StateStep { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { exists(ConnectCall call | - pred = call.getMapStateToProps().getReturn().getARhs() and + pred = call.getMapStateToProps().getReturn().getASink() and succ = call.getReactComponent().getADirectPropsAccess() ) } @@ -1221,13 +1221,13 @@ module Redux { // Return value of `i`th callback flows to the `i`th parameter of the last callback. exists(CreateSelectorCall call, int index | call.getNumArgument() > 1 and - pred = call.getSelectorFunction(index).getReturn().getARhs() and + pred = call.getSelectorFunction(index).getReturn().getASink() and succ = call.getLastParameter().getParameter(index).getASource() ) or // The result of the last callback is the final result exists(CreateSelectorCall call | - pred = call.getLastParameter().getReturn().getARhs() and + pred = call.getLastParameter().getReturn().getASink() and succ = call ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/SQL.qll b/javascript/ql/lib/semmle/javascript/frameworks/SQL.qll index f8f3fb08ff0..bd4e2b8c139 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/SQL.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/SQL.qll @@ -9,7 +9,7 @@ module SQL { abstract class SqlString extends Expr { } private class SqlStringFromModel extends SqlString { - SqlStringFromModel() { this = ModelOutput::getASinkNode("sql-injection").getARhs().asExpr() } + SqlStringFromModel() { this = ModelOutput::getASinkNode("sql-injection").getASink().asExpr() } } /** @@ -109,7 +109,7 @@ private module MySql { Credentials() { exists(API::Node callee, string prop | callee in [createConnection(), createPool()] and - this = callee.getParameter(0).getMember(prop).getARhs().asExpr() and + this = callee.getParameter(0).getMember(prop).getASink().asExpr() and ( prop = "user" and kind = "user name" or @@ -200,7 +200,7 @@ private module Postgres { QueryString() { this = any(QueryCall qc).getAQueryArgument().asExpr() or - this = API::moduleImport("pg-cursor").getParameter(0).getARhs().asExpr() + this = API::moduleImport("pg-cursor").getParameter(0).getASink().asExpr() } } @@ -210,9 +210,9 @@ private module Postgres { Credentials() { exists(string prop | - this = [newClient(), newPool()].getParameter(0).getMember(prop).getARhs().asExpr() + this = [newClient(), newPool()].getParameter(0).getMember(prop).getASink().asExpr() or - this = pgPromise().getParameter(0).getMember(prop).getARhs().asExpr() + this = pgPromise().getParameter(0).getMember(prop).getASink().asExpr() | prop = "user" and kind = "user name" or @@ -494,7 +494,7 @@ private module MsSql { or callee = mssql().getMember("ConnectionPool") ) and - this = callee.getParameter(0).getMember(prop).getARhs().asExpr() and + this = callee.getParameter(0).getMember(prop).getASink().asExpr() and ( prop = "user" and kind = "user name" or diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll b/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll index b26d4e56339..9e2315c13ee 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll @@ -233,7 +233,7 @@ module Templating { /** Gets an API node that may flow to `succ` through a template instantiation. */ private API::Node getTemplateInput(DataFlow::SourceNode succ) { exists(TemplateInstantiation inst, API::Node base, string name | - base.getARhs() = inst.getTemplateParamsNode() and + base.getASink() = inst.getTemplateParamsNode() and result = base.getMember(name) and succ = inst.getTemplateFile() @@ -244,7 +244,7 @@ module Templating { ) or exists(TemplateInstantiation inst, string accessPath | - result.getARhs() = inst.getTemplateParamForValue(accessPath) and + result.getASink() = inst.getTemplateParamForValue(accessPath) and succ = inst.getTemplateFile() .getAnImportedFile*() @@ -261,7 +261,7 @@ module Templating { private class TemplateInputStep extends DataFlow::SharedFlowStep { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { - getTemplateInput(succ).getARhs() = pred + getTemplateInput(succ).getASink() = pred } } @@ -321,8 +321,8 @@ module Templating { result = this.getStringValue() or exists(API::Node node | - this = node.getARhs() and - result = node.getAValueReachingRhs().getStringValue() + this = node.getASink() and + result = node.getAValueReachingSink().getStringValue() ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/TrustedTypes.qll b/javascript/ql/lib/semmle/javascript/frameworks/TrustedTypes.qll index 8335600f9db..222bcf0830c 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/TrustedTypes.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/TrustedTypes.qll @@ -38,7 +38,7 @@ module TrustedTypes { private class PolicyInputStep extends DataFlow::SharedFlowStep { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { exists(PolicyCreation policy, string method | - pred = policy.getReturn().getMember(method).getParameter(0).getARhs() and + pred = policy.getReturn().getMember(method).getParameter(0).getASink() and succ = policy.getPolicyCallback(method).getParameter(0) ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll index 0e7031bbe9a..cfabf6d291a 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll @@ -177,7 +177,7 @@ module Vue { /** Gets a component which is extended by this one. */ Component getABaseComponent() { result.getComponentRef().getAValueReachableFromSource() = - getOwnOptions().getMember(["extends", "mixins"]).getARhs() + getOwnOptions().getMember(["extends", "mixins"]).getASink() } /** @@ -200,7 +200,7 @@ module Vue { * Gets the options passed to the Vue object, such as the object literal `{...}` in `new Vue{{...})` * or the default export of a single-file component. */ - deprecated DataFlow::Node getOwnOptionsObject() { result = getOwnOptions().getARhs() } + deprecated DataFlow::Node getOwnOptionsObject() { result = getOwnOptions().getASink() } /** * Gets the class implementing this Vue component, if any. @@ -208,19 +208,19 @@ module Vue { * Specifically, this is a class annotated with `@Component` which flows to the options * object of this Vue component. */ - ClassComponent getAsClassComponent() { result = getOwnOptions().getAValueReachingRhs() } + ClassComponent getAsClassComponent() { result = getOwnOptions().getAValueReachingSink() } /** * Gets the node for option `name` for this component, not including * those from extended objects and mixins. */ - DataFlow::Node getOwnOption(string name) { result = getOwnOptions().getMember(name).getARhs() } + DataFlow::Node getOwnOption(string name) { result = getOwnOptions().getMember(name).getASink() } /** * Gets the node for option `name` for this component, including those from * extended objects and mixins. */ - DataFlow::Node getOption(string name) { result = getOptions().getMember(name).getARhs() } + DataFlow::Node getOption(string name) { result = getOptions().getMember(name).getASink() } /** * Gets a source node flowing into the option `name` of this component, including those from @@ -228,7 +228,7 @@ module Vue { */ pragma[nomagic] DataFlow::SourceNode getOptionSource(string name) { - result = getOptions().getMember(name).getAValueReachingRhs() + result = getOptions().getMember(name).getAValueReachingSink() } /** @@ -289,7 +289,7 @@ module Vue { DataFlow::FunctionNode getWatchHandler(string propName) { exists(API::Node propWatch | propWatch = getOptions().getMember("watch").getMember(propName) and - result = [propWatch, propWatch.getMember("handler")].getAValueReachingRhs() + result = [propWatch, propWatch.getMember("handler")].getAValueReachingSink() ) } @@ -322,7 +322,7 @@ module Vue { * Gets a node for a function that will be invoked with `this` bound to this component. */ DataFlow::FunctionNode getABoundFunction() { - result = getOptions().getAMember+().getAValueReachingRhs() + result = getOptions().getAMember+().getAValueReachingSink() or result = getAsClassComponent().getAnInstanceMember() } @@ -539,7 +539,7 @@ module Vue { override API::Node getOwnOptions() { // Use the entry point generated by `VueExportEntryPoint` - result.getARhs() = getModule().getDefaultOrBulkExport() + result.getASink() = getModule().getDefaultOrBulkExport() } override string toString() { result = file.toString() } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Vuex.qll b/javascript/ql/lib/semmle/javascript/frameworks/Vuex.qll index 5efef1271e7..ea6e64b4e79 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Vuex.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Vuex.qll @@ -75,7 +75,7 @@ module Vuex { or exists(API::CallNode call | call = vuex().getMember("createNamespacedHelpers").getACall() and - namespace = call.getParameter(0).getAValueReachingRhs().getStringValue() + "/" and + namespace = call.getParameter(0).getAValueReachingSink().getStringValue() + "/" and this = call.getReturn().getMember(helperName).getACall() ) ) @@ -88,7 +88,7 @@ module Vuex { pragma[noinline] string getNamespace() { getNumArgument() = 2 and - result = appendToNamespace(namespace, getParameter(0).getAValueReachingRhs().getStringValue()) + result = appendToNamespace(namespace, getParameter(0).getAValueReachingSink().getStringValue()) or getNumArgument() = 1 and result = namespace @@ -99,17 +99,17 @@ module Vuex { */ predicate hasMapping(string localName, string storeName) { // mapGetters('foo') - getLastParameter().getAValueReachingRhs().getStringValue() = localName and + getLastParameter().getAValueReachingSink().getStringValue() = localName and storeName = getNamespace() + localName or // mapGetters(['foo', 'bar']) - getLastParameter().getUnknownMember().getAValueReachingRhs().getStringValue() = localName and + getLastParameter().getUnknownMember().getAValueReachingSink().getStringValue() = localName and storeName = getNamespace() + localName or // mapGetters({foo: 'bar'}) storeName = getNamespace() + - getLastParameter().getMember(localName).getAValueReachingRhs().getStringValue() and + getLastParameter().getMember(localName).getAValueReachingSink().getStringValue() and localName != "*" // ignore special API graph member named "*" } @@ -117,10 +117,10 @@ module Vuex { Vue::Component getVueComponent() { exists(DataFlow::ObjectLiteralNode obj | obj.getASpreadProperty() = getReturn().getAValueReachableFromSource() and - result.getOwnOptions().getAMember().getARhs() = obj + result.getOwnOptions().getAMember().getASink() = obj ) or - result.getOwnOptions().getAMember().getARhs() = this + result.getOwnOptions().getAMember().getASink() = this } } @@ -146,7 +146,7 @@ module Vuex { /** Gets a value that is returned by a getter registered with the given name. */ private DataFlow::Node getterPred(string name) { exists(string prefix, string prop | - result = storeConfigObject(prefix).getMember("getters").getMember(prop).getReturn().getARhs() and + result = storeConfigObject(prefix).getMember("getters").getMember(prop).getReturn().getASink() and name = prefix + prop ) } @@ -212,19 +212,19 @@ module Vuex { commitCall = commitLikeFunctionRef(kind, prefix).getACall() | // commit('name', payload) - name = prefix + commitCall.getParameter(0).getAValueReachingRhs().getStringValue() and + name = prefix + commitCall.getParameter(0).getAValueReachingSink().getStringValue() and result = commitCall.getArgument(1) or // commit({type: 'name', ......}) name = prefix + - commitCall.getParameter(0).getMember("type").getAValueReachingRhs().getStringValue() and + commitCall.getParameter(0).getMember("type").getAValueReachingSink().getStringValue() and result = commitCall.getArgument(0) ) or // this.name(payload) // methods: {...mapMutations(['name'])} } - result = getAMappedAccess(getMapHelperForCommitKind(kind), name).getParameter(0).getARhs() + result = getAMappedAccess(getMapHelperForCommitKind(kind), name).getParameter(0).getASink() } /** Gets a node that refers the payload of a committed mutation with the given `name.` */ @@ -293,7 +293,7 @@ module Vuex { /** Gets a value that flows into the given access path of the state. */ DataFlow::Node stateMutationPred(string path) { - result = stateRefByAccessPath(path).getARhs() + result = stateRefByAccessPath(path).getASink() or exists(ExtendCall call, string base, string prop | call.getDestinationOperand() = stateRefByAccessPath(base).getAValueReachableFromSource() and @@ -325,7 +325,7 @@ module Vuex { exists(MapHelperCall call | call.getHelperName() = "mapState" and component = call.getVueComponent() and - result = call.getLastParameter().getMember(name).getReturn().getARhs() + result = call.getLastParameter().getMember(name).getReturn().getASink() ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/ModelsAsData.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/ModelsAsData.qll index 06059ec2680..3276a0feb30 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/ModelsAsData.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/ModelsAsData.qll @@ -37,7 +37,7 @@ private class RemoteFlowSourceFromCsv extends RemoteFlowSource { private predicate summaryStepNodes(DataFlow::Node pred, DataFlow::Node succ, string kind) { exists(API::Node predNode, API::Node succNode | Specific::summaryStep(predNode, succNode, kind) and - pred = predNode.getARhs() and + pred = predNode.getASink() and succ = succNode.getASource() ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll index a5e366a671f..6cc06e97488 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -151,7 +151,7 @@ API::Node getExtraSuccessorFromInvoke(API::InvokeNode node, AccessPathToken toke or token.getName() = "Argument" and token.getAnArgument() = "this" and - result.getARhs() = node.(DataFlow::CallNode).getReceiver() + result.getASink() = node.(DataFlow::CallNode).getReceiver() } /** diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll index fb1e4a903db..b627f8fadac 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll @@ -48,7 +48,7 @@ module ExternalApiUsedWithUntrustedData { } /** Holds if `node` corresponds to a deep object argument. */ - private predicate isDeepObjectSink(API::Node node) { node.getARhs() instanceof DeepObjectSink } + private predicate isDeepObjectSink(API::Node node) { node.getASink() instanceof DeepObjectSink } /** * A sanitizer for data flowing to an external API. diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll index d5b2f11f1c1..b762198a25f 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll @@ -211,7 +211,7 @@ class RateLimiterFlexibleRateLimiter extends DataFlow::FunctionNode { rateLimiterClass = API::moduleImport("rate-limiter-flexible").getMember(rateLimiterClassName) and rateLimiterConsume = rateLimiterClass.getInstance().getMember("consume") and request.getParameter() = getRouteHandlerParameter(this.getFunction(), "request") and - request.getAPropertyRead().flowsTo(rateLimiterConsume.getAParameter().getARhs()) + request.getAPropertyRead().flowsTo(rateLimiterConsume.getAParameter().getASink()) ) } } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll index e3bf02362a4..dd634de28ea 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll @@ -51,7 +51,7 @@ module SqlInjection { this = any(LdapJS::ClientCall call).getArgument(0) or // A search options object, which contains a filter and a baseDN. - this = any(LdapJS::SearchOptions opt).getARhs() + this = any(LdapJS::SearchOptions opt).getASink() or // A call to "parseDN", which parses a DN from a string. this = LdapJS::ldapjs().getMember("parseDN").getACall().getArgument(0) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll index c094d82163c..cb93bd39432 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll @@ -681,7 +681,7 @@ module TaintedPath { .getMember(["pdf", "screenshot"]) .getParameter(0) .getMember("path") - .getARhs() + .getASink() } } @@ -702,7 +702,7 @@ module TaintedPath { .getACall() .getParameter(1) .getMember("config") - .getARhs() + .getASink() } } @@ -716,7 +716,7 @@ module TaintedPath { .getMember(["readPackageAsync", "readPackageSync"]) .getParameter(0) .getMember("cwd") - .getARhs() + .getASink() } } @@ -726,8 +726,8 @@ module TaintedPath { private class ShellCwdSink extends TaintedPath::Sink { ShellCwdSink() { exists(SystemCommandExecution sys, API::Node opts | - opts.getARhs() = sys.getOptionsArg() and // assuming that an API::Node exists here. - this = opts.getMember("cwd").getARhs() + opts.getASink() = sys.getOptionsArg() and // assuming that an API::Node exists here. + this = opts.getMember("cwd").getASink() ) } } diff --git a/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.ql b/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.ql index 42861fcb9be..c8a36ed78f3 100644 --- a/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.ql +++ b/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.ql @@ -45,7 +45,7 @@ where or // the same thing, but with API-nodes if they happen to be available exists(API::Node tlsInvk | tlsInvk.getAnInvocation() = tlsInvocation() | - disable.getRhs() = tlsInvk.getAParameter().getMember("rejectUnauthorized").getARhs() + disable.getRhs() = tlsInvk.getAParameter().getMember("rejectUnauthorized").getASink() ) ) and disable.getRhs().(AnalyzedNode).getTheBooleanValue() = false diff --git a/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql b/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql index 52617ce675d..1a366c66710 100644 --- a/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql +++ b/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql @@ -143,7 +143,7 @@ API::CallNode passportAuthenticateCall() { */ API::CallNode nonSessionBasedAuthMiddleware() { result = passportAuthenticateCall() and - result.getParameter(1).getMember("session").getARhs().mayHaveBooleanValue(false) + result.getParameter(1).getMember("session").getASink().mayHaveBooleanValue(false) } /** diff --git a/javascript/ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql b/javascript/ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql index fcb98dc57af..0057d186e52 100644 --- a/javascript/ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql +++ b/javascript/ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql @@ -12,4 +12,4 @@ import javascript import meta.MetaMetrics -select projectRoot(), count(any(API::Node nd).getARhs()) +select projectRoot(), count(any(API::Node nd).getASink()) diff --git a/javascript/ql/test/ApiGraphs/VerifyAssertions.qll b/javascript/ql/test/ApiGraphs/VerifyAssertions.qll index 57e7c3d3a99..0a472c090a6 100644 --- a/javascript/ql/test/ApiGraphs/VerifyAssertions.qll +++ b/javascript/ql/test/ApiGraphs/VerifyAssertions.qll @@ -21,7 +21,7 @@ import javascript private DataFlow::Node getNode(API::Node nd, string kind) { kind = "def" and - result = nd.getARhs() + result = nd.getASink() or kind = "use" and result = nd.getAValueReachableFromSource() diff --git a/javascript/ql/test/ApiGraphs/call-nodes/test.ql b/javascript/ql/test/ApiGraphs/call-nodes/test.ql index cbea83fa4be..d4f086923a3 100644 --- a/javascript/ql/test/ApiGraphs/call-nodes/test.ql +++ b/javascript/ql/test/ApiGraphs/call-nodes/test.ql @@ -3,9 +3,9 @@ import javascript class FooCall extends API::CallNode { FooCall() { this = API::moduleImport("mylibrary").getMember("foo").getACall() } - DataFlow::Node getFirst() { result = getParameter(0).getMember("value").getARhs() } + DataFlow::Node getFirst() { result = getParameter(0).getMember("value").getASink() } - DataFlow::Node getSecond() { result = getParameter(1).getMember("value").getARhs() } + DataFlow::Node getSecond() { result = getParameter(1).getMember("value").getASink() } } query predicate values(FooCall call, int first, int second) { diff --git a/javascript/ql/test/library-tests/Routing/test.ql b/javascript/ql/test/library-tests/Routing/test.ql index 2593f8263b3..ea546c0722d 100644 --- a/javascript/ql/test/library-tests/Routing/test.ql +++ b/javascript/ql/test/library-tests/Routing/test.ql @@ -15,6 +15,6 @@ class Taint extends TaintTracking::Configuration { override predicate isSink(DataFlow::Node node) { node = any(DataFlow::CallNode call | call.getCalleeName() = "sink").getAnArgument() or - node = testInstance().getMember("getSink").getAParameter().getARhs() + node = testInstance().getMember("getSink").getAParameter().getASink() } } diff --git a/javascript/ql/test/library-tests/frameworks/data/test.ql b/javascript/ql/test/library-tests/frameworks/data/test.ql index a73cccfd94c..ee889cb9161 100644 --- a/javascript/ql/test/library-tests/frameworks/data/test.ql +++ b/javascript/ql/test/library-tests/frameworks/data/test.ql @@ -68,7 +68,7 @@ class BasicTaintTracking extends TaintTracking::Configuration { override predicate isSink(DataFlow::Node sink) { sink = any(DataFlow::CallNode call | call.getCalleeName() = "sink").getAnArgument() or - sink = ModelOutput::getASinkNode("test-sink").getARhs() + sink = ModelOutput::getASinkNode("test-sink").getASink() } } @@ -77,7 +77,7 @@ query predicate taintFlow(DataFlow::Node source, DataFlow::Node sink) { } query predicate isSink(DataFlow::Node node, string kind) { - node = ModelOutput::getASinkNode(kind).getARhs() + node = ModelOutput::getASinkNode(kind).getASink() } class SyntaxErrorTest extends ModelInput::SinkModelCsv { From ce9c3b3eb59c91ce294e714a3de13f216ab887e6 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 5 Apr 2022 09:37:30 +0200 Subject: [PATCH 069/125] JS: Also rename predicates on API::EntryPoint --- .../ql/lib/semmle/javascript/ApiGraphs.qll | 18 ++++++++++++------ .../ql/lib/semmle/javascript/frameworks/D3.qll | 4 ++-- .../semmle/javascript/frameworks/History.qll | 4 ++-- .../semmle/javascript/frameworks/Immutable.qll | 4 ++-- .../semmle/javascript/frameworks/Logging.qll | 4 ++-- .../lib/semmle/javascript/frameworks/Nest.qll | 4 ++-- .../lib/semmle/javascript/frameworks/Redux.qll | 4 ++-- .../javascript/frameworks/Templating.qll | 4 ++-- .../javascript/frameworks/TrustedTypes.qll | 4 ++-- .../lib/semmle/javascript/frameworks/Vue.qll | 12 ++++++------ .../data/internal/ApiGraphModelsSpecific.qll | 4 ++-- .../security/dataflow/RemoteFlowSources.qll | 4 ++-- .../custom-entry-point/VerifyAssertions.ql | 4 ++-- 13 files changed, 40 insertions(+), 34 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index 45864ec8ccc..d935819861a 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -562,11 +562,17 @@ module API { bindingset[this] EntryPoint() { any() } - /** Gets a data-flow node that uses this entry point. */ - abstract DataFlow::SourceNode getAUse(); + /** DEPRECATED. This predicate has been renamed to `getASource`. */ + deprecated DataFlow::SourceNode getAUse() { none() } - /** Gets a data-flow node that defines this entry point. */ - abstract DataFlow::Node getARhs(); + /** DEPRECATED. This predicate has been renamed to `getASink`. */ + deprecated DataFlow::SourceNode getARhs() { none() } + + /** Gets a data-flow node where a value enters the current codebase through this entry-point. */ + abstract DataFlow::SourceNode getASource(); + + /** Gets a data-flow node where a value leaves the current codebase through this entry-point. */ + abstract DataFlow::Node getASink(); /** Gets an API-node for this entry point. */ API::Node getANode() { result = root().getASuccessor(Label::entryPoint(this)) } @@ -684,7 +690,7 @@ module API { base = MkRoot() and exists(EntryPoint e | lbl = Label::entryPoint(e) and - rhs = e.getARhs() + rhs = e.getASink() ) or exists(string m, string prop | @@ -861,7 +867,7 @@ module API { base = MkRoot() and exists(EntryPoint e | lbl = Label::entryPoint(e) and - ref = e.getAUse() + ref = e.getASource() ) or // property reads diff --git a/javascript/ql/lib/semmle/javascript/frameworks/D3.qll b/javascript/ql/lib/semmle/javascript/frameworks/D3.qll index 7ec3f0d95fe..1c2e45f13af 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/D3.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/D3.qll @@ -9,9 +9,9 @@ module D3 { private class D3GlobalEntry extends API::EntryPoint { D3GlobalEntry() { this = "D3GlobalEntry" } - override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef("d3") } + override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("d3") } - override DataFlow::Node getARhs() { none() } + override DataFlow::Node getASink() { none() } } /** Gets an API node referring to the `d3` module. */ diff --git a/javascript/ql/lib/semmle/javascript/frameworks/History.qll b/javascript/ql/lib/semmle/javascript/frameworks/History.qll index 3907b80af1d..acb465ac2fe 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/History.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/History.qll @@ -8,9 +8,9 @@ module History { private class HistoryGlobalEntry extends API::EntryPoint { HistoryGlobalEntry() { this = "HistoryLibrary" } - override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef("HistoryLibrary") } + override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("HistoryLibrary") } - override DataFlow::Node getARhs() { none() } + override DataFlow::Node getASink() { none() } } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Immutable.qll b/javascript/ql/lib/semmle/javascript/frameworks/Immutable.qll index 39b0ea201bb..4e6d9f5d894 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Immutable.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Immutable.qll @@ -16,9 +16,9 @@ private module Immutable { private class ImmutableGlobalEntry extends API::EntryPoint { ImmutableGlobalEntry() { this = "ImmutableGlobalEntry" } - override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef("Immutable") } + override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("Immutable") } - override DataFlow::Node getARhs() { none() } + override DataFlow::Node getASink() { none() } } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll b/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll index 993df7ff10a..0d3a493d8e2 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll @@ -35,9 +35,9 @@ private module Console { private class ConsoleGlobalEntry extends API::EntryPoint { ConsoleGlobalEntry() { this = "ConsoleGlobalEntry" } - override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef("console") } + override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("console") } - override DataFlow::Node getARhs() { none() } + override DataFlow::Node getASink() { none() } } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll b/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll index 0a8ba5ca632..89ec6334ba2 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll @@ -140,11 +140,11 @@ module NestJS { private class ValidationNodeEntry extends API::EntryPoint { ValidationNodeEntry() { this = "ValidationNodeEntry" } - override DataFlow::SourceNode getAUse() { + override DataFlow::SourceNode getASource() { result.(DataFlow::ClassNode).getName() = "ValidationPipe" } - override DataFlow::Node getARhs() { none() } + override DataFlow::Node getASink() { none() } } /** Gets an API node referring to the constructor of `ValidationPipe` */ diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll b/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll index fe05080c25c..50cbd2cdf0e 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll @@ -1096,9 +1096,9 @@ module Redux { private class HeuristicConnectEntryPoint extends API::EntryPoint { HeuristicConnectEntryPoint() { this = "react-redux-connect" } - override DataFlow::Node getARhs() { none() } + override DataFlow::Node getASink() { none() } - override DataFlow::SourceNode getAUse() { + override DataFlow::SourceNode getASource() { exists(DataFlow::CallNode call | call.getAnArgument().asExpr().(Identifier).getName() = ["mapStateToProps", "mapDispatchToProps"] and diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll b/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll index 9e2315c13ee..9227fab3544 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll @@ -657,11 +657,11 @@ module Templating { private class IncludeFunctionAsEntryPoint extends API::EntryPoint { IncludeFunctionAsEntryPoint() { this = "IncludeFunctionAsEntryPoint" } - override DataFlow::SourceNode getAUse() { + override DataFlow::SourceNode getASource() { result = any(TemplatePlaceholderTag tag).getInnerTopLevel().getAVariableUse("include") } - override DataFlow::Node getARhs() { none() } + override DataFlow::Node getASink() { none() } } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/TrustedTypes.qll b/javascript/ql/lib/semmle/javascript/frameworks/TrustedTypes.qll index 222bcf0830c..d9394538d76 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/TrustedTypes.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/TrustedTypes.qll @@ -14,9 +14,9 @@ module TrustedTypes { private class TrustedTypesEntry extends API::EntryPoint { TrustedTypesEntry() { this = "TrustedTypesEntry" } - override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef("trustedTypes") } + override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("trustedTypes") } - override DataFlow::Node getARhs() { none() } + override DataFlow::Node getASink() { none() } } private API::Node trustedTypesObj() { result = any(TrustedTypesEntry entry).getANode() } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll index cfabf6d291a..583e90635d1 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll @@ -9,9 +9,9 @@ module Vue { private class GlobalVueEntryPoint extends API::EntryPoint { GlobalVueEntryPoint() { this = "VueEntryPoint" } - override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef("Vue") } + override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("Vue") } - override DataFlow::Node getARhs() { none() } + override DataFlow::Node getASink() { none() } } /** @@ -22,9 +22,9 @@ module Vue { private class VueExportEntryPoint extends API::EntryPoint { VueExportEntryPoint() { this = "VueExportEntryPoint" } - override DataFlow::SourceNode getAUse() { none() } + override DataFlow::SourceNode getASource() { none() } - override DataFlow::Node getARhs() { + override DataFlow::Node getASink() { result = any(SingleFileComponent c).getModule().getDefaultOrBulkExport() } } @@ -484,14 +484,14 @@ module Vue { private class VueFileImportEntryPoint extends API::EntryPoint { VueFileImportEntryPoint() { this = "VueFileImportEntryPoint" } - override DataFlow::SourceNode getAUse() { + override DataFlow::SourceNode getASource() { exists(Import imprt | imprt.getImportedPath().resolve() instanceof VueFile and result = imprt.getImportedModuleNode() ) } - override DataFlow::Node getARhs() { none() } + override DataFlow::Node getASink() { none() } } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll index 6cc06e97488..e70777fb83c 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -61,9 +61,9 @@ private class GlobalApiEntryPoint extends API::EntryPoint { this = "GlobalApiEntryPoint:" + global } - override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef(global) } + override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef(global) } - override DataFlow::Node getARhs() { none() } + override DataFlow::Node getASink() { none() } /** Gets the name of the global variable. */ string getGlobal() { result = global } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/RemoteFlowSources.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/RemoteFlowSources.qll index 5f5cf99bdd2..75a3b642eec 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/RemoteFlowSources.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/RemoteFlowSources.qll @@ -164,9 +164,9 @@ private class ExternalRemoteFlowSourceSpecEntryPoint extends API::EntryPoint { string getName() { result = name } - override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef(name) } + override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef(name) } - override DataFlow::Node getARhs() { none() } + override DataFlow::Node getASink() { none() } } /** diff --git a/javascript/ql/test/ApiGraphs/custom-entry-point/VerifyAssertions.ql b/javascript/ql/test/ApiGraphs/custom-entry-point/VerifyAssertions.ql index fb8a943f2ca..233f44c34c3 100644 --- a/javascript/ql/test/ApiGraphs/custom-entry-point/VerifyAssertions.ql +++ b/javascript/ql/test/ApiGraphs/custom-entry-point/VerifyAssertions.ql @@ -1,9 +1,9 @@ class CustomEntryPoint extends API::EntryPoint { CustomEntryPoint() { this = "CustomEntryPoint" } - override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef("CustomEntryPoint") } + override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("CustomEntryPoint") } - override DataFlow::Node getARhs() { none() } + override DataFlow::Node getASink() { none() } } import ApiGraphs.VerifyAssertions From 76ba78294ffe0f492bb81dad43d30d8fc1e04e96 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 5 Apr 2022 09:39:34 +0200 Subject: [PATCH 070/125] JS: Make API::EntryPoint overrides optional --- javascript/ql/lib/semmle/javascript/ApiGraphs.qll | 4 ++-- javascript/ql/lib/semmle/javascript/frameworks/D3.qll | 2 -- javascript/ql/lib/semmle/javascript/frameworks/History.qll | 2 -- .../ql/lib/semmle/javascript/frameworks/Immutable.qll | 2 -- javascript/ql/lib/semmle/javascript/frameworks/Logging.qll | 2 -- javascript/ql/lib/semmle/javascript/frameworks/Nest.qll | 2 -- javascript/ql/lib/semmle/javascript/frameworks/Redux.qll | 2 -- .../ql/lib/semmle/javascript/frameworks/Templating.qll | 2 -- .../ql/lib/semmle/javascript/frameworks/TrustedTypes.qll | 2 -- javascript/ql/lib/semmle/javascript/frameworks/Vue.qll | 6 ------ .../frameworks/data/internal/ApiGraphModelsSpecific.qll | 2 -- .../javascript/security/dataflow/RemoteFlowSources.qll | 2 -- .../test/ApiGraphs/custom-entry-point/VerifyAssertions.ql | 2 -- 13 files changed, 2 insertions(+), 30 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index d935819861a..a036ae56271 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -569,10 +569,10 @@ module API { deprecated DataFlow::SourceNode getARhs() { none() } /** Gets a data-flow node where a value enters the current codebase through this entry-point. */ - abstract DataFlow::SourceNode getASource(); + DataFlow::SourceNode getASource() { none() } /** Gets a data-flow node where a value leaves the current codebase through this entry-point. */ - abstract DataFlow::Node getASink(); + DataFlow::Node getASink() { none() } /** Gets an API-node for this entry point. */ API::Node getANode() { result = root().getASuccessor(Label::entryPoint(this)) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/D3.qll b/javascript/ql/lib/semmle/javascript/frameworks/D3.qll index 1c2e45f13af..9bed73e0a22 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/D3.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/D3.qll @@ -10,8 +10,6 @@ module D3 { D3GlobalEntry() { this = "D3GlobalEntry" } override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("d3") } - - override DataFlow::Node getASink() { none() } } /** Gets an API node referring to the `d3` module. */ diff --git a/javascript/ql/lib/semmle/javascript/frameworks/History.qll b/javascript/ql/lib/semmle/javascript/frameworks/History.qll index acb465ac2fe..7b7d3d6042e 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/History.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/History.qll @@ -9,8 +9,6 @@ module History { HistoryGlobalEntry() { this = "HistoryLibrary" } override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("HistoryLibrary") } - - override DataFlow::Node getASink() { none() } } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Immutable.qll b/javascript/ql/lib/semmle/javascript/frameworks/Immutable.qll index 4e6d9f5d894..3a5ef400801 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Immutable.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Immutable.qll @@ -17,8 +17,6 @@ private module Immutable { ImmutableGlobalEntry() { this = "ImmutableGlobalEntry" } override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("Immutable") } - - override DataFlow::Node getASink() { none() } } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll b/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll index 0d3a493d8e2..fb6f34f7d82 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll @@ -36,8 +36,6 @@ private module Console { ConsoleGlobalEntry() { this = "ConsoleGlobalEntry" } override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("console") } - - override DataFlow::Node getASink() { none() } } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll b/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll index 89ec6334ba2..07be0df2b28 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll @@ -143,8 +143,6 @@ module NestJS { override DataFlow::SourceNode getASource() { result.(DataFlow::ClassNode).getName() = "ValidationPipe" } - - override DataFlow::Node getASink() { none() } } /** Gets an API node referring to the constructor of `ValidationPipe` */ diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll b/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll index 50cbd2cdf0e..c6b83765d63 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll @@ -1096,8 +1096,6 @@ module Redux { private class HeuristicConnectEntryPoint extends API::EntryPoint { HeuristicConnectEntryPoint() { this = "react-redux-connect" } - override DataFlow::Node getASink() { none() } - override DataFlow::SourceNode getASource() { exists(DataFlow::CallNode call | call.getAnArgument().asExpr().(Identifier).getName() = diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll b/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll index 9227fab3544..b081ec9e782 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll @@ -660,8 +660,6 @@ module Templating { override DataFlow::SourceNode getASource() { result = any(TemplatePlaceholderTag tag).getInnerTopLevel().getAVariableUse("include") } - - override DataFlow::Node getASink() { none() } } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/TrustedTypes.qll b/javascript/ql/lib/semmle/javascript/frameworks/TrustedTypes.qll index d9394538d76..3845a30e950 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/TrustedTypes.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/TrustedTypes.qll @@ -15,8 +15,6 @@ module TrustedTypes { TrustedTypesEntry() { this = "TrustedTypesEntry" } override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("trustedTypes") } - - override DataFlow::Node getASink() { none() } } private API::Node trustedTypesObj() { result = any(TrustedTypesEntry entry).getANode() } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll index 583e90635d1..452c524b99b 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll @@ -10,8 +10,6 @@ module Vue { GlobalVueEntryPoint() { this = "VueEntryPoint" } override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("Vue") } - - override DataFlow::Node getASink() { none() } } /** @@ -22,8 +20,6 @@ module Vue { private class VueExportEntryPoint extends API::EntryPoint { VueExportEntryPoint() { this = "VueExportEntryPoint" } - override DataFlow::SourceNode getASource() { none() } - override DataFlow::Node getASink() { result = any(SingleFileComponent c).getModule().getDefaultOrBulkExport() } @@ -490,8 +486,6 @@ module Vue { result = imprt.getImportedModuleNode() ) } - - override DataFlow::Node getASink() { none() } } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll index e70777fb83c..5ab12d0aab2 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -63,8 +63,6 @@ private class GlobalApiEntryPoint extends API::EntryPoint { override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef(global) } - override DataFlow::Node getASink() { none() } - /** Gets the name of the global variable. */ string getGlobal() { result = global } } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/RemoteFlowSources.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/RemoteFlowSources.qll index 75a3b642eec..294958504f7 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/RemoteFlowSources.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/RemoteFlowSources.qll @@ -165,8 +165,6 @@ private class ExternalRemoteFlowSourceSpecEntryPoint extends API::EntryPoint { string getName() { result = name } override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef(name) } - - override DataFlow::Node getASink() { none() } } /** diff --git a/javascript/ql/test/ApiGraphs/custom-entry-point/VerifyAssertions.ql b/javascript/ql/test/ApiGraphs/custom-entry-point/VerifyAssertions.ql index 233f44c34c3..3502c0ea556 100644 --- a/javascript/ql/test/ApiGraphs/custom-entry-point/VerifyAssertions.ql +++ b/javascript/ql/test/ApiGraphs/custom-entry-point/VerifyAssertions.ql @@ -2,8 +2,6 @@ class CustomEntryPoint extends API::EntryPoint { CustomEntryPoint() { this = "CustomEntryPoint" } override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("CustomEntryPoint") } - - override DataFlow::Node getASink() { none() } } import ApiGraphs.VerifyAssertions From 9fad4b883bc9f30072fac6bb4ab8d59143363488 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 5 Apr 2022 09:40:04 +0200 Subject: [PATCH 071/125] JS: Autoformat --- .../ql/lib/semmle/javascript/frameworks/ClassValidator.qll | 3 ++- javascript/ql/lib/semmle/javascript/frameworks/SQL.qll | 3 ++- .../lib/semmle/javascript/frameworks/TorrentLibraries.qll | 4 +++- javascript/ql/lib/semmle/javascript/frameworks/Vue.qll | 6 +----- javascript/ql/lib/semmle/javascript/frameworks/Vuex.qll | 7 +++---- .../ql/lib/semmle/javascript/frameworks/XmlParsers.qll | 6 +----- .../javascript/security/dataflow/MissingRateLimiting.qll | 4 +--- .../security/dataflow/XssThroughDomCustomizations.qll | 3 +-- .../library-tests/frameworks/Express/MiddlewareFlow.qll | 4 +++- 9 files changed, 17 insertions(+), 23 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ClassValidator.qll b/javascript/ql/lib/semmle/javascript/frameworks/ClassValidator.qll index cff8da8aaf8..35f966217bd 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/ClassValidator.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/ClassValidator.qll @@ -39,7 +39,8 @@ module ClassValidator { /** Holds if the given field has a decorator that sanitizes its value for the purpose of taint tracking. */ predicate isFieldSanitizedByDecorator(FieldDefinition field) { - field.getADecorator().getExpression().flow() = sanitizingDecorator().getReturn().getAValueReachableFromSource() + field.getADecorator().getExpression().flow() = + sanitizingDecorator().getReturn().getAValueReachableFromSource() } pragma[noinline] diff --git a/javascript/ql/lib/semmle/javascript/frameworks/SQL.qll b/javascript/ql/lib/semmle/javascript/frameworks/SQL.qll index bd4e2b8c139..26833cb3019 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/SQL.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/SQL.qll @@ -440,7 +440,8 @@ private module MsSql { override TaggedTemplateExpr astNode; QueryTemplateExpr() { - mssql().getMember("query").getAValueReachableFromSource() = DataFlow::valueNode(astNode.getTag()) + mssql().getMember("query").getAValueReachableFromSource() = + DataFlow::valueNode(astNode.getTag()) } override DataFlow::Node getAResult() { diff --git a/javascript/ql/lib/semmle/javascript/frameworks/TorrentLibraries.qll b/javascript/ql/lib/semmle/javascript/frameworks/TorrentLibraries.qll index a9ab45ba821..384d6402244 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/TorrentLibraries.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/TorrentLibraries.qll @@ -29,7 +29,9 @@ module ParseTorrent { } /** Gets a data flow node referring to a parsed torrent. */ - DataFlow::SourceNode parsedTorrentRef() { result = any(ParsedTorrent t).asApiNode().getAValueReachableFromSource() } + DataFlow::SourceNode parsedTorrentRef() { + result = any(ParsedTorrent t).asApiNode().getAValueReachableFromSource() + } /** * An access to user-controlled torrent information. diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll index 452c524b99b..e375bd091a1 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll @@ -700,11 +700,7 @@ module Vue { .getASource() or result = - router - .getParameter(0) - .getMember("scrollBehavior") - .getParameter([0, 1]) - .getASource() + router.getParameter(0).getMember("scrollBehavior").getParameter([0, 1]).getASource() ) or result = routeConfig().getMember("beforeEnter").getParameter([0, 1]).getASource() diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Vuex.qll b/javascript/ql/lib/semmle/javascript/frameworks/Vuex.qll index ea6e64b4e79..c6d6be5f768 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Vuex.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Vuex.qll @@ -88,7 +88,8 @@ module Vuex { pragma[noinline] string getNamespace() { getNumArgument() = 2 and - result = appendToNamespace(namespace, getParameter(0).getAValueReachingSink().getStringValue()) + result = + appendToNamespace(namespace, getParameter(0).getAValueReachingSink().getStringValue()) or getNumArgument() = 1 and result = namespace @@ -303,9 +304,7 @@ module Vuex { } /** Gets a value that refers to the given access path of the state. */ - DataFlow::Node stateMutationSucc(string path) { - result = stateRefByAccessPath(path).getASource() - } + DataFlow::Node stateMutationSucc(string path) { result = stateRefByAccessPath(path).getASource() } /** Holds if `pred -> succ` is a step from state mutation to state access. */ predicate stateMutationStep(DataFlow::Node pred, DataFlow::Node succ) { diff --git a/javascript/ql/lib/semmle/javascript/frameworks/XmlParsers.qll b/javascript/ql/lib/semmle/javascript/frameworks/XmlParsers.qll index bd57a4ab1f8..665825c3cad 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/XmlParsers.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/XmlParsers.qll @@ -282,11 +282,7 @@ module XML { override DataFlow::Node getAResult() { result = - parser - .getReturn() - .getMember(any(string s | s.matches("on%"))) - .getAParameter() - .getASource() + parser.getReturn().getMember(any(string s | s.matches("on%"))).getAParameter().getASource() } } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll index b762198a25f..4c3debcaeea 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll @@ -152,9 +152,7 @@ abstract class RateLimitingMiddleware extends DataFlow::SourceNode { * A rate limiter constructed using the `express-rate-limit` package. */ class ExpressRateLimit extends RateLimitingMiddleware { - ExpressRateLimit() { - this = API::moduleImport("express-rate-limit").getReturn().getASource() - } + ExpressRateLimit() { this = API::moduleImport("express-rate-limit").getReturn().getASource() } } /** diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/XssThroughDomCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/XssThroughDomCustomizations.qll index 924176d99c5..792311dd8a9 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/XssThroughDomCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/XssThroughDomCustomizations.qll @@ -208,8 +208,7 @@ module XssThroughDom { exists(API::Node useForm | useForm = API::moduleImport("react-hook-form").getMember("useForm").getReturn() | - this = - useForm.getMember("handleSubmit").getParameter(0).getParameter(0).getASource() + this = useForm.getMember("handleSubmit").getParameter(0).getParameter(0).getASource() or this = useForm.getMember("getValues").getACall() ) diff --git a/javascript/ql/test/library-tests/frameworks/Express/MiddlewareFlow.qll b/javascript/ql/test/library-tests/frameworks/Express/MiddlewareFlow.qll index e1a8e825099..9578d5139b5 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/MiddlewareFlow.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/MiddlewareFlow.qll @@ -1,3 +1,5 @@ import javascript -query DataFlow::Node dbUse() { result = API::moduleImport("@example/db").getInstance().getAValueReachableFromSource() } +query DataFlow::Node dbUse() { + result = API::moduleImport("@example/db").getInstance().getAValueReachableFromSource() +} From 1ae97d9d54849dfb9c738ae885f84a48136fbd15 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 4 Apr 2022 12:50:23 +0200 Subject: [PATCH 072/125] Apply suggestions from code review Co-authored-by: Nick Rolfe --- javascript/ql/lib/semmle/javascript/ApiGraphs.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index a036ae56271..c40aa214903 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -56,7 +56,7 @@ module API { * * ### Access paths and edge labels * - * Nodes in the API graph nodes are associated with a set of access paths, describing a series of operations + * Nodes in the API graph are associated with a set of access paths, describing a series of operations * that may be performed to obtain that value. * * For example, the access path `API::moduleImport("lodash").getMember("extend")` represents the action of @@ -77,7 +77,7 @@ module API { * Because the implementation of the external library is not visible, it is not known exactly what operations * it will perform on values that flow there. Instead, the edges starting from a def-node are operations that would * lead to an observable effect within the current codebase; without knowing for certain if the library will actually perform - * those operations. (When constructing these edge, we assume the library is somewhat well-behaved). + * those operations. (When constructing these edges, we assume the library is somewhat well-behaved). * * For example, given this snippet: * ```js From 8da96ed40350e28ae1df820108d9874f9bf68562 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 5 Apr 2022 09:43:50 +0200 Subject: [PATCH 073/125] JS: Update doc comment --- javascript/ql/lib/semmle/javascript/ApiGraphs.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index c40aa214903..1a29265ecb2 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -28,11 +28,11 @@ module API { * The most basic use of API graphs is typically as follows: * 1. Start with `API::moduleImport` for the relevant library. * 2. Follow up with a chain of accessors such as `getMember` describing how to get to the relevant API function. - * 3. Map the resulting API graph nodes to data-flow nodes, using `getAnImmediateUse` or `getARhs`. + * 3. Map the resulting API graph nodes to data-flow nodes, using `getASource` or `getASink`. * * For example, a simplified way to get arguments to `underscore.extend` would be * ```codeql - * API::moduleImport("underscore").getMember("extend").getParameter(0).getARhs() + * API::moduleImport("underscore").getMember("extend").getParameter(0).getASink() * ``` * * The most commonly used accessors are `getMember`, `getParameter`, and `getReturn`. From e2858b7b6435612ae35fe81279789d6e86ed7232 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 5 Apr 2022 09:46:43 +0200 Subject: [PATCH 074/125] JS: Update ATM code --- .../experimental/adaptivethreatmodeling/EndpointFeatures.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointFeatures.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointFeatures.qll index ca7f12b7a33..e2ce4753fb4 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointFeatures.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointFeatures.qll @@ -144,9 +144,9 @@ private module AccessPaths { not param = base.getReceiver() | result = param and - name = param.getAnImmediateUse().asExpr().(Parameter).getName() + name = param.getASource().asExpr().(Parameter).getName() or - param.getAnImmediateUse().asExpr() instanceof DestructuringPattern and + param.getASource().asExpr() instanceof DestructuringPattern and result = param.getMember(name) ) } From 777d344dde1f5102d7743982e97da0770c6ae219 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 5 Apr 2022 09:56:34 +0200 Subject: [PATCH 075/125] JS: Fix up qldoc for getAValueReachingSink --- javascript/ql/lib/semmle/javascript/ApiGraphs.qll | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index 1a29265ecb2..329840b0b30 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -207,8 +207,10 @@ module API { DataFlow::Node getASink() { Impl::rhs(this, result) } /** - * Gets a data-flow node that may interprocedurally flow to the right-hand side of a definition - * of the API component represented by this node. + * Get a data-flow node that transitively flows to an external library (or in general, any external codebase). + * + * This is similar to `getASink()` but additionally includes nodes that transitively reach a sink by data flow. + * See `getASink()` for examples. */ DataFlow::Node getAValueReachingSink() { result = Impl::trackDefNode(this.getASink()) } From 1e96b1e559607812aae7068cd114cff021f74059 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 5 Apr 2022 10:15:27 +0200 Subject: [PATCH 076/125] JS: Fix typo --- javascript/ql/lib/semmle/javascript/ApiGraphs.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index 329840b0b30..d1eee769738 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -191,7 +191,7 @@ module API { * external library (or in general, any external codebase). * * Concretely, this is either an argument passed to a call to external code, - * or the right-hand side of a property write on an object flows into such a call. + * or the right-hand side of a property write on an object flowing into such a call. * * For example: * ```js From 18dc39484d2ba1e81794ccdcc71fd4cb235ecfe5 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 7 Apr 2022 10:56:11 +0200 Subject: [PATCH 077/125] Update javascript/ql/lib/semmle/javascript/ApiGraphs.qll Co-authored-by: Erik Krogh Kristensen --- javascript/ql/lib/semmle/javascript/ApiGraphs.qll | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index d1eee769738..7bd2ae95d96 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -126,9 +126,7 @@ module API { */ pragma[inline] DataFlow::Node getAValueReachableFromSource() { - exists(DataFlow::SourceNode src | Impl::use(this, src) | - Impl::trackUseNode(src).flowsTo(result) - ) + Impl::trackUseNode(this.getASource()).flowsTo(result) } /** From f80f8b6630b79c3e3df1657bc6af8e3693c524e4 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 18 May 2022 15:00:04 +0200 Subject: [PATCH 078/125] JS: Update a comment mentioning getARhs --- javascript/ql/lib/semmle/javascript/frameworks/Vue.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll index e375bd091a1..e4d61ec8ed6 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll @@ -191,7 +191,7 @@ module Vue { } /** - * DEPRECATED. Use `getOwnOptions().getARhs()`. + * DEPRECATED. Use `getOwnOptions().getASink()`. * * Gets the options passed to the Vue object, such as the object literal `{...}` in `new Vue{{...})` * or the default export of a single-file component. From bc601261ed65e11657c0d428d0bb6b4119218879 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 19 May 2022 08:47:31 +0200 Subject: [PATCH 079/125] JS: Use 'ql' language for markdown snippets --- javascript/ql/lib/semmle/javascript/ApiGraphs.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index 7bd2ae95d96..9a19cb38d10 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -31,7 +31,7 @@ module API { * 3. Map the resulting API graph nodes to data-flow nodes, using `getASource` or `getASink`. * * For example, a simplified way to get arguments to `underscore.extend` would be - * ```codeql + * ```ql * API::moduleImport("underscore").getMember("extend").getParameter(0).getASink() * ``` * From 631527fe49c36514ad96d20d3dfa9598c84f4fee Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 23 May 2022 13:54:50 +0200 Subject: [PATCH 080/125] JS: Rename Node.{getASource -> asSource, getASink -> asSink} --- .../ql/lib/semmle/javascript/ApiGraphs.qll | 56 +++++++++--------- .../ql/lib/semmle/javascript/JsonParsers.qll | 2 +- .../ql/lib/semmle/javascript/JsonSchema.qll | 4 +- .../semmle/javascript/JsonStringifiers.qll | 2 +- .../semmle/javascript/frameworks/Babel.qll | 2 +- .../javascript/frameworks/ClientRequests.qll | 10 ++-- .../javascript/frameworks/Credentials.qll | 2 +- .../lib/semmle/javascript/frameworks/D3.qll | 8 +-- .../semmle/javascript/frameworks/Electron.qll | 2 +- .../semmle/javascript/frameworks/Files.qll | 12 ++-- .../javascript/frameworks/FormParsers.qll | 6 +- .../semmle/javascript/frameworks/History.qll | 6 +- .../javascript/frameworks/HttpProxy.qll | 12 ++-- .../semmle/javascript/frameworks/LdapJS.qll | 4 +- .../javascript/frameworks/LiveServer.qll | 2 +- .../semmle/javascript/frameworks/Logging.qll | 2 +- .../semmle/javascript/frameworks/Markdown.qll | 2 +- .../lib/semmle/javascript/frameworks/Nest.qll | 10 ++-- .../lib/semmle/javascript/frameworks/Next.qll | 2 +- .../semmle/javascript/frameworks/NoSQL.qll | 12 ++-- .../javascript/frameworks/NodeJSLib.qll | 8 +-- .../semmle/javascript/frameworks/Prettier.qll | 2 +- .../javascript/frameworks/Puppeteer.qll | 2 +- .../semmle/javascript/frameworks/Redux.qll | 58 +++++++++---------- .../lib/semmle/javascript/frameworks/SQL.qll | 14 ++--- .../javascript/frameworks/Snapdragon.qll | 4 +- .../semmle/javascript/frameworks/SocketIO.qll | 4 +- .../javascript/frameworks/Templating.qll | 10 ++-- .../frameworks/TorrentLibraries.qll | 4 +- .../javascript/frameworks/TrustedTypes.qll | 2 +- .../javascript/frameworks/UriLibraries.qll | 2 +- .../lib/semmle/javascript/frameworks/Vue.qll | 27 +++++---- .../lib/semmle/javascript/frameworks/Vuex.qll | 24 ++++---- .../javascript/frameworks/XmlParsers.qll | 4 +- .../frameworks/data/ModelsAsData.qll | 6 +- .../data/internal/ApiGraphModelsSpecific.qll | 2 +- .../dataflow/DomBasedXssCustomizations.qll | 2 +- .../dataflow/ExceptionXssCustomizations.qll | 2 +- ...APIUsedWithUntrustedDataCustomizations.qll | 6 +- ...IndirectCommandInjectionCustomizations.qll | 2 +- .../security/dataflow/MissingRateLimiting.qll | 8 +-- .../security/dataflow/RemoteFlowSources.qll | 2 +- .../dataflow/SqlInjectionCustomizations.qll | 2 +- .../dataflow/TaintedPathCustomizations.qll | 10 ++-- .../dataflow/XssThroughDomCustomizations.qll | 2 +- .../dataflow/ZipSlipCustomizations.qll | 4 +- .../CWE-295/DisablingCertificateValidation.ql | 2 +- .../Security/CWE-352/MissingCsrfMiddleware.ql | 2 +- .../ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql | 2 +- .../ql/test/ApiGraphs/VerifyAssertions.qll | 2 +- .../ql/test/ApiGraphs/call-nodes/test.ql | 4 +- .../ql/test/ApiGraphs/typed/NodeOfType.ql | 2 +- .../ql/test/library-tests/Routing/test.ql | 4 +- .../library-tests/frameworks/data/test.ql | 6 +- 54 files changed, 197 insertions(+), 198 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index 9a19cb38d10..205ff21d1fd 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -28,11 +28,11 @@ module API { * The most basic use of API graphs is typically as follows: * 1. Start with `API::moduleImport` for the relevant library. * 2. Follow up with a chain of accessors such as `getMember` describing how to get to the relevant API function. - * 3. Map the resulting API graph nodes to data-flow nodes, using `getASource` or `getASink`. + * 3. Map the resulting API graph nodes to data-flow nodes, using `asSource` or `asSink`. * * For example, a simplified way to get arguments to `underscore.extend` would be * ```ql - * API::moduleImport("underscore").getMember("extend").getParameter(0).getASink() + * API::moduleImport("underscore").getMember("extend").getParameter(0).asSink() * ``` * * The most commonly used accessors are `getMember`, `getParameter`, and `getReturn`. @@ -121,12 +121,12 @@ module API { /** * Get a data-flow node where this value may flow after entering the current codebase. * - * This is similar to `getASource()` but additionally includes nodes that are transitively reachable by data flow. - * See `getASource()` for examples. + * This is similar to `asSource()` but additionally includes nodes that are transitively reachable by data flow. + * See `asSource()` for examples. */ pragma[inline] DataFlow::Node getAValueReachableFromSource() { - Impl::trackUseNode(this.getASource()).flowsTo(result) + Impl::trackUseNode(this.asSource()).flowsTo(result) } /** @@ -134,27 +134,27 @@ module API { * * For example: * ```js - * // API::moduleImport("fs").getASource() + * // API::moduleImport("fs").asSource() * require('fs'); * - * // API::moduleImport("fs").getMember("readFile").getASource() + * // API::moduleImport("fs").getMember("readFile").asSource() * require('fs').readFile; * - * // API::moduleImport("fs").getMember("readFile").getReturn().getASource() + * // API::moduleImport("fs").getMember("readFile").getReturn().asSource() * require('fs').readFile(); * * require('fs').readFile( * filename, - * // 'y' matched by API::moduleImport("fs").getMember("readFile").getParameter(1).getParameter(0).getASource() + * // 'y' matched by API::moduleImport("fs").getMember("readFile").getParameter(1).getParameter(0).asSource() * y => { * ... * }); * ``` */ - DataFlow::SourceNode getASource() { Impl::use(this, result) } + DataFlow::SourceNode asSource() { Impl::use(this, result) } - /** DEPRECATED. This predicate has been renamed to `getASource`. */ - deprecated DataFlow::SourceNode getAnImmediateUse() { result = this.getASource() } + /** DEPRECATED. This predicate has been renamed to `asSource`. */ + deprecated DataFlow::SourceNode getAnImmediateUse() { result = this.asSource() } /** DEPRECATED. This predicate has been renamed to `getAValueReachableFromSource`. */ deprecated DataFlow::Node getAUse() { result = this.getAValueReachableFromSource() } @@ -162,7 +162,7 @@ module API { /** * Gets a call to the function represented by this API component. */ - CallNode getACall() { result = this.getReturn().getASource() } + CallNode getACall() { result = this.getReturn().asSource() } /** * Gets a call to the function represented by this API component, @@ -177,7 +177,7 @@ module API { /** * Gets a `new` call to the function represented by this API component. */ - NewNode getAnInstantiation() { result = this.getInstance().getASource() } + NewNode getAnInstantiation() { result = this.getInstance().asSource() } /** * Gets an invocation (with our without `new`) to the function represented by this API component. @@ -193,27 +193,27 @@ module API { * * For example: * ```js - * // 'x' is matched by API::moduleImport("foo").getParameter(0).getASink() + * // 'x' is matched by API::moduleImport("foo").getParameter(0).asSink() * require('foo')(x); * - * // 'x' is matched by API::moduleImport("foo").getParameter(0).getMember("prop").getASink() + * // 'x' is matched by API::moduleImport("foo").getParameter(0).getMember("prop").asSink() * require('foo')({ * prop: x * }); * ``` */ - DataFlow::Node getASink() { Impl::rhs(this, result) } + DataFlow::Node asSink() { Impl::rhs(this, result) } /** * Get a data-flow node that transitively flows to an external library (or in general, any external codebase). * - * This is similar to `getASink()` but additionally includes nodes that transitively reach a sink by data flow. - * See `getASink()` for examples. + * This is similar to `asSink()` but additionally includes nodes that transitively reach a sink by data flow. + * See `asSink()` for examples. */ - DataFlow::Node getAValueReachingSink() { result = Impl::trackDefNode(this.getASink()) } + DataFlow::Node getAValueReachingSink() { result = Impl::trackDefNode(this.asSink()) } - /** DEPRECATED. This predicate has been renamed to `getASink`. */ - deprecated DataFlow::Node getARhs() { result = this.getASink() } + /** DEPRECATED. This predicate has been renamed to `asSink`. */ + deprecated DataFlow::Node getARhs() { result = this.asSink() } /** DEPRECATED. This predicate has been renamed to `getAValueReachingSink`. */ deprecated DataFlow::Node getAValueReachingRhs() { result = this.getAValueReachingSink() } @@ -451,7 +451,7 @@ module API { * In other words, the value of a use of `that` may flow into the right-hand side of a * definition of this node. */ - predicate refersTo(Node that) { this.getASink() = that.getAValueReachableFromSource() } + predicate refersTo(Node that) { this.asSink() = that.getAValueReachableFromSource() } /** * Gets the data-flow node that gives rise to this node, if any. @@ -1301,8 +1301,8 @@ module API { API::Node callee; InvokeNode() { - this = callee.getReturn().getASource() or - this = callee.getInstance().getASource() or + this = callee.getReturn().asSource() or + this = callee.getInstance().asSource() or this = Impl::getAPromisifiedInvocation(callee, _, _) } @@ -1317,7 +1317,7 @@ module API { * Gets an API node where a RHS of the node is the `i`th argument to this call. */ pragma[noinline] - private Node getAParameterCandidate(int i) { result.getASink() = this.getArgument(i) } + private Node getAParameterCandidate(int i) { result.asSink() = this.getArgument(i) } /** Gets the API node for a parameter of this invocation. */ Node getAParameter() { result = this.getParameter(_) } @@ -1328,13 +1328,13 @@ module API { /** Gets the API node for the return value of this call. */ Node getReturn() { result = callee.getReturn() and - result.getASource() = this + result.asSource() = this } /** Gets the API node for the object constructed by this invocation. */ Node getInstance() { result = callee.getInstance() and - result.getASource() = this + result.asSource() = this } } diff --git a/javascript/ql/lib/semmle/javascript/JsonParsers.qll b/javascript/ql/lib/semmle/javascript/JsonParsers.qll index cd4a104679f..e3f1f285a21 100644 --- a/javascript/ql/lib/semmle/javascript/JsonParsers.qll +++ b/javascript/ql/lib/semmle/javascript/JsonParsers.qll @@ -29,7 +29,7 @@ private class PlainJsonParserCall extends JsonParserCall { callee = DataFlow::moduleMember(["json3", "json5", "flatted", "teleport-javascript", "json-cycle"], "parse") or - callee = API::moduleImport("replicator").getInstance().getMember("decode").getASource() or + callee = API::moduleImport("replicator").getInstance().getMember("decode").asSource() or callee = DataFlow::moduleImport("parse-json") or callee = DataFlow::moduleImport("json-parse-better-errors") or callee = DataFlow::moduleImport("json-safe-parse") or diff --git a/javascript/ql/lib/semmle/javascript/JsonSchema.qll b/javascript/ql/lib/semmle/javascript/JsonSchema.qll index 4c6540efc13..a298abfd2dd 100644 --- a/javascript/ql/lib/semmle/javascript/JsonSchema.qll +++ b/javascript/ql/lib/semmle/javascript/JsonSchema.qll @@ -134,7 +134,7 @@ module JsonSchema { .ref() .getMember(["addSchema", "validate", "compile", "compileAsync"]) .getParameter(0) - .getASink() + .asSink() } } } @@ -184,7 +184,7 @@ module JsonSchema { override boolean getPolarity() { none() } override DataFlow::Node getAValidationResultAccess(boolean polarity) { - result = this.getReturn().getMember("error").getASource() and + result = this.getReturn().getMember("error").asSource() and polarity = false } } diff --git a/javascript/ql/lib/semmle/javascript/JsonStringifiers.qll b/javascript/ql/lib/semmle/javascript/JsonStringifiers.qll index fc6d1dfe1f5..f4cdda89e31 100644 --- a/javascript/ql/lib/semmle/javascript/JsonStringifiers.qll +++ b/javascript/ql/lib/semmle/javascript/JsonStringifiers.qll @@ -14,7 +14,7 @@ class JsonStringifyCall extends DataFlow::CallNode { callee = DataFlow::moduleMember(["json3", "json5", "flatted", "teleport-javascript", "json-cycle"], "stringify") or - callee = API::moduleImport("replicator").getInstance().getMember("encode").getASource() or + callee = API::moduleImport("replicator").getInstance().getMember("encode").asSource() or callee = DataFlow::moduleImport([ "json-stringify-safe", "json-stable-stringify", "stringify-object", diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Babel.qll b/javascript/ql/lib/semmle/javascript/frameworks/Babel.qll index 2134a84fb10..4280862c6e0 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Babel.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Babel.qll @@ -198,7 +198,7 @@ module Babel { .getMember(["transform", "transformSync", "transformAsync"]) .getACall() and pred = call.getArgument(0) and - succ = [call, call.getParameter(2).getParameter(0).getASource()] + succ = [call, call.getParameter(2).getParameter(0).asSource()] ) } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll b/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll index c14d4047602..8e56a36b9bf 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll @@ -265,7 +265,7 @@ module ClientRequest { or responseType = this.getResponseType() and promise = false and - result = this.getReturn().getPromisedError().getMember("response").getASource() + result = this.getReturn().getPromisedError().getMember("response").asSource() } } @@ -463,7 +463,7 @@ module ClientRequest { */ private API::Node netSocketInstantiation(DataFlow::NewNode socket) { result = API::moduleImport("net").getMember("Socket").getInstance() and - socket = result.getASource() + socket = result.asSource() } /** @@ -827,7 +827,7 @@ module ClientRequest { class ApolloClientRequest extends ClientRequest::Range, API::InvokeNode { ApolloClientRequest() { this = apolloUriCallee().getAnInvocation() } - override DataFlow::Node getUrl() { result = this.getParameter(0).getMember("uri").getASink() } + override DataFlow::Node getUrl() { result = this.getParameter(0).getMember("uri").asSink() } override DataFlow::Node getHost() { none() } @@ -848,10 +848,10 @@ module ClientRequest { override DataFlow::Node getUrl() { result = this.getArgument(0) } - override DataFlow::Node getHost() { result = this.getParameter(0).getMember("host").getASink() } + override DataFlow::Node getHost() { result = this.getParameter(0).getMember("host").asSink() } override DataFlow::Node getADataNode() { - result = form.getMember("append").getACall().getParameter(1).getASink() + result = form.getMember("append").getACall().getParameter(1).asSink() } } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Credentials.qll b/javascript/ql/lib/semmle/javascript/frameworks/Credentials.qll index 925dba2305b..164bc6e8f88 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Credentials.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Credentials.qll @@ -21,7 +21,7 @@ private class CredentialsFromModel extends CredentialsExpr { string kind; CredentialsFromModel() { - this = ModelOutput::getASinkNode("credentials[" + kind + "]").getASink().asExpr() + this = ModelOutput::getASinkNode("credentials[" + kind + "]").asSink().asExpr() } override string getCredentialsKind() { result = kind } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/D3.qll b/javascript/ql/lib/semmle/javascript/frameworks/D3.qll index 9bed73e0a22..76bdeb1324a 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/D3.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/D3.qll @@ -69,18 +69,18 @@ module D3 { D3XssSink() { exists(API::Node htmlArg | htmlArg = d3Selection().getMember("html").getParameter(0) and - this = [htmlArg, htmlArg.getReturn()].getASink() + this = [htmlArg, htmlArg.getReturn()].asSink() ) } } private class D3DomValueSource extends DOM::DomValueSource::Range { D3DomValueSource() { - this = d3Selection().getMember("each").getReceiver().getASource() + this = d3Selection().getMember("each").getReceiver().asSource() or - this = d3Selection().getMember("node").getReturn().getASource() + this = d3Selection().getMember("node").getReturn().asSource() or - this = d3Selection().getMember("nodes").getReturn().getUnknownMember().getASource() + this = d3Selection().getMember("nodes").getReturn().getUnknownMember().asSource() } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Electron.qll b/javascript/ql/lib/semmle/javascript/frameworks/Electron.qll index f0ab6a75bc5..038f4b8afb3 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Electron.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Electron.qll @@ -56,7 +56,7 @@ module Electron { } } - private API::Node browserObject() { result.getASource() instanceof NewBrowserObject } + private API::Node browserObject() { result.asSource() instanceof NewBrowserObject } /** * A data flow node whose value may originate from a browser object instantiation. diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Files.qll b/javascript/ql/lib/semmle/javascript/frameworks/Files.qll index 8420ade8039..f03f5ee1458 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Files.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Files.qll @@ -89,7 +89,7 @@ private API::Node globbyFileNameSource() { * A file name or an array of file names from the `globby` library. */ private class GlobbyFileNameSource extends FileNameSource { - GlobbyFileNameSource() { this = globbyFileNameSource().getASource() } + GlobbyFileNameSource() { this = globbyFileNameSource().asSource() } } /** Gets a file name or an array of file names from the `fast-glob` library. */ @@ -116,7 +116,7 @@ private API::Node fastGlobFileName() { * A file name or an array of file names from the `fast-glob` library. */ private class FastGlobFileNameSource extends FileNameSource { - FastGlobFileNameSource() { this = fastGlobFileName().getASource() } + FastGlobFileNameSource() { this = fastGlobFileName().asSource() } } /** @@ -200,7 +200,7 @@ private class RecursiveReadDir extends FileSystemAccess, FileNameProducer, API:: override DataFlow::Node getAPathArgument() { result = this.getArgument(0) } - override DataFlow::Node getAFileName() { result = this.trackFileSource().getASource() } + override DataFlow::Node getAFileName() { result = this.trackFileSource().asSource() } private API::Node trackFileSource() { result = this.getParameter([1 .. 2]).getParameter(1) @@ -223,7 +223,7 @@ private module JsonFile { override DataFlow::Node getAPathArgument() { result = this.getArgument(0) } - override DataFlow::Node getADataNode() { result = this.trackRead().getASource() } + override DataFlow::Node getADataNode() { result = this.trackRead().asSource() } private API::Node trackRead() { this.getCalleeName() = "readFile" and @@ -272,7 +272,7 @@ private class LoadJsonFile extends FileSystemReadAccess, API::CallNode { override DataFlow::Node getAPathArgument() { result = this.getArgument(0) } - override DataFlow::Node getADataNode() { result = this.trackRead().getASource() } + override DataFlow::Node getADataNode() { result = this.trackRead().asSource() } private API::Node trackRead() { this.getCalleeName() = "sync" and result = this.getReturn() @@ -310,7 +310,7 @@ private class WalkDir extends FileNameProducer, FileSystemAccess, API::CallNode override DataFlow::Node getAPathArgument() { result = this.getArgument(0) } - override DataFlow::Node getAFileName() { result = this.trackFileSource().getASource() } + override DataFlow::Node getAFileName() { result = this.trackFileSource().asSource() } private API::Node trackFileSource() { not this.getCalleeName() = ["sync", "async"] and diff --git a/javascript/ql/lib/semmle/javascript/frameworks/FormParsers.qll b/javascript/ql/lib/semmle/javascript/frameworks/FormParsers.qll index 37d20219ab8..26e0d4fe94f 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/FormParsers.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/FormParsers.qll @@ -15,7 +15,7 @@ private class BusBoyRemoteFlow extends RemoteFlowSource { .getMember("on") .getParameter(1) .getAParameter() - .getASource() + .asSource() } override string getSourceType() { result = "parsed user value from Busbuy" } @@ -49,12 +49,12 @@ private class MultipartyRemoteFlow extends RemoteFlowSource { MultipartyRemoteFlow() { exists(API::Node form | form = API::moduleImport("multiparty").getMember("Form").getInstance() | exists(API::CallNode parse | parse = form.getMember("parse").getACall() | - this = parse.getParameter(1).getAParameter().getASource() + this = parse.getParameter(1).getAParameter().asSource() ) or exists(API::CallNode on | on = form.getMember("on").getACall() | on.getArgument(0).mayHaveStringValue(["part", "file", "field"]) and - this = on.getParameter(1).getAParameter().getASource() + this = on.getParameter(1).getAParameter().asSource() ) ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/History.qll b/javascript/ql/lib/semmle/javascript/frameworks/History.qll index 7b7d3d6042e..e076bee9670 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/History.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/History.qll @@ -38,11 +38,11 @@ module History { HistoryLibaryRemoteFlow() { exists(API::Node loc | loc = [getBrowserHistory(), getHashHistory()].getMember("location") | - this = loc.getMember("hash").getASource() and kind.isFragment() + this = loc.getMember("hash").asSource() and kind.isFragment() or - this = loc.getMember("pathname").getASource() and kind.isPath() + this = loc.getMember("pathname").asSource() and kind.isPath() or - this = loc.getMember("search").getASource() and kind.isQuery() + this = loc.getMember("search").asSource() and kind.isQuery() ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/HttpProxy.qll b/javascript/ql/lib/semmle/javascript/frameworks/HttpProxy.qll index b879207c602..24e694ca8d9 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/HttpProxy.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/HttpProxy.qll @@ -19,10 +19,10 @@ private module HttpProxy { .getACall() } - override DataFlow::Node getUrl() { result = getParameter(0).getMember("target").getASink() } + override DataFlow::Node getUrl() { result = getParameter(0).getMember("target").asSink() } override DataFlow::Node getHost() { - result = getParameter(0).getMember("target").getMember("host").getASink() + result = getParameter(0).getMember("target").getMember("host").asSink() } override DataFlow::Node getADataNode() { none() } @@ -49,10 +49,10 @@ private module HttpProxy { ) } - override DataFlow::Node getUrl() { result = getOptionsObject().getMember("target").getASink() } + override DataFlow::Node getUrl() { result = getOptionsObject().getMember("target").asSink() } override DataFlow::Node getHost() { - result = getOptionsObject().getMember("target").getMember("host").getASink() + result = getOptionsObject().getMember("target").getMember("host").asSink() } override DataFlow::Node getADataNode() { none() } @@ -78,8 +78,8 @@ private module HttpProxy { ProxyListenerCallback() { exists(API::CallNode call | call = any(CreateServerCall server).getReturn().getMember(["on", "once"]).getACall() and - call.getParameter(0).getASink().mayHaveStringValue(event) and - this = call.getParameter(1).getASink().getAFunctionValue() + call.getParameter(0).asSink().mayHaveStringValue(event) and + this = call.getParameter(1).asSink().getAFunctionValue() ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/LdapJS.qll b/javascript/ql/lib/semmle/javascript/frameworks/LdapJS.qll index 9a50f2315fe..1118f05b506 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/LdapJS.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/LdapJS.qll @@ -61,10 +61,10 @@ module LdapJS { SearchFilter() { options = ldapClient().getMember("search").getACall().getParameter(1) and - this = options.getASink() + this = options.asSink() } - override DataFlow::Node getInput() { result = options.getMember("filter").getASink() } + override DataFlow::Node getInput() { result = options.getMember("filter").asSink() } override DataFlow::Node getOutput() { result = this } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/LiveServer.qll b/javascript/ql/lib/semmle/javascript/frameworks/LiveServer.qll index 5272881c7d8..1d7604e0b0c 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/LiveServer.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/LiveServer.qll @@ -12,7 +12,7 @@ private module LiveServer { class ServerDefinition extends HTTP::Servers::StandardServerDefinition { ServerDefinition() { this = DataFlow::moduleImport("live-server").asExpr() } - API::Node getImportNode() { result.getASource().asExpr() = this } + API::Node getImportNode() { result.asSource().asExpr() = this } } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll b/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll index fb6f34f7d82..8f5938f4865 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll @@ -350,7 +350,7 @@ private module Pino { // `pino` is installed as the "log" property on the request object in `Express` and similar libraries. // in `Hapi` the property is "logger". exists(HTTP::RequestExpr req, API::Node reqNode | - reqNode.getASource() = req.flow().getALocalSource() and + reqNode.asSource() = req.flow().getALocalSource() and result = reqNode.getMember(["log", "logger"]) ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Markdown.qll b/javascript/ql/lib/semmle/javascript/frameworks/Markdown.qll index d22d64b1adc..492ab0cbcec 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Markdown.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Markdown.qll @@ -163,7 +163,7 @@ module Markdown { or call = API::moduleImport("markdown-it").getMember("Markdown").getAnInvocation() | - call.getParameter(0).getMember("html").getASink().mayHaveBooleanValue(true) and + call.getParameter(0).getMember("html").asSink().mayHaveBooleanValue(true) and result = call.getReturn() ) or diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll b/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll index 07be0df2b28..1799f35beb8 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll @@ -191,7 +191,7 @@ module NestJS { .getAMember() .getMember("useFactory") .getReturn() - .getASink() = validationPipe().getInstance().getAValueReachableFromSource() and + .asSink() = validationPipe().getInstance().getAValueReachableFromSource() and folder = decorator.getFile().getParentContainer() ) or @@ -397,7 +397,7 @@ module NestJS { } /** Gets a value returned by the decorator's callback, which becomes the value of the decorated parameter. */ - DataFlow::Node getResult() { result = getParameter(0).getReturn().getASink() } + DataFlow::Node getResult() { result = getParameter(0).getReturn().asSink() } } /** @@ -425,7 +425,7 @@ module NestJS { private class ExpressRequestSource extends Express::RequestSource { ExpressRequestSource() { this.(DataFlow::ParameterNode).getADecorator() = - nestjs().getMember(["Req", "Request"]).getReturn().getASource() + nestjs().getMember(["Req", "Request"]).getReturn().asSource() or this = executionContext() @@ -433,7 +433,7 @@ module NestJS { .getReturn() .getMember("getRequest") .getReturn() - .getASource() + .asSource() } /** @@ -450,7 +450,7 @@ module NestJS { private class ExpressResponseSource extends Express::ResponseSource { ExpressResponseSource() { this.(DataFlow::ParameterNode).getADecorator() = - nestjs().getMember(["Res", "Response"]).getReturn().getASource() + nestjs().getMember(["Res", "Response"]).getReturn().asSource() } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Next.qll b/javascript/ql/lib/semmle/javascript/frameworks/Next.qll index d131e6b3674..091aa8ba1af 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Next.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Next.qll @@ -252,6 +252,6 @@ module NextJS { .getParameter(0) .getParameter(0) .getMember("router") - .getASource() + .asSource() } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll b/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll index 122d3b994be..0d2da4b10bb 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll @@ -20,7 +20,7 @@ deprecated module NoSQL = NoSql; * Gets a value that has been assigned to the "$where" property of an object that flows to `queryArg`. */ private DataFlow::Node getADollarWhereProperty(API::Node queryArg) { - result = queryArg.getMember("$where").getASink() + result = queryArg.getMember("$where").asSink() } /** @@ -418,7 +418,7 @@ private module Mongoose { param = f.getParameter(0).getParameter(1) | exists(DataFlow::MethodCallNode pred | - // limitation: look at the previous method call + // limitation: look at the previous method call Query::MethodSignatures::returnsDocumentQuery(pred.getMethodName(), asArray) and pred.getAMethodCall() = f.getACall() ) @@ -501,7 +501,7 @@ private module Mongoose { Credentials() { exists(string prop | - this = createConnection().getParameter(3).getMember(prop).getASink().asExpr() + this = createConnection().getParameter(3).getMember(prop).asSink().asExpr() | prop = "user" and kind = "user name" or @@ -518,7 +518,7 @@ private module Mongoose { class MongoDBQueryPart extends NoSql::Query { MongooseFunction f; - MongoDBQueryPart() { this = f.getQueryArgument().getASink().asExpr() } + MongoDBQueryPart() { this = f.getQueryArgument().asSink().asExpr() } override DataFlow::Node getACodeOperator() { result = getADollarWhereProperty(f.getQueryArgument()) @@ -540,7 +540,7 @@ private module Mongoose { override DataFlow::Node getAQueryArgument() { // NB: the complete information is not easily accessible for deeply chained calls - f.getQueryArgument().getASink() = result + f.getQueryArgument().asSink() = result } override DataFlow::Node getAResult() { @@ -770,7 +770,7 @@ private module Redis { RedisKeyArgument() { exists(string method, int argIndex | QuerySignatures::argumentIsAmbiguousKey(method, argIndex) and - this = redis().getMember(method).getParameter(argIndex).getASink().asExpr() + this = redis().getMember(method).getParameter(argIndex).asSink().asExpr() ) } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll b/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll index 580109b6cc4..75e222730cc 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll @@ -739,7 +739,7 @@ module NodeJSLib { methodName = ["execFile", "execFileSync", "spawn", "spawnSync", "fork"] ) and // all of the above methods take the command as their first argument - result = this.getParameter(0).getASink() + result = this.getParameter(0).asSink() } override DataFlow::Node getACommandArgument() { result = this.getACommandArgument(_) } @@ -751,7 +751,7 @@ module NodeJSLib { override DataFlow::Node getArgumentList() { methodName = ["execFile", "execFileSync", "fork", "spawn", "spawnSync"] and // all of the above methods take the argument list as their second argument - result = this.getParameter(1).getASink() + result = this.getParameter(1).asSink() } override predicate isSync() { methodName.matches("%Sync") } @@ -759,7 +759,7 @@ module NodeJSLib { override DataFlow::Node getOptionsArg() { not result.getALocalSource() instanceof DataFlow::FunctionNode and // looks like callback not result.getALocalSource() instanceof DataFlow::ArrayCreationNode and // looks like argumentlist - not result = this.getParameter(0).getASink() and + not result = this.getParameter(0).asSink() and // fork/spawn and all sync methos always has options as the last argument if methodName.matches("fork%") or @@ -768,7 +768,7 @@ module NodeJSLib { then result = this.getLastArgument() else // the rest (exec/execFile) has the options argument as their second last. - result = this.getParameter(this.getNumArgument() - 2).getASink() + result = this.getParameter(this.getNumArgument() - 2).asSink() } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Prettier.qll b/javascript/ql/lib/semmle/javascript/frameworks/Prettier.qll index 3b7accdaa26..1277c1ee133 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Prettier.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Prettier.qll @@ -22,7 +22,7 @@ private module Prettier { call = API::moduleImport("prettier").getMember("formatWithCursor").getACall() | pred = call.getArgument(0) and - succ = call.getReturn().getMember("formatted").getASource() + succ = call.getReturn().getMember("formatted").asSource() ) } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Puppeteer.qll b/javascript/ql/lib/semmle/javascript/frameworks/Puppeteer.qll index 94e98a5bdee..0636b8603b9 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Puppeteer.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Puppeteer.qll @@ -86,7 +86,7 @@ module Puppeteer { this = page().getMember(["addStyleTag", "addScriptTag"]).getACall() } - override DataFlow::Node getUrl() { result = getParameter(0).getMember("url").getASink() } + override DataFlow::Node getUrl() { result = getParameter(0).getMember("url").asSink() } override DataFlow::Node getHost() { none() } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll b/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll index c6b83765d63..67901528f2b 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Redux.qll @@ -61,7 +61,7 @@ module Redux { DataFlow::SourceNode ref() { result = asApiNode().getAValueReachableFromSource() } /** Gets an API node that refers to this store creation. */ - API::Node asApiNode() { result.getASource() = this } + API::Node asApiNode() { result.asSource() = this } /** Gets the data flow node holding the root reducer for this store. */ DataFlow::Node getReducerArg() { result = super.getReducerArg() } @@ -94,7 +94,7 @@ module Redux { } override DataFlow::Node getReducerArg() { - result = getParameter(0).getMember("reducer").getASink() + result = getParameter(0).getMember("reducer").asSink() } } } @@ -106,7 +106,7 @@ module Redux { private API::Node rootState() { result instanceof RootStateSource or - stateStep(rootState().getAValueReachableFromSource(), result.getASource()) + stateStep(rootState().getAValueReachableFromSource(), result.asSource()) } /** @@ -120,7 +120,7 @@ module Redux { accessPath = joinAccessPaths(base, prop) ) or - stateStep(rootStateAccessPath(accessPath).getAValueReachableFromSource(), result.getASource()) + stateStep(rootStateAccessPath(accessPath).getAValueReachableFromSource(), result.asSource()) } /** @@ -193,7 +193,7 @@ module Redux { CombineReducers() { this = combineReducers().getACall() } override DataFlow::Node getStateHandlerArg(string prop) { - result = getParameter(0).getMember(prop).getASink() + result = getParameter(0).getMember(prop).asSink() } } @@ -235,7 +235,7 @@ module Redux { override DataFlow::Node getActionHandlerArg(DataFlow::Node actionType) { exists(DataFlow::PropWrite write | - result = getParameter(0).getAMember().getASink() and + result = getParameter(0).getAMember().asSink() and write.getRhs() = result and actionType = write.getPropertyNameExpr().flow() ) @@ -374,7 +374,7 @@ module Redux { CreateSliceReducer() { call = API::moduleImport("@reduxjs/toolkit").getMember("createSlice").getACall() and - this = call.getReturn().getMember("reducer").getASource() + this = call.getReturn().getMember("reducer").asSource() } private API::Node getABuilderRef() { @@ -385,14 +385,14 @@ module Redux { override DataFlow::Node getActionHandlerArg(DataFlow::Node actionType) { exists(string name | - result = call.getParameter(0).getMember("reducers").getMember(name).getASink() and - actionType = call.getReturn().getMember("actions").getMember(name).getASource() + result = call.getParameter(0).getMember("reducers").getMember(name).asSink() and + actionType = call.getReturn().getMember("actions").getMember(name).asSource() ) or // Properties of 'extraReducers': // { extraReducers: { [action]: reducer }} exists(DataFlow::PropWrite write | - result = call.getParameter(0).getMember("extraReducers").getAMember().getASink() and + result = call.getParameter(0).getMember("extraReducers").getAMember().asSink() and write.getRhs() = result and actionType = write.getPropertyNameExpr().flow() ) @@ -444,8 +444,8 @@ module Redux { or // x -> bindActionCreators({ x, ... }) exists(BindActionCreatorsCall bind, string prop | - ref(t.continue()).flowsTo(bind.getParameter(0).getMember(prop).getASink()) and - result = bind.getReturn().getMember(prop).getASource() + ref(t.continue()).flowsTo(bind.getParameter(0).getMember(prop).asSink()) and + result = bind.getReturn().getMember(prop).asSource() ) or // x -> combineActions(x, ...) @@ -580,11 +580,11 @@ module Redux { MultiAction() { createActions = API::moduleImport("redux-actions").getMember("createActions").getACall() and - this = createActions.getReturn().getMember(name).getASource() + this = createActions.getReturn().getMember(name).asSource() } override DataFlow::FunctionNode getMiddlewareFunction(boolean async) { - result.flowsTo(createActions.getParameter(0).getMember(getTypeTag()).getASink()) and + result.flowsTo(createActions.getParameter(0).getMember(getTypeTag()).asSink()) and async = false } @@ -614,12 +614,12 @@ module Redux { CreateSliceAction() { call = API::moduleImport("@reduxjs/toolkit").getMember("createSlice").getACall() and - this = call.getReturn().getMember("actions").getMember(actionName).getASource() + this = call.getReturn().getMember("actions").getMember(actionName).asSource() } override string getTypeTag() { exists(string prefix | - call.getParameter(0).getMember("name").getASink().mayHaveStringValue(prefix) and + call.getParameter(0).getMember("name").asSink().mayHaveStringValue(prefix) and result = prefix + "/" + actionName ) } @@ -885,12 +885,12 @@ module Redux { accessPath = getAffectedStateAccessPath(reducer) | pred = function.getReturnNode() and - succ = rootStateAccessPath(accessPath).getASource() + succ = rootStateAccessPath(accessPath).asSource() or exists(string suffix, DataFlow::SourceNode base | base = [function.getParameter(0), function.getReturnNode().getALocalSource()] and pred = AccessPath::getAnAssignmentTo(base, suffix) and - succ = rootStateAccessPath(accessPath + "." + suffix).getASource() + succ = rootStateAccessPath(accessPath + "." + suffix).asSource() ) ) or @@ -901,7 +901,7 @@ module Redux { reducer.isRootStateHandler() and base = [function.getParameter(0), function.getReturnNode().getALocalSource()] and pred = AccessPath::getAnAssignmentTo(base, suffix) and - succ = rootStateAccessPath(suffix).getASource() + succ = rootStateAccessPath(suffix).asSource() ) } @@ -994,7 +994,7 @@ module Redux { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { exists(API::CallNode call | call = useSelector().getACall() and - pred = call.getParameter(0).getReturn().getASink() and + pred = call.getParameter(0).getReturn().asSink() and succ = call ) } @@ -1046,19 +1046,19 @@ module Redux { // // const mapDispatchToProps = { foo } // - result = getMapDispatchToProps().getMember(name).getASink() + result = getMapDispatchToProps().getMember(name).asSink() or // // const mapDispatchToProps = dispatch => ( { foo } ) // - result = getMapDispatchToProps().getReturn().getMember(name).getASink() + result = getMapDispatchToProps().getReturn().getMember(name).asSink() or // Explicitly bound by bindActionCreators: // // const mapDispatchToProps = dispatch => bindActionCreators({ foo }, dispatch); // exists(BindActionCreatorsCall bind | - bind.flowsTo(getMapDispatchToProps().getReturn().getASink()) and + bind.flowsTo(getMapDispatchToProps().getReturn().asSink()) and result = bind.getOptionArgument(0, name) ) } @@ -1113,12 +1113,12 @@ module Redux { override API::Node getMapStateToProps() { result = getAParameter() and - result.getASink().asExpr().(Identifier).getName() = "mapStateToProps" + result.asSink().asExpr().(Identifier).getName() = "mapStateToProps" } override API::Node getMapDispatchToProps() { result = getAParameter() and - result.getASink().asExpr().(Identifier).getName() = "mapDispatchToProps" + result.asSink().asExpr().(Identifier).getName() = "mapDispatchToProps" } } @@ -1128,7 +1128,7 @@ module Redux { private class StateToPropsStep extends StateStep { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { exists(ConnectCall call | - pred = call.getMapStateToProps().getReturn().getASink() and + pred = call.getMapStateToProps().getReturn().asSink() and succ = call.getReactComponent().getADirectPropsAccess() ) } @@ -1219,13 +1219,13 @@ module Redux { // Return value of `i`th callback flows to the `i`th parameter of the last callback. exists(CreateSelectorCall call, int index | call.getNumArgument() > 1 and - pred = call.getSelectorFunction(index).getReturn().getASink() and - succ = call.getLastParameter().getParameter(index).getASource() + pred = call.getSelectorFunction(index).getReturn().asSink() and + succ = call.getLastParameter().getParameter(index).asSource() ) or // The result of the last callback is the final result exists(CreateSelectorCall call | - pred = call.getLastParameter().getReturn().getASink() and + pred = call.getLastParameter().getReturn().asSink() and succ = call ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/SQL.qll b/javascript/ql/lib/semmle/javascript/frameworks/SQL.qll index 26833cb3019..edd92614a2c 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/SQL.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/SQL.qll @@ -9,7 +9,7 @@ module SQL { abstract class SqlString extends Expr { } private class SqlStringFromModel extends SqlString { - SqlStringFromModel() { this = ModelOutput::getASinkNode("sql-injection").getASink().asExpr() } + SqlStringFromModel() { this = ModelOutput::getASinkNode("sql-injection").asSink().asExpr() } } /** @@ -109,7 +109,7 @@ private module MySql { Credentials() { exists(API::Node callee, string prop | callee in [createConnection(), createPool()] and - this = callee.getParameter(0).getMember(prop).getASink().asExpr() and + this = callee.getParameter(0).getMember(prop).asSink().asExpr() and ( prop = "user" and kind = "user name" or @@ -200,7 +200,7 @@ private module Postgres { QueryString() { this = any(QueryCall qc).getAQueryArgument().asExpr() or - this = API::moduleImport("pg-cursor").getParameter(0).getASink().asExpr() + this = API::moduleImport("pg-cursor").getParameter(0).asSink().asExpr() } } @@ -210,9 +210,9 @@ private module Postgres { Credentials() { exists(string prop | - this = [newClient(), newPool()].getParameter(0).getMember(prop).getASink().asExpr() + this = [newClient(), newPool()].getParameter(0).getMember(prop).asSink().asExpr() or - this = pgPromise().getParameter(0).getMember(prop).getASink().asExpr() + this = pgPromise().getParameter(0).getMember(prop).asSink().asExpr() | prop = "user" and kind = "user name" or @@ -383,7 +383,7 @@ private module Sqlite { /** A call to a Sqlite query method. */ private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode { QueryCall() { - this = getAChainingQueryCall().getASource() + this = getAChainingQueryCall().asSource() or this = database().getMember("prepare").getACall() } @@ -495,7 +495,7 @@ private module MsSql { or callee = mssql().getMember("ConnectionPool") ) and - this = callee.getParameter(0).getMember(prop).getASink().asExpr() and + this = callee.getParameter(0).getMember(prop).asSink().asExpr() and ( prop = "user" and kind = "user name" or diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Snapdragon.qll b/javascript/ql/lib/semmle/javascript/frameworks/Snapdragon.qll index 2569d205473..6f460a842ff 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Snapdragon.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Snapdragon.qll @@ -27,7 +27,7 @@ private module Snapdragon { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { exists(string methodName, API::CallNode set, API::CallNode call, API::Node base | // the handler, registered with a call to `.set`. - set = getSetCall+(base.getMember(methodName + "r")).getASource() and + set = getSetCall+(base.getMember(methodName + "r")).asSource() and // the snapdragon instance. The API is chaining, you can also use the instance directly. base = API::moduleImport("snapdragon").getInstance() and methodName = ["parse", "compile"] and @@ -47,7 +47,7 @@ private module Snapdragon { or // for compiler handlers the input is the first parameter. methodName = "compile" and - succ = set.getParameter(1).getParameter(0).getASource() + succ = set.getParameter(1).getParameter(0).asSource() ) ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/SocketIO.qll b/javascript/ql/lib/semmle/javascript/frameworks/SocketIO.qll index 7e0cc61c3ca..e48d674fa74 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/SocketIO.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/SocketIO.qll @@ -41,7 +41,7 @@ module SocketIO { class ServerObject extends SocketIOObject { API::Node node; - ServerObject() { node = newServer() and this = node.getASource() } + ServerObject() { node = newServer() and this = node.asSource() } /** Gets the Api node for this server. */ API::Node asApiNode() { result = node } @@ -119,7 +119,7 @@ module SocketIO { API::Node node; NamespaceBase() { - this = node.getASource() and + this = node.asSource() and exists(ServerObject srv | // namespace lookup on `srv` node = srv.asApiNode().getMember("sockets") and diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll b/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll index b081ec9e782..90adf7d7de6 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll @@ -233,7 +233,7 @@ module Templating { /** Gets an API node that may flow to `succ` through a template instantiation. */ private API::Node getTemplateInput(DataFlow::SourceNode succ) { exists(TemplateInstantiation inst, API::Node base, string name | - base.getASink() = inst.getTemplateParamsNode() and + base.asSink() = inst.getTemplateParamsNode() and result = base.getMember(name) and succ = inst.getTemplateFile() @@ -244,7 +244,7 @@ module Templating { ) or exists(TemplateInstantiation inst, string accessPath | - result.getASink() = inst.getTemplateParamForValue(accessPath) and + result.asSink() = inst.getTemplateParamForValue(accessPath) and succ = inst.getTemplateFile() .getAnImportedFile*() @@ -261,7 +261,7 @@ module Templating { private class TemplateInputStep extends DataFlow::SharedFlowStep { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { - getTemplateInput(succ).getASink() = pred + getTemplateInput(succ).asSink() = pred } } @@ -321,7 +321,7 @@ module Templating { result = this.getStringValue() or exists(API::Node node | - this = node.getASink() and + this = node.asSink() and result = node.getAValueReachingSink().getStringValue() ) } @@ -716,7 +716,7 @@ module Templating { override TemplateSyntax getTemplateSyntax() { result.getAPackageName() = engine } override DataFlow::SourceNode getOutput() { - result = this.getParameter([1, 2]).getParameter(1).getASource() + result = this.getParameter([1, 2]).getParameter(1).asSource() or not exists(this.getParameter([1, 2]).getParameter(1)) and result = this diff --git a/javascript/ql/lib/semmle/javascript/frameworks/TorrentLibraries.qll b/javascript/ql/lib/semmle/javascript/frameworks/TorrentLibraries.qll index 384d6402244..29c100234bf 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/TorrentLibraries.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/TorrentLibraries.qll @@ -21,7 +21,7 @@ module ParseTorrent { node = mod().getReturn() or node = mod().getMember("remote").getParameter(1).getParameter(1) ) and - this = node.getASource() + this = node.asSource() } /** Gets the API node for this torrent object. */ @@ -40,7 +40,7 @@ module ParseTorrent { UserControlledTorrentInfo() { exists(API::Node read | read = any(ParsedTorrent t).asApiNode().getAMember() and - this = read.getASource() + this = read.asSource() | exists(string prop | not ( diff --git a/javascript/ql/lib/semmle/javascript/frameworks/TrustedTypes.qll b/javascript/ql/lib/semmle/javascript/frameworks/TrustedTypes.qll index 3845a30e950..b1b2cc6cf0a 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/TrustedTypes.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/TrustedTypes.qll @@ -36,7 +36,7 @@ module TrustedTypes { private class PolicyInputStep extends DataFlow::SharedFlowStep { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { exists(PolicyCreation policy, string method | - pred = policy.getReturn().getMember(method).getParameter(0).getASink() and + pred = policy.getReturn().getMember(method).getParameter(0).asSink() and succ = policy.getPolicyCallback(method).getParameter(0) ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/UriLibraries.qll b/javascript/ql/lib/semmle/javascript/frameworks/UriLibraries.qll index 6e59884237d..26fff795250 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/UriLibraries.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/UriLibraries.qll @@ -190,7 +190,7 @@ module Querystringify { * Gets a data flow source node for member `name` of the querystringify library. */ DataFlow::SourceNode querystringifyMember(string name) { - result = querystringify().getMember(name).getASource() + result = querystringify().getMember(name).asSource() } /** Gets an API node referring to the `querystringify` module. */ diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll index e4d61ec8ed6..95a372025e2 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll @@ -37,7 +37,7 @@ module Vue { /** * Gets a reference to the 'Vue' object. */ - DataFlow::SourceNode vue() { result = vueLibrary().getASource() } + DataFlow::SourceNode vue() { result = vueLibrary().asSource() } /** Gets an API node referring to a component or `Vue`. */ private API::Node component() { @@ -173,7 +173,7 @@ module Vue { /** Gets a component which is extended by this one. */ Component getABaseComponent() { result.getComponentRef().getAValueReachableFromSource() = - getOwnOptions().getMember(["extends", "mixins"]).getASink() + getOwnOptions().getMember(["extends", "mixins"]).asSink() } /** @@ -196,7 +196,7 @@ module Vue { * Gets the options passed to the Vue object, such as the object literal `{...}` in `new Vue{{...})` * or the default export of a single-file component. */ - deprecated DataFlow::Node getOwnOptionsObject() { result = getOwnOptions().getASink() } + deprecated DataFlow::Node getOwnOptionsObject() { result = getOwnOptions().asSink() } /** * Gets the class implementing this Vue component, if any. @@ -210,13 +210,13 @@ module Vue { * Gets the node for option `name` for this component, not including * those from extended objects and mixins. */ - DataFlow::Node getOwnOption(string name) { result = getOwnOptions().getMember(name).getASink() } + DataFlow::Node getOwnOption(string name) { result = getOwnOptions().getMember(name).asSink() } /** * Gets the node for option `name` for this component, including those from * extended objects and mixins. */ - DataFlow::Node getOption(string name) { result = getOptions().getMember(name).getASink() } + DataFlow::Node getOption(string name) { result = getOptions().getMember(name).asSink() } /** * Gets a source node flowing into the option `name` of this component, including those from @@ -324,10 +324,10 @@ module Vue { } /** Gets an API node referring to an instance of this component. */ - API::Node getInstance() { result.getASource() = getABoundFunction().getReceiver() } + API::Node getInstance() { result.asSource() = getABoundFunction().getReceiver() } /** Gets a data flow node referring to an instance of this component. */ - DataFlow::SourceNode getAnInstanceRef() { result = getInstance().getASource() } + DataFlow::SourceNode getAnInstanceRef() { result = getInstance().asSource() } pragma[noinline] private DataFlow::PropWrite getAPropertyValueWrite(string name) { @@ -527,13 +527,13 @@ module Vue { // of the .vue file. exists(Import imprt | imprt.getImportedPath().resolve() = file and - result.getASource() = imprt.getImportedModuleNode() + result.asSource() = imprt.getImportedModuleNode() ) } override API::Node getOwnOptions() { // Use the entry point generated by `VueExportEntryPoint` - result.getASink() = getModule().getDefaultOrBulkExport() + result.asSink() = getModule().getDefaultOrBulkExport() } override string toString() { result = file.toString() } @@ -689,7 +689,7 @@ module Vue { t.start() and ( exists(API::Node router | router = API::moduleImport("vue-router") | - result = router.getInstance().getMember("currentRoute").getASource() + result = router.getInstance().getMember("currentRoute").asSource() or result = router @@ -697,13 +697,12 @@ module Vue { .getMember(["beforeEach", "beforeResolve", "afterEach"]) .getParameter(0) .getParameter([0, 1]) - .getASource() + .asSource() or - result = - router.getParameter(0).getMember("scrollBehavior").getParameter([0, 1]).getASource() + result = router.getParameter(0).getMember("scrollBehavior").getParameter([0, 1]).asSource() ) or - result = routeConfig().getMember("beforeEnter").getParameter([0, 1]).getASource() + result = routeConfig().getMember("beforeEnter").getParameter([0, 1]).asSource() or exists(Component c | result = c.getABoundFunction().getAFunctionValue().getReceiver().getAPropertyRead("$route") diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Vuex.qll b/javascript/ql/lib/semmle/javascript/frameworks/Vuex.qll index c6d6be5f768..71132fb531d 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Vuex.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Vuex.qll @@ -118,10 +118,10 @@ module Vuex { Vue::Component getVueComponent() { exists(DataFlow::ObjectLiteralNode obj | obj.getASpreadProperty() = getReturn().getAValueReachableFromSource() and - result.getOwnOptions().getAMember().getASink() = obj + result.getOwnOptions().getAMember().asSink() = obj ) or - result.getOwnOptions().getAMember().getASink() = this + result.getOwnOptions().getAMember().asSink() = this } } @@ -147,7 +147,7 @@ module Vuex { /** Gets a value that is returned by a getter registered with the given name. */ private DataFlow::Node getterPred(string name) { exists(string prefix, string prop | - result = storeConfigObject(prefix).getMember("getters").getMember(prop).getReturn().getASink() and + result = storeConfigObject(prefix).getMember("getters").getMember(prop).getReturn().asSink() and name = prefix + prop ) } @@ -155,12 +155,12 @@ module Vuex { /** Gets a property access that may receive the produced by a getter of the given name. */ private DataFlow::Node getterSucc(string name) { exists(string prefix, string prop | - result = storeRef(prefix).getMember("getters").getMember(prop).getASource() and + result = storeRef(prefix).getMember("getters").getMember(prop).asSource() and prop != "*" and name = prefix + prop ) or - result = getAMappedAccess("mapGetters", name).getASource() + result = getAMappedAccess("mapGetters", name).asSource() } /** Holds if `pred -> succ` is a step from a getter function to a relevant property access. */ @@ -225,7 +225,7 @@ module Vuex { or // this.name(payload) // methods: {...mapMutations(['name'])} } - result = getAMappedAccess(getMapHelperForCommitKind(kind), name).getParameter(0).getASink() + result = getAMappedAccess(getMapHelperForCommitKind(kind), name).getParameter(0).asSink() } /** Gets a node that refers the payload of a committed mutation with the given `name.` */ @@ -239,7 +239,7 @@ module Vuex { .getMember(getStorePropForCommitKind(kind)) .getMember(prop) .getParameter(1) - .getASource() and + .asSource() and prop != "*" and name = prefix + prop ) @@ -294,7 +294,7 @@ module Vuex { /** Gets a value that flows into the given access path of the state. */ DataFlow::Node stateMutationPred(string path) { - result = stateRefByAccessPath(path).getASink() + result = stateRefByAccessPath(path).asSink() or exists(ExtendCall call, string base, string prop | call.getDestinationOperand() = stateRefByAccessPath(base).getAValueReachableFromSource() and @@ -304,7 +304,7 @@ module Vuex { } /** Gets a value that refers to the given access path of the state. */ - DataFlow::Node stateMutationSucc(string path) { result = stateRefByAccessPath(path).getASource() } + DataFlow::Node stateMutationSucc(string path) { result = stateRefByAccessPath(path).asSource() } /** Holds if `pred -> succ` is a step from state mutation to state access. */ predicate stateMutationStep(DataFlow::Node pred, DataFlow::Node succ) { @@ -324,7 +324,7 @@ module Vuex { exists(MapHelperCall call | call.getHelperName() = "mapState" and component = call.getVueComponent() and - result = call.getLastParameter().getMember(name).getReturn().getASink() + result = call.getLastParameter().getMember(name).getReturn().asSink() ) } @@ -335,7 +335,7 @@ module Vuex { predicate mapStateHelperStep(DataFlow::Node pred, DataFlow::Node succ) { exists(Vue::Component component, string name | pred = mapStateHelperPred(component, name) and - succ = pragma[only_bind_out](component).getInstance().getMember(name).getASource() + succ = pragma[only_bind_out](component).getInstance().getMember(name).asSource() ) } @@ -377,7 +377,7 @@ module Vuex { /** Gets a package that can be considered an entry point for a Vuex app. */ private PackageJson entryPointPackage() { - result = getPackageJson(storeRef().getASource().getFile()) + result = getPackageJson(storeRef().asSource().getFile()) or // Any package that imports a store-creating package is considered a potential entry point. packageDependsOn(result, entryPointPackage()) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/XmlParsers.qll b/javascript/ql/lib/semmle/javascript/frameworks/XmlParsers.qll index 665825c3cad..355ee485246 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/XmlParsers.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/XmlParsers.qll @@ -100,7 +100,7 @@ module XML { } override DataFlow::Node getAResult() { - result = [doc(), element(), attr()].getASource() + result = [doc(), element(), attr()].asSource() or result = element().getMember(["name", "text"]).getACall() or @@ -282,7 +282,7 @@ module XML { override DataFlow::Node getAResult() { result = - parser.getReturn().getMember(any(string s | s.matches("on%"))).getAParameter().getASource() + parser.getReturn().getMember(any(string s | s.matches("on%"))).getAParameter().asSource() } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/ModelsAsData.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/ModelsAsData.qll index 3276a0feb30..bbed827c3e5 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/ModelsAsData.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/ModelsAsData.qll @@ -26,7 +26,7 @@ import Shared::ModelOutput as ModelOutput * A remote flow source originating from a CSV source row. */ private class RemoteFlowSourceFromCsv extends RemoteFlowSource { - RemoteFlowSourceFromCsv() { this = ModelOutput::getASourceNode("remote").getASource() } + RemoteFlowSourceFromCsv() { this = ModelOutput::getASourceNode("remote").asSource() } override string getSourceType() { result = "Remote flow" } } @@ -37,8 +37,8 @@ private class RemoteFlowSourceFromCsv extends RemoteFlowSource { private predicate summaryStepNodes(DataFlow::Node pred, DataFlow::Node succ, string kind) { exists(API::Node predNode, API::Node succNode | Specific::summaryStep(predNode, succNode, kind) and - pred = predNode.getASink() and - succ = succNode.getASource() + pred = predNode.asSink() and + succ = succNode.asSource() ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll index 5ab12d0aab2..861a44a2cfc 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -149,7 +149,7 @@ API::Node getExtraSuccessorFromInvoke(API::InvokeNode node, AccessPathToken toke or token.getName() = "Argument" and token.getAnArgument() = "this" and - result.getASink() = node.(DataFlow::CallNode).getReceiver() + result.asSink() = node.(DataFlow::CallNode).getReceiver() } /** diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/DomBasedXssCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/DomBasedXssCustomizations.qll index 879a03ca029..b23f52d7c22 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/DomBasedXssCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/DomBasedXssCustomizations.qll @@ -49,7 +49,7 @@ module DomBasedXss { or // A construction of a JSDOM object (server side DOM), where scripts are allowed. exists(DataFlow::NewNode instance | - instance = API::moduleImport("jsdom").getMember("JSDOM").getInstance().getASource() and + instance = API::moduleImport("jsdom").getMember("JSDOM").getInstance().asSource() and this = instance.getArgument(0) and instance.getOptionArgument(1, "runScripts").mayHaveStringValue("dangerously") ) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/ExceptionXssCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/ExceptionXssCustomizations.qll index 26c5ded744f..4605de869a0 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/ExceptionXssCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/ExceptionXssCustomizations.qll @@ -62,7 +62,7 @@ module ExceptionXss { */ private class JsonSchemaValidationError extends Source { JsonSchemaValidationError() { - this = any(JsonSchema::Ajv::Instance i).getAValidationError().getASource() + this = any(JsonSchema::Ajv::Instance i).getAValidationError().asSource() or this = any(JsonSchema::Joi::JoiValidationErrorRead r).getAValidationResultAccess(_) } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll index b627f8fadac..fb663a755ed 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/ExternalAPIUsedWithUntrustedDataCustomizations.qll @@ -48,7 +48,7 @@ module ExternalApiUsedWithUntrustedData { } /** Holds if `node` corresponds to a deep object argument. */ - private predicate isDeepObjectSink(API::Node node) { node.getASink() instanceof DeepObjectSink } + private predicate isDeepObjectSink(API::Node node) { node.asSink() instanceof DeepObjectSink } /** * A sanitizer for data flowing to an external API. @@ -165,9 +165,9 @@ module ExternalApiUsedWithUntrustedData { not param = base.getReceiver() | result = param and - name = param.getASource().asExpr().(Parameter).getName() + name = param.asSource().asExpr().(Parameter).getName() or - param.getASource().asExpr() instanceof DestructuringPattern and + param.asSource().asExpr() instanceof DestructuringPattern and result = param.getMember(name) ) } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/IndirectCommandInjectionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/IndirectCommandInjectionCustomizations.qll index 9a0fb9c4e2c..108ce5d8e62 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/IndirectCommandInjectionCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/IndirectCommandInjectionCustomizations.qll @@ -74,7 +74,7 @@ module IndirectCommandInjection { ].getMember("parse").getACall() or // `require('commander').myCmdArgumentName` - this = commander().getAMember().getASource() + this = commander().getAMember().asSource() or // `require('commander').opt()` => `{a: ..., b: ...}` this = commander().getMember("opts").getACall() diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll index 4c3debcaeea..f1a27697725 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll @@ -152,7 +152,7 @@ abstract class RateLimitingMiddleware extends DataFlow::SourceNode { * A rate limiter constructed using the `express-rate-limit` package. */ class ExpressRateLimit extends RateLimitingMiddleware { - ExpressRateLimit() { this = API::moduleImport("express-rate-limit").getReturn().getASource() } + ExpressRateLimit() { this = API::moduleImport("express-rate-limit").getReturn().asSource() } } /** @@ -160,7 +160,7 @@ class ExpressRateLimit extends RateLimitingMiddleware { */ class BruteForceRateLimit extends RateLimitingMiddleware { BruteForceRateLimit() { - this = API::moduleImport("express-brute").getInstance().getMember("prevent").getASource() + this = API::moduleImport("express-brute").getInstance().getMember("prevent").asSource() } } @@ -172,7 +172,7 @@ class BruteForceRateLimit extends RateLimitingMiddleware { */ class RouteHandlerLimitedByExpressLimiter extends RateLimitingMiddleware { RouteHandlerLimitedByExpressLimiter() { - this = API::moduleImport("express-limiter").getReturn().getReturn().getASource() + this = API::moduleImport("express-limiter").getReturn().getReturn().asSource() } override Routing::Node getRoutingNode() { @@ -209,7 +209,7 @@ class RateLimiterFlexibleRateLimiter extends DataFlow::FunctionNode { rateLimiterClass = API::moduleImport("rate-limiter-flexible").getMember(rateLimiterClassName) and rateLimiterConsume = rateLimiterClass.getInstance().getMember("consume") and request.getParameter() = getRouteHandlerParameter(this.getFunction(), "request") and - request.getAPropertyRead().flowsTo(rateLimiterConsume.getAParameter().getASink()) + request.getAPropertyRead().flowsTo(rateLimiterConsume.getAParameter().asSink()) ) } } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/RemoteFlowSources.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/RemoteFlowSources.qll index 294958504f7..bb60b5bc23e 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/RemoteFlowSources.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/RemoteFlowSources.qll @@ -173,7 +173,7 @@ private class ExternalRemoteFlowSourceSpecEntryPoint extends API::EntryPoint { private class ExternalRemoteFlowSource extends RemoteFlowSource { RemoteFlowSourceAccessPath ap; - ExternalRemoteFlowSource() { Stages::Taint::ref() and this = ap.resolve().getASource() } + ExternalRemoteFlowSource() { Stages::Taint::ref() and this = ap.resolve().asSource() } override string getSourceType() { result = ap.getSourceType() } } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll index dd634de28ea..42ffe2ea3c3 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll @@ -51,7 +51,7 @@ module SqlInjection { this = any(LdapJS::ClientCall call).getArgument(0) or // A search options object, which contains a filter and a baseDN. - this = any(LdapJS::SearchOptions opt).getASink() + this = any(LdapJS::SearchOptions opt).asSink() or // A call to "parseDN", which parses a DN from a string. this = LdapJS::ldapjs().getMember("parseDN").getACall().getArgument(0) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll index cb93bd39432..03b1211326a 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll @@ -681,7 +681,7 @@ module TaintedPath { .getMember(["pdf", "screenshot"]) .getParameter(0) .getMember("path") - .getASink() + .asSink() } } @@ -702,7 +702,7 @@ module TaintedPath { .getACall() .getParameter(1) .getMember("config") - .getASink() + .asSink() } } @@ -716,7 +716,7 @@ module TaintedPath { .getMember(["readPackageAsync", "readPackageSync"]) .getParameter(0) .getMember("cwd") - .getASink() + .asSink() } } @@ -726,8 +726,8 @@ module TaintedPath { private class ShellCwdSink extends TaintedPath::Sink { ShellCwdSink() { exists(SystemCommandExecution sys, API::Node opts | - opts.getASink() = sys.getOptionsArg() and // assuming that an API::Node exists here. - this = opts.getMember("cwd").getASink() + opts.asSink() = sys.getOptionsArg() and // assuming that an API::Node exists here. + this = opts.getMember("cwd").asSink() ) } } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/XssThroughDomCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/XssThroughDomCustomizations.qll index 792311dd8a9..3f0f569eff6 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/XssThroughDomCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/XssThroughDomCustomizations.qll @@ -208,7 +208,7 @@ module XssThroughDom { exists(API::Node useForm | useForm = API::moduleImport("react-hook-form").getMember("useForm").getReturn() | - this = useForm.getMember("handleSubmit").getParameter(0).getParameter(0).getASource() + this = useForm.getMember("handleSubmit").getParameter(0).getParameter(0).asSource() or this = useForm.getMember("getValues").getACall() ) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/ZipSlipCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/ZipSlipCustomizations.qll index 2e417d018db..1cb58609d13 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/ZipSlipCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/ZipSlipCustomizations.qll @@ -103,7 +103,7 @@ module ZipSlip { class JSZipFilesSource extends Source instanceof DynamicPropertyAccess::EnumeratedPropName { JSZipFilesSource() { super.getSourceObject() = - API::moduleImport("jszip").getInstance().getMember("files").getASource() + API::moduleImport("jszip").getInstance().getMember("files").asSource() } } @@ -116,7 +116,7 @@ module ZipSlip { .getMember(["forEach", "filter"]) .getParameter(0) .getParameter(0) - .getASource() + .asSource() } } diff --git a/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.ql b/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.ql index c8a36ed78f3..7fd76a635ec 100644 --- a/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.ql +++ b/javascript/ql/src/Security/CWE-295/DisablingCertificateValidation.ql @@ -45,7 +45,7 @@ where or // the same thing, but with API-nodes if they happen to be available exists(API::Node tlsInvk | tlsInvk.getAnInvocation() = tlsInvocation() | - disable.getRhs() = tlsInvk.getAParameter().getMember("rejectUnauthorized").getASink() + disable.getRhs() = tlsInvk.getAParameter().getMember("rejectUnauthorized").asSink() ) ) and disable.getRhs().(AnalyzedNode).getTheBooleanValue() = false diff --git a/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql b/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql index 1a366c66710..6240615a6e7 100644 --- a/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql +++ b/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql @@ -143,7 +143,7 @@ API::CallNode passportAuthenticateCall() { */ API::CallNode nonSessionBasedAuthMiddleware() { result = passportAuthenticateCall() and - result.getParameter(1).getMember("session").getASink().mayHaveBooleanValue(false) + result.getParameter(1).getMember("session").asSink().mayHaveBooleanValue(false) } /** diff --git a/javascript/ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql b/javascript/ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql index 0057d186e52..4f07f9be7fe 100644 --- a/javascript/ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql +++ b/javascript/ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql @@ -12,4 +12,4 @@ import javascript import meta.MetaMetrics -select projectRoot(), count(any(API::Node nd).getASink()) +select projectRoot(), count(any(API::Node nd).asSink()) diff --git a/javascript/ql/test/ApiGraphs/VerifyAssertions.qll b/javascript/ql/test/ApiGraphs/VerifyAssertions.qll index 0a472c090a6..2f4b80ee1c6 100644 --- a/javascript/ql/test/ApiGraphs/VerifyAssertions.qll +++ b/javascript/ql/test/ApiGraphs/VerifyAssertions.qll @@ -21,7 +21,7 @@ import javascript private DataFlow::Node getNode(API::Node nd, string kind) { kind = "def" and - result = nd.getASink() + result = nd.asSink() or kind = "use" and result = nd.getAValueReachableFromSource() diff --git a/javascript/ql/test/ApiGraphs/call-nodes/test.ql b/javascript/ql/test/ApiGraphs/call-nodes/test.ql index d4f086923a3..8f0ea4bfc8b 100644 --- a/javascript/ql/test/ApiGraphs/call-nodes/test.ql +++ b/javascript/ql/test/ApiGraphs/call-nodes/test.ql @@ -3,9 +3,9 @@ import javascript class FooCall extends API::CallNode { FooCall() { this = API::moduleImport("mylibrary").getMember("foo").getACall() } - DataFlow::Node getFirst() { result = getParameter(0).getMember("value").getASink() } + DataFlow::Node getFirst() { result = getParameter(0).getMember("value").asSink() } - DataFlow::Node getSecond() { result = getParameter(1).getMember("value").getASink() } + DataFlow::Node getSecond() { result = getParameter(1).getMember("value").asSink() } } query predicate values(FooCall call, int first, int second) { diff --git a/javascript/ql/test/ApiGraphs/typed/NodeOfType.ql b/javascript/ql/test/ApiGraphs/typed/NodeOfType.ql index b703739ff04..10c5133f9a8 100644 --- a/javascript/ql/test/ApiGraphs/typed/NodeOfType.ql +++ b/javascript/ql/test/ApiGraphs/typed/NodeOfType.ql @@ -1,4 +1,4 @@ import javascript from string mod, string tp -select mod, tp, API::Node::ofType(mod, tp).getASource() +select mod, tp, API::Node::ofType(mod, tp).asSource() diff --git a/javascript/ql/test/library-tests/Routing/test.ql b/javascript/ql/test/library-tests/Routing/test.ql index ea546c0722d..b427f710894 100644 --- a/javascript/ql/test/library-tests/Routing/test.ql +++ b/javascript/ql/test/library-tests/Routing/test.ql @@ -9,12 +9,12 @@ class Taint extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node node) { node.(DataFlow::CallNode).getCalleeName() = "source" or - node = testInstance().getMember("getSource").getReturn().getASource() + node = testInstance().getMember("getSource").getReturn().asSource() } override predicate isSink(DataFlow::Node node) { node = any(DataFlow::CallNode call | call.getCalleeName() = "sink").getAnArgument() or - node = testInstance().getMember("getSink").getAParameter().getASink() + node = testInstance().getMember("getSink").getAParameter().asSink() } } diff --git a/javascript/ql/test/library-tests/frameworks/data/test.ql b/javascript/ql/test/library-tests/frameworks/data/test.ql index ee889cb9161..ff385f3afff 100644 --- a/javascript/ql/test/library-tests/frameworks/data/test.ql +++ b/javascript/ql/test/library-tests/frameworks/data/test.ql @@ -62,13 +62,13 @@ class BasicTaintTracking extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source.(DataFlow::CallNode).getCalleeName() = "source" or - source = ModelOutput::getASourceNode("test-source").getASource() + source = ModelOutput::getASourceNode("test-source").asSource() } override predicate isSink(DataFlow::Node sink) { sink = any(DataFlow::CallNode call | call.getCalleeName() = "sink").getAnArgument() or - sink = ModelOutput::getASinkNode("test-sink").getASink() + sink = ModelOutput::getASinkNode("test-sink").asSink() } } @@ -77,7 +77,7 @@ query predicate taintFlow(DataFlow::Node source, DataFlow::Node sink) { } query predicate isSink(DataFlow::Node node, string kind) { - node = ModelOutput::getASinkNode(kind).getASink() + node = ModelOutput::getASinkNode(kind).asSink() } class SyntaxErrorTest extends ModelInput::SinkModelCsv { From 87cbf7b2162d1d7cafeb2049e7072c218a5e54a2 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 24 May 2022 11:38:19 +0200 Subject: [PATCH 081/125] JS: Update ATM code --- .../experimental/adaptivethreatmodeling/EndpointFeatures.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointFeatures.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointFeatures.qll index e2ce4753fb4..f146f569684 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointFeatures.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointFeatures.qll @@ -144,9 +144,9 @@ private module AccessPaths { not param = base.getReceiver() | result = param and - name = param.getASource().asExpr().(Parameter).getName() + name = param.asSource().asExpr().(Parameter).getName() or - param.getASource().asExpr() instanceof DestructuringPattern and + param.asSource().asExpr() instanceof DestructuringPattern and result = param.getMember(name) ) } From 572f247fd9b20d6740485aa0c09469ac9ff5eb48 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 24 May 2022 12:34:29 +0200 Subject: [PATCH 082/125] bump the supported ECMAScript version to 2022 --- docs/codeql/support/reusables/versions-compilers.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/support/reusables/versions-compilers.rst b/docs/codeql/support/reusables/versions-compilers.rst index a5f68cb64e1..b1e0350aa49 100644 --- a/docs/codeql/support/reusables/versions-compilers.rst +++ b/docs/codeql/support/reusables/versions-compilers.rst @@ -20,7 +20,7 @@ Java,"Java 7 to 18 [4]_","javac (OpenJDK and Oracle JDK), Eclipse compiler for Java (ECJ) [5]_",``.java`` - JavaScript,ECMAScript 2021 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [6]_" + JavaScript,ECMAScript 2022 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [6]_" Python,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10",Not applicable,``.py`` Ruby [7]_,"up to 3.0.2",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``" TypeScript [8]_,"2.6-4.6",Standard TypeScript compiler,"``.ts``, ``.tsx``" From fc25d14af7aaebff28c0c1a80f4e8c0a6839ae37 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 24 May 2022 12:37:28 +0200 Subject: [PATCH 083/125] add change note --- javascript/ql/lib/change-notes/2022-05-24-ecmascript-2022.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 javascript/ql/lib/change-notes/2022-05-24-ecmascript-2022.md diff --git a/javascript/ql/lib/change-notes/2022-05-24-ecmascript-2022.md b/javascript/ql/lib/change-notes/2022-05-24-ecmascript-2022.md new file mode 100644 index 00000000000..389b7c9044b --- /dev/null +++ b/javascript/ql/lib/change-notes/2022-05-24-ecmascript-2022.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* All new ECMAScript 2022 features are now supported. From 1717d17fb3cb236d650bbca7d8643073901dee9e Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 24 May 2022 12:41:27 +0200 Subject: [PATCH 084/125] add flow step for Array.prototype.at --- .../ql/lib/semmle/javascript/Arrays.qll | 6 +- .../library-tests/Arrays/DataFlow.expected | 1 + .../ql/test/library-tests/Arrays/arrays.js | 2 + .../library-tests/Arrays/printAst.expected | 207 ++++++++++-------- .../TaintTracking/BasicTaintTracking.expected | 1 + .../library-tests/TaintTracking/arrays.js | 2 + 6 files changed, 129 insertions(+), 90 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/Arrays.qll b/javascript/ql/lib/semmle/javascript/Arrays.qll index 0433b1b5681..c129e9a31b2 100644 --- a/javascript/ql/lib/semmle/javascript/Arrays.qll +++ b/javascript/ql/lib/semmle/javascript/Arrays.qll @@ -75,7 +75,7 @@ module ArrayTaintTracking { succ.(DataFlow::SourceNode).getAMethodCall("splice") = call or // `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`. - call.(DataFlow::MethodCallNode).calls(pred, ["pop", "shift", "slice", "splice"]) and + call.(DataFlow::MethodCallNode).calls(pred, ["pop", "shift", "slice", "splice", "at"]) and succ = call or // `e = Array.from(x)`: if `x` is tainted, then so is `e`. @@ -199,13 +199,13 @@ private module ArrayDataFlow { } /** - * A step for retrieving an element from an array using `.pop()` or `.shift()`. + * A step for retrieving an element from an array using `.pop()`, `.shift()`, or `.at()`. * E.g. `array.pop()`. */ private class ArrayPopStep extends DataFlow::SharedFlowStep { override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) { exists(DataFlow::MethodCallNode call | - call.getMethodName() = ["pop", "shift"] and + call.getMethodName() = ["pop", "shift", "at"] and prop = arrayElement() and obj = call.getReceiver() and element = call diff --git a/javascript/ql/test/library-tests/Arrays/DataFlow.expected b/javascript/ql/test/library-tests/Arrays/DataFlow.expected index fd98b66d4db..2f5179075cf 100644 --- a/javascript/ql/test/library-tests/Arrays/DataFlow.expected +++ b/javascript/ql/test/library-tests/Arrays/DataFlow.expected @@ -11,6 +11,7 @@ | arrays.js:2:16:2:23 | "source" | arrays.js:74:8:74:29 | arr.fin ... llback) | | arrays.js:2:16:2:23 | "source" | arrays.js:77:8:77:35 | arrayFi ... llback) | | arrays.js:2:16:2:23 | "source" | arrays.js:81:10:81:10 | x | +| arrays.js:2:16:2:23 | "source" | arrays.js:84:8:84:17 | arr.at(-1) | | 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() | diff --git a/javascript/ql/test/library-tests/Arrays/arrays.js b/javascript/ql/test/library-tests/Arrays/arrays.js index 9b445760f47..2dfb203cd54 100644 --- a/javascript/ql/test/library-tests/Arrays/arrays.js +++ b/javascript/ql/test/library-tests/Arrays/arrays.js @@ -80,4 +80,6 @@ for (const x of uniq(arr)) { sink(x); // NOT OK } + + sink(arr.at(-1)); // NOT OK }); diff --git a/javascript/ql/test/library-tests/Arrays/printAst.expected b/javascript/ql/test/library-tests/Arrays/printAst.expected index 8364838f8bd..c6e097c770e 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:83:2 | [ParExpr] (functi ... } }) | semmle.label | [ParExpr] (functi ... } }) | -| arrays.js:1:1:83:3 | [ExprStmt] (functi ... } }); | semmle.label | [ExprStmt] (functi ... } }); | -| arrays.js:1:1:83:3 | [ExprStmt] (functi ... } }); | semmle.order | 1 | -| arrays.js:1:2:83:1 | [FunctionExpr] functio ... K } } | semmle.label | [FunctionExpr] functio ... K } } | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | semmle.label | [BlockStmt] { let ... K } } | +| arrays.js:1:1:85:2 | [ParExpr] (functi ... T OK }) | semmle.label | [ParExpr] (functi ... T OK }) | +| arrays.js:1:1:85:3 | [ExprStmt] (functi ... OK }); | semmle.label | [ExprStmt] (functi ... OK }); | +| arrays.js:1:1:85:3 | [ExprStmt] (functi ... OK }); | semmle.order | 1 | +| arrays.js:1:2:85:1 | [FunctionExpr] functio ... OT OK } | semmle.label | [FunctionExpr] functio ... OT OK } | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | semmle.label | [BlockStmt] { let ... OT OK } | | 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" | @@ -339,6 +339,17 @@ nodes | arrays.js:81:5:81:11 | [CallExpr] sink(x) | semmle.label | [CallExpr] sink(x) | | arrays.js:81:5:81:12 | [ExprStmt] sink(x); | semmle.label | [ExprStmt] sink(x); | | arrays.js:81:10:81:10 | [VarRef] x | semmle.label | [VarRef] x | +| arrays.js:84:3:84:6 | [VarRef] sink | semmle.label | [VarRef] sink | +| arrays.js:84:3:84:18 | [CallExpr] sink(arr.at(-1)) | semmle.label | [CallExpr] sink(arr.at(-1)) | +| arrays.js:84:3:84:19 | [ExprStmt] sink(arr.at(-1)); | semmle.label | [ExprStmt] sink(arr.at(-1)); | +| arrays.js:84:8:84:10 | [VarRef] arr | semmle.label | [VarRef] arr | +| arrays.js:84:8:84:13 | [DotExpr] arr.at | semmle.label | [DotExpr] arr.at | +| arrays.js:84:8:84:17 | [MethodCallExpr] arr.at(-1) | semmle.label | [MethodCallExpr] arr.at(-1) | +| arrays.js:84:12:84:13 | [Label] at | semmle.label | [Label] at | +| arrays.js:84:15:84:16 | [UnaryExpr] -1 | semmle.label | [UnaryExpr] -1 | +| arrays.js:84:16:84:16 | [Literal] 1 | semmle.label | [Literal] 1 | +| 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) | @@ -386,88 +397,90 @@ nodes | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | edges -| arrays.js:1:1:83:2 | [ParExpr] (functi ... } }) | arrays.js:1:2:83:1 | [FunctionExpr] functio ... K } } | semmle.label | 1 | -| arrays.js:1:1:83:2 | [ParExpr] (functi ... } }) | arrays.js:1:2:83:1 | [FunctionExpr] functio ... K } } | semmle.order | 1 | -| arrays.js:1:1:83:3 | [ExprStmt] (functi ... } }); | arrays.js:1:1:83:2 | [ParExpr] (functi ... } }) | semmle.label | 1 | -| arrays.js:1:1:83:3 | [ExprStmt] (functi ... } }); | arrays.js:1:1:83:2 | [ParExpr] (functi ... } }) | semmle.order | 1 | -| arrays.js:1:2:83:1 | [FunctionExpr] functio ... K } } | arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | semmle.label | 5 | -| arrays.js:1:2:83:1 | [FunctionExpr] functio ... K } } | arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | semmle.order | 5 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.label | 1 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.order | 1 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.label | 2 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.order | 2 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.label | 3 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.order | 3 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.label | 4 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.order | 4 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.label | 5 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.order | 5 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.label | 6 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.order | 6 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.label | 7 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.order | 7 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.label | 8 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.order | 8 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.label | 9 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.order | 9 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.label | 10 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.order | 10 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.label | 11 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.order | 11 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.label | 12 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.order | 12 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.label | 13 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.order | 13 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.label | 14 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.order | 14 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.label | 15 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.order | 15 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.label | 16 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.order | 16 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.label | 17 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.order | 17 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:32:3:32:29 | [DeclStmt] var arr5 = ... | semmle.label | 18 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:32:3:32:29 | [DeclStmt] var arr5 = ... | semmle.order | 18 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:33:3:33:19 | [ExprStmt] sink(arr5.pop()); | semmle.label | 19 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:33:3:33:19 | [ExprStmt] sink(arr5.pop()); | semmle.order | 19 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:35:3:35:28 | [ExprStmt] sink(ar ... pop()); | semmle.label | 20 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:35:3:35:28 | [ExprStmt] sink(ar ... pop()); | semmle.order | 20 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:37:3:37:16 | [DeclStmt] var arr6 = ... | semmle.label | 21 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:37:3:37:16 | [DeclStmt] var arr6 = ... | semmle.order | 21 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:38:3:40:3 | [ForStmt] for (va ... i]; } | semmle.label | 22 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:38:3:40:3 | [ForStmt] for (va ... i]; } | semmle.order | 22 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:41:3:41:19 | [ExprStmt] sink(arr6.pop()); | semmle.label | 23 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:41:3:41:19 | [ExprStmt] sink(arr6.pop()); | semmle.order | 23 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:44:3:47:5 | [ExprStmt] ["sourc ... . }); | semmle.label | 24 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:44:3:47:5 | [ExprStmt] ["sourc ... . }); | semmle.order | 24 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:49:3:49:15 | [ExprStmt] sink(arr[0]); | semmle.label | 25 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:49:3:49:15 | [ExprStmt] sink(arr[0]); | semmle.order | 25 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:51:3:53:3 | [ForOfStmt] for (co ... OK } | semmle.label | 26 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:51:3:53:3 | [ForOfStmt] for (co ... OK } | semmle.order | 26 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:55:3:57:3 | [ForOfStmt] for (co ... OK } | semmle.label | 27 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:55:3:57:3 | [ForOfStmt] for (co ... OK } | semmle.order | 27 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:59:3:61:3 | [ForOfStmt] for (co ... OK } | semmle.label | 28 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:59:3:61:3 | [ForOfStmt] for (co ... OK } | semmle.order | 28 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:63:3:63:16 | [DeclStmt] var arr7 = ... | semmle.label | 29 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:63:3:63:16 | [DeclStmt] var arr7 = ... | semmle.order | 29 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:64:3:64:20 | [ExprStmt] arr7.push(...arr); | semmle.label | 30 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:64:3:64:20 | [ExprStmt] arr7.push(...arr); | semmle.order | 30 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:65:3:67:3 | [ForOfStmt] for (co ... OK } | semmle.label | 31 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:65:3:67:3 | [ForOfStmt] for (co ... OK } | semmle.order | 31 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:69:3:69:42 | [DeclStmt] const arrayFrom = ... | semmle.label | 32 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:69:3:69:42 | [DeclStmt] const arrayFrom = ... | semmle.order | 32 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:70:3:72:3 | [ForOfStmt] for (co ... OK } | semmle.label | 33 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:70:3:72:3 | [ForOfStmt] for (co ... OK } | semmle.order | 33 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:74:3:74:31 | [ExprStmt] sink(ar ... back)); | semmle.label | 34 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:74:3:74:31 | [ExprStmt] sink(ar ... back)); | semmle.order | 34 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:76:3:76:42 | [DeclStmt] const arrayFind = ... | semmle.label | 35 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:76:3:76:42 | [DeclStmt] const arrayFind = ... | semmle.order | 35 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:77:3:77:37 | [ExprStmt] sink(ar ... back)); | semmle.label | 36 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:77:3:77:37 | [ExprStmt] sink(ar ... back)); | semmle.order | 36 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:79:3:79:31 | [DeclStmt] const uniq = ... | semmle.label | 37 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:79:3:79:31 | [DeclStmt] const uniq = ... | semmle.order | 37 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:80:3:82:3 | [ForOfStmt] for (co ... OK } | semmle.label | 38 | -| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:80:3:82:3 | [ForOfStmt] for (co ... OK } | semmle.order | 38 | +| arrays.js:1:1:85:2 | [ParExpr] (functi ... T OK }) | arrays.js:1:2:85:1 | [FunctionExpr] functio ... OT OK } | semmle.label | 1 | +| arrays.js:1:1:85:2 | [ParExpr] (functi ... T OK }) | arrays.js:1:2:85:1 | [FunctionExpr] functio ... OT OK } | semmle.order | 1 | +| arrays.js:1:1:85:3 | [ExprStmt] (functi ... OK }); | arrays.js:1:1:85:2 | [ParExpr] (functi ... T OK }) | semmle.label | 1 | +| arrays.js:1:1:85:3 | [ExprStmt] (functi ... OK }); | arrays.js:1:1:85:2 | [ParExpr] (functi ... T OK }) | semmle.order | 1 | +| arrays.js:1:2:85:1 | [FunctionExpr] functio ... OT OK } | arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | semmle.label | 5 | +| arrays.js:1:2:85:1 | [FunctionExpr] functio ... OT OK } | arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | semmle.order | 5 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.label | 1 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.order | 1 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.label | 2 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.order | 2 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.label | 3 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.order | 3 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.label | 4 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.order | 4 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.label | 5 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.order | 5 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.label | 6 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.order | 6 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.label | 7 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.order | 7 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.label | 8 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.order | 8 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.label | 9 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.order | 9 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.label | 10 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.order | 10 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.label | 11 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.order | 11 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.label | 12 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.order | 12 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.label | 13 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.order | 13 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.label | 14 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.order | 14 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.label | 15 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.order | 15 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.label | 16 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.order | 16 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.label | 17 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.order | 17 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:32:3:32:29 | [DeclStmt] var arr5 = ... | semmle.label | 18 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:32:3:32:29 | [DeclStmt] var arr5 = ... | semmle.order | 18 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:33:3:33:19 | [ExprStmt] sink(arr5.pop()); | semmle.label | 19 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:33:3:33:19 | [ExprStmt] sink(arr5.pop()); | semmle.order | 19 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:35:3:35:28 | [ExprStmt] sink(ar ... pop()); | semmle.label | 20 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:35:3:35:28 | [ExprStmt] sink(ar ... pop()); | semmle.order | 20 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:37:3:37:16 | [DeclStmt] var arr6 = ... | semmle.label | 21 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:37:3:37:16 | [DeclStmt] var arr6 = ... | semmle.order | 21 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:38:3:40:3 | [ForStmt] for (va ... i]; } | semmle.label | 22 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:38:3:40:3 | [ForStmt] for (va ... i]; } | semmle.order | 22 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:41:3:41:19 | [ExprStmt] sink(arr6.pop()); | semmle.label | 23 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:41:3:41:19 | [ExprStmt] sink(arr6.pop()); | semmle.order | 23 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:44:3:47:5 | [ExprStmt] ["sourc ... . }); | semmle.label | 24 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:44:3:47:5 | [ExprStmt] ["sourc ... . }); | semmle.order | 24 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:49:3:49:15 | [ExprStmt] sink(arr[0]); | semmle.label | 25 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:49:3:49:15 | [ExprStmt] sink(arr[0]); | semmle.order | 25 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:51:3:53:3 | [ForOfStmt] for (co ... OK } | semmle.label | 26 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:51:3:53:3 | [ForOfStmt] for (co ... OK } | semmle.order | 26 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:55:3:57:3 | [ForOfStmt] for (co ... OK } | semmle.label | 27 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:55:3:57:3 | [ForOfStmt] for (co ... OK } | semmle.order | 27 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:59:3:61:3 | [ForOfStmt] for (co ... OK } | semmle.label | 28 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:59:3:61:3 | [ForOfStmt] for (co ... OK } | semmle.order | 28 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:63:3:63:16 | [DeclStmt] var arr7 = ... | semmle.label | 29 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:63:3:63:16 | [DeclStmt] var arr7 = ... | semmle.order | 29 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:64:3:64:20 | [ExprStmt] arr7.push(...arr); | semmle.label | 30 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:64:3:64:20 | [ExprStmt] arr7.push(...arr); | semmle.order | 30 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:65:3:67:3 | [ForOfStmt] for (co ... OK } | semmle.label | 31 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:65:3:67:3 | [ForOfStmt] for (co ... OK } | semmle.order | 31 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:69:3:69:42 | [DeclStmt] const arrayFrom = ... | semmle.label | 32 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:69:3:69:42 | [DeclStmt] const arrayFrom = ... | semmle.order | 32 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:70:3:72:3 | [ForOfStmt] for (co ... OK } | semmle.label | 33 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:70:3:72:3 | [ForOfStmt] for (co ... OK } | semmle.order | 33 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:74:3:74:31 | [ExprStmt] sink(ar ... back)); | semmle.label | 34 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:74:3:74:31 | [ExprStmt] sink(ar ... back)); | semmle.order | 34 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:76:3:76:42 | [DeclStmt] const arrayFind = ... | semmle.label | 35 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:76:3:76:42 | [DeclStmt] const arrayFind = ... | semmle.order | 35 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:77:3:77:37 | [ExprStmt] sink(ar ... back)); | semmle.label | 36 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:77:3:77:37 | [ExprStmt] sink(ar ... back)); | semmle.order | 36 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:79:3:79:31 | [DeclStmt] const uniq = ... | semmle.label | 37 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:79:3:79:31 | [DeclStmt] const uniq = ... | semmle.order | 37 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:80:3:82:3 | [ForOfStmt] for (co ... OK } | semmle.label | 38 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:80:3:82:3 | [ForOfStmt] for (co ... OK } | semmle.order | 38 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:84:3:84:19 | [ExprStmt] sink(arr.at(-1)); | semmle.label | 39 | +| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:84:3:84:19 | [ExprStmt] sink(arr.at(-1)); | semmle.order | 39 | | 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 | @@ -1052,6 +1065,22 @@ edges | arrays.js:81:5:81:11 | [CallExpr] sink(x) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | | arrays.js:81:5:81:12 | [ExprStmt] sink(x); | arrays.js:81:5:81:11 | [CallExpr] sink(x) | semmle.label | 1 | | arrays.js:81:5:81:12 | [ExprStmt] sink(x); | arrays.js:81:5:81:11 | [CallExpr] sink(x) | semmle.order | 1 | +| arrays.js:84:3:84:18 | [CallExpr] sink(arr.at(-1)) | arrays.js:84:3:84:6 | [VarRef] sink | semmle.label | 0 | +| arrays.js:84:3:84:18 | [CallExpr] sink(arr.at(-1)) | arrays.js:84:3:84:6 | [VarRef] sink | semmle.order | 0 | +| arrays.js:84:3:84:18 | [CallExpr] sink(arr.at(-1)) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | +| arrays.js:84:3:84:18 | [CallExpr] sink(arr.at(-1)) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | +| arrays.js:84:3:84:19 | [ExprStmt] sink(arr.at(-1)); | arrays.js:84:3:84:18 | [CallExpr] sink(arr.at(-1)) | semmle.label | 1 | +| arrays.js:84:3:84:19 | [ExprStmt] sink(arr.at(-1)); | arrays.js:84:3:84:18 | [CallExpr] sink(arr.at(-1)) | semmle.order | 1 | +| arrays.js:84:8:84:13 | [DotExpr] arr.at | arrays.js:84:8:84:10 | [VarRef] arr | semmle.label | 1 | +| arrays.js:84:8:84:13 | [DotExpr] arr.at | arrays.js:84:8:84:10 | [VarRef] arr | semmle.order | 1 | +| arrays.js:84:8:84:13 | [DotExpr] arr.at | arrays.js:84:12:84:13 | [Label] at | semmle.label | 2 | +| arrays.js:84:8:84:13 | [DotExpr] arr.at | arrays.js:84:12:84:13 | [Label] at | semmle.order | 2 | +| arrays.js:84:8:84:17 | [MethodCallExpr] arr.at(-1) | arrays.js:84:8:84:13 | [DotExpr] arr.at | semmle.label | 0 | +| arrays.js:84:8:84:17 | [MethodCallExpr] arr.at(-1) | arrays.js:84:8:84:13 | [DotExpr] arr.at | semmle.order | 0 | +| arrays.js:84:8:84:17 | [MethodCallExpr] arr.at(-1) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | +| arrays.js:84:8:84:17 | [MethodCallExpr] arr.at(-1) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | +| arrays.js:84:15:84:16 | [UnaryExpr] -1 | arrays.js:84:16:84:16 | [Literal] 1 | semmle.label | 1 | +| arrays.js:84:15:84:16 | [UnaryExpr] -1 | arrays.js:84:16:84:16 | [Literal] 1 | 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 | @@ -1140,6 +1169,10 @@ edges | file://:0:0:0:0 | (Arguments) | arrays.js:80:24:80:26 | [VarRef] arr | semmle.order | 0 | | file://:0:0:0:0 | (Arguments) | arrays.js:81:10:81:10 | [VarRef] x | semmle.label | 0 | | file://:0:0:0:0 | (Arguments) | arrays.js:81:10:81:10 | [VarRef] x | semmle.order | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:84:8:84:17 | [MethodCallExpr] arr.at(-1) | semmle.label | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:84:8:84:17 | [MethodCallExpr] arr.at(-1) | semmle.order | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:84:15:84:16 | [UnaryExpr] -1 | semmle.label | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:84:15:84:16 | [UnaryExpr] -1 | 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 | diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index 12fdd69e80f..cd41461e4a8 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -17,6 +17,7 @@ typeInferenceMismatch | arrays.js:2:15:2:22 | source() | arrays.js:11:10:11:28 | union(["bla"], foo) | | arrays.js:2:15:2:22 | source() | arrays.js:14:10:14:18 | flat(foo) | | arrays.js:2:15:2:22 | source() | arrays.js:19:10:19:12 | res | +| arrays.js:2:15:2:22 | source() | arrays.js:21:10:21:19 | foo.at(-1) | | booleanOps.js:2:11:2:18 | source() | booleanOps.js:4:8:4:8 | x | | booleanOps.js:2:11:2:18 | source() | booleanOps.js:13:10:13:10 | x | | booleanOps.js:2:11:2:18 | source() | booleanOps.js:19:10:19:10 | x | diff --git a/javascript/ql/test/library-tests/TaintTracking/arrays.js b/javascript/ql/test/library-tests/TaintTracking/arrays.js index b99e2058d80..b9daf4943b3 100644 --- a/javascript/ql/test/library-tests/TaintTracking/arrays.js +++ b/javascript/ql/test/library-tests/TaintTracking/arrays.js @@ -17,4 +17,6 @@ function test() { return prev + '' + current + ''; }, ''); sink(res); // NOT OK + + sink(foo.at(-1)); // NOT OK } From 2a97dd9f6f36ab50dc8224f56a618810b6eade74 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 24 May 2022 13:47:41 +0200 Subject: [PATCH 085/125] add support for Object.hasOwn(obj, key) --- .../javascript/MembershipCandidates.qll | 8 +++ .../javascript/dataflow/TaintTracking.qll | 16 +++--- .../ql/src/Declarations/UnusedProperty.ql | 2 + .../CWE-915/PrototypePollutingFunction.ql | 14 +++++ .../TaintBarriers/tests.expected | 14 +++++ .../test/library-tests/TaintBarriers/tst.js | 19 +++++++ .../Declarations/UnusedProperty/tst.js | 8 +++ .../ServerSideUrlRedirect.expected | 15 +++++ .../CWE-601/ServerSideUrlRedirect/express.js | 16 +++++- .../PrototypePollutingFunction.expected | 57 +++++++++++++++++++ .../PrototypePollutingFunction/tests.js | 12 ++++ 11 files changed, 171 insertions(+), 10 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/MembershipCandidates.qll b/javascript/ql/lib/semmle/javascript/MembershipCandidates.qll index 6c51b487f43..57d477c182b 100644 --- a/javascript/ql/lib/semmle/javascript/MembershipCandidates.qll +++ b/javascript/ql/lib/semmle/javascript/MembershipCandidates.qll @@ -234,6 +234,14 @@ module MembershipCandidate { test = hasOwn and hasOwn.calls(membersNode, "hasOwnProperty") ) + or + exists(DataFlow::CallNode hasOwn | + hasOwn = DataFlow::globalVarRef("Object").getAMemberCall("hasOwn") + | + hasOwn.getArgument(0).asExpr() = membersNode and + this = hasOwn.getArgument(1) and + test = hasOwn.asExpr() + ) } override DataFlow::Node getTest() { result = test.flow() } diff --git a/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll index dde1c004946..0f82ec2e8e6 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll @@ -1027,18 +1027,16 @@ module TaintTracking { class WhitelistContainmentCallSanitizer extends AdditionalSanitizerGuardNode, DataFlow::MethodCallNode { WhitelistContainmentCallSanitizer() { - exists(string name | - name = "contains" or - name = "has" or - name = "hasOwnProperty" - | - this.getMethodName() = name - ) + this.getMethodName() = ["contains", "has", "hasOwnProperty", "hasOwn"] } override predicate sanitizes(boolean outcome, Expr e) { - outcome = true and - e = this.getArgument(0).asExpr() + exists(int propertyIndex | + if this.getMethodName() = "hasOwn" then propertyIndex = 1 else propertyIndex = 0 + | + outcome = true and + e = this.getArgument(propertyIndex).asExpr() + ) } override predicate appliesTo(Configuration cfg) { any() } diff --git a/javascript/ql/src/Declarations/UnusedProperty.ql b/javascript/ql/src/Declarations/UnusedProperty.ql index 19d43a09db2..1a8f9ee291f 100644 --- a/javascript/ql/src/Declarations/UnusedProperty.ql +++ b/javascript/ql/src/Declarations/UnusedProperty.ql @@ -27,6 +27,8 @@ predicate hasUnknownPropertyRead(LocalObject obj) { or exists(obj.getAPropertyRead("hasOwnProperty")) or + obj.flowsTo(DataFlow::globalVarRef("Object").getAMemberCall("hasOwn").getArgument(0)) + or exists(obj.getAPropertyRead("propertyIsEnumerable")) } diff --git a/javascript/ql/src/Security/CWE-915/PrototypePollutingFunction.ql b/javascript/ql/src/Security/CWE-915/PrototypePollutingFunction.ql index 2fb9cece66d..61483e4a9cb 100644 --- a/javascript/ql/src/Security/CWE-915/PrototypePollutingFunction.ql +++ b/javascript/ql/src/Security/CWE-915/PrototypePollutingFunction.ql @@ -286,6 +286,7 @@ class PropNameTracking extends DataFlow::Configuration { node instanceof DenyListEqualityGuard or node instanceof AllowListEqualityGuard or node instanceof HasOwnPropertyGuard or + node instanceof HasOwnGuard or node instanceof InExprGuard or node instanceof InstanceOfGuard or node instanceof TypeofGuard or @@ -355,6 +356,19 @@ class HasOwnPropertyGuard extends DataFlow::BarrierGuardNode, CallNode { } } +/** Sanitizer guard for calls to `Object.hasOwn(obj, prop)`. */ +class HasOwnGuard extends DataFlow::BarrierGuardNode, CallNode { + HasOwnGuard() { + this = DataFlow::globalVarRef("Object").getAMemberCall("hasOwn") and + // same check as in HasOwnPropertyGuard + not arePropertiesEnumerated(getArgument(0).getALocalSource()) + } + + override predicate blocks(boolean outcome, Expr e) { + e = getArgument(1).asExpr() and outcome = true + } +} + /** * A sanitizer guard for `key in dst`. * diff --git a/javascript/ql/test/library-tests/TaintBarriers/tests.expected b/javascript/ql/test/library-tests/TaintBarriers/tests.expected index 33f06a2ff23..d8cca36c7ca 100644 --- a/javascript/ql/test/library-tests/TaintBarriers/tests.expected +++ b/javascript/ql/test/library-tests/TaintBarriers/tests.expected @@ -43,6 +43,10 @@ isLabeledBarrier | ExampleConfiguration | tst.js:361:14:361:14 | v | taint | | ExampleConfiguration | tst.js:371:14:371:16 | o.p | taint | | ExampleConfiguration | tst.js:378:14:378:17 | o[p] | taint | +| ExampleConfiguration | tst.js:392:14:392:14 | v | taint | +| ExampleConfiguration | tst.js:394:14:394:16 | v.p | taint | +| ExampleConfiguration | tst.js:396:14:396:18 | v.p.q | taint | +| ExampleConfiguration | tst.js:402:14:402:14 | v | taint | isSanitizer | ExampleConfiguration | tst.js:176:18:176:18 | v | sanitizingGuard @@ -122,6 +126,13 @@ sanitizingGuard | tst.js:370:9:370:29 | o.p == ... listed" | tst.js:370:16:370:29 | "white-listed" | true | | tst.js:377:11:377:32 | o[p] == ... listed" | tst.js:377:11:377:14 | o[p] | true | | tst.js:377:11:377:32 | o[p] == ... listed" | tst.js:377:19:377:32 | "white-listed" | true | +| tst.js:391:9:391:27 | o.hasOwnProperty(v) | tst.js:391:26:391:26 | v | true | +| tst.js:393:16:393:36 | o.hasOw ... ty(v.p) | tst.js:393:33:393:35 | v.p | true | +| tst.js:395:16:395:38 | o.hasOw ... (v.p.q) | tst.js:395:33:395:37 | v.p.q | true | +| tst.js:397:16:397:36 | o.hasOw ... ty(v.p) | tst.js:397:33:397:35 | v.p | true | +| tst.js:399:16:399:41 | o.hasOw ... "p.q"]) | tst.js:399:33:399:40 | v["p.q"] | true | +| tst.js:401:16:401:34 | Object.hasOwn(o, v) | tst.js:401:30:401:30 | o | true | +| tst.js:401:16:401:34 | Object.hasOwn(o, v) | tst.js:401:33:401:33 | v | true | taintedSink | tst.js:2:13:2:20 | SOURCE() | tst.js:3:10:3:10 | v | | tst.js:2:13:2:20 | SOURCE() | tst.js:8:14:8:14 | v | @@ -186,3 +197,6 @@ taintedSink | tst.js:367:13:367:20 | SOURCE() | tst.js:373:14:373:16 | o.p | | tst.js:367:13:367:20 | SOURCE() | tst.js:380:14:380:17 | o[p] | | tst.js:367:13:367:20 | SOURCE() | tst.js:382:14:382:17 | o[p] | +| tst.js:388:13:388:20 | SOURCE() | tst.js:389:10:389:14 | v.p.q | +| tst.js:388:13:388:20 | SOURCE() | tst.js:398:14:398:14 | v | +| tst.js:388:13:388:20 | SOURCE() | tst.js:400:14:400:18 | v.p.q | diff --git a/javascript/ql/test/library-tests/TaintBarriers/tst.js b/javascript/ql/test/library-tests/TaintBarriers/tst.js index 265c5ac6210..20102605af1 100644 --- a/javascript/ql/test/library-tests/TaintBarriers/tst.js +++ b/javascript/ql/test/library-tests/TaintBarriers/tst.js @@ -383,3 +383,22 @@ function constantComparisonSanitizer2() { } } } + +function propertySanitization(o) { + var v = SOURCE(); + SINK(v.p.q); // NOT OK + + if (o.hasOwnProperty(v)) { + SINK(v); // OK + } else if (o.hasOwnProperty(v.p)) { + SINK(v.p); // OK + } else if (o.hasOwnProperty(v.p.q)) { + SINK(v.p.q); // OK + } else if (o.hasOwnProperty(v.p)) { + SINK(v); // NOT OK + } else if (o.hasOwnProperty(v["p.q"])) { + SINK(v.p.q); // NOT OK + } else if (Object.hasOwn(o, v)) { + SINK(v); // OK + } +} diff --git a/javascript/ql/test/query-tests/Declarations/UnusedProperty/tst.js b/javascript/ql/test/query-tests/Declarations/UnusedProperty/tst.js index 26cd1a110bb..847f30bd944 100644 --- a/javascript/ql/test/query-tests/Declarations/UnusedProperty/tst.js +++ b/javascript/ql/test/query-tests/Declarations/UnusedProperty/tst.js @@ -81,3 +81,11 @@ (function(){ ({ unusedProp: 42 }, 42); }); + +(function(){ + var foo = { + unused: 42 + }; + foo.unused = 42; + Object.hasOwn(foo, blab); +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected index a4654b29c27..5fa8a043558 100644 --- a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected +++ b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected @@ -50,6 +50,13 @@ nodes | express.js:146:16:146:24 | query.foo | | express.js:146:16:146:24 | query.foo | | express.js:146:16:146:24 | query.foo | +| express.js:150:7:150:34 | target | +| express.js:150:16:150:34 | req.param("target") | +| express.js:150:16:150:34 | req.param("target") | +| express.js:155:18:155:23 | target | +| express.js:155:18:155:23 | target | +| express.js:160:18:160:23 | target | +| express.js:160:18:160:23 | target | | koa.js:6:6:6:27 | url | | koa.js:6:12:6:27 | ctx.query.target | | koa.js:6:12:6:27 | ctx.query.target | @@ -140,6 +147,12 @@ edges | express.js:136:22:136:36 | req.params.user | express.js:136:16:136:36 | 'u' + r ... ms.user | | express.js:143:16:143:28 | req.query.foo | express.js:143:16:143:28 | req.query.foo | | express.js:146:16:146:24 | query.foo | express.js:146:16:146:24 | query.foo | +| express.js:150:7:150:34 | target | express.js:155:18:155:23 | target | +| express.js:150:7:150:34 | target | express.js:155:18:155:23 | target | +| express.js:150:7:150:34 | target | express.js:160:18:160:23 | target | +| express.js:150:7:150:34 | target | express.js:160:18:160:23 | target | +| express.js:150:16:150:34 | req.param("target") | express.js:150:7:150:34 | target | +| express.js:150:16:150:34 | req.param("target") | express.js:150:7:150:34 | target | | koa.js:6:6:6:27 | url | koa.js:7:15:7:17 | url | | koa.js:6:6:6:27 | url | koa.js:7:15:7:17 | url | | koa.js:6:6:6:27 | url | koa.js:8:18:8:20 | url | @@ -199,6 +212,8 @@ edges | express.js:136:16:136:36 | 'u' + r ... ms.user | express.js:136:22:136:36 | req.params.user | express.js:136:16:136:36 | 'u' + r ... ms.user | Untrusted URL redirection due to $@. | express.js:136:22:136:36 | req.params.user | user-provided value | | express.js:143:16:143:28 | req.query.foo | express.js:143:16:143:28 | req.query.foo | express.js:143:16:143:28 | req.query.foo | Untrusted URL redirection due to $@. | express.js:143:16:143:28 | req.query.foo | user-provided value | | express.js:146:16:146:24 | query.foo | express.js:146:16:146:24 | query.foo | express.js:146:16:146:24 | query.foo | Untrusted URL redirection due to $@. | express.js:146:16:146:24 | query.foo | user-provided value | +| express.js:155:18:155:23 | target | express.js:150:16:150:34 | req.param("target") | express.js:155:18:155:23 | target | Untrusted URL redirection due to $@. | express.js:150:16:150:34 | req.param("target") | user-provided value | +| express.js:160:18:160:23 | target | express.js:150:16:150:34 | req.param("target") | express.js:160:18:160:23 | target | Untrusted URL redirection due to $@. | express.js:150:16:150:34 | req.param("target") | user-provided value | | koa.js:7:15:7:17 | url | koa.js:6:12:6:27 | ctx.query.target | koa.js:7:15:7:17 | url | Untrusted URL redirection due to $@. | koa.js:6:12:6:27 | ctx.query.target | user-provided value | | koa.js:8:15:8:26 | `${url}${x}` | koa.js:6:12:6:27 | ctx.query.target | koa.js:8:15:8:26 | `${url}${x}` | Untrusted URL redirection due to $@. | koa.js:6:12:6:27 | ctx.query.target | user-provided value | | koa.js:14:16:14:18 | url | koa.js:6:12:6:27 | ctx.query.target | koa.js:14:16:14:18 | url | Untrusted URL redirection due to $@. | koa.js:6:12:6:27 | ctx.query.target | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/express.js b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/express.js index b319315b985..667d6f89f05 100644 --- a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/express.js +++ b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/express.js @@ -144,4 +144,18 @@ app.get("foo", (req, res) => { }); app.get("bar", ({query}, res) => { res.redirect(query.foo); // NOT OK -}) \ No newline at end of file +}) + +app.get('/some/path', function(req, res) { + let target = req.param("target"); + + if (SAFE_TARGETS.hasOwnProperty(target)) + res.redirect(target); // OK: request parameter is checked against whitelist + else + res.redirect(target); // NOT OK + + if (Object.hasOwn(SAFE_TARGETS, target)) + res.redirect(target); // OK: request parameter is checked against whitelist + else + res.redirect(target); // NOT OK +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/PrototypePollutingFunction.expected b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/PrototypePollutingFunction.expected index 23b21f12460..65161e5df90 100644 --- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/PrototypePollutingFunction.expected +++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/PrototypePollutingFunction.expected @@ -1478,6 +1478,31 @@ nodes | tests.js:547:24:547:28 | value | | tests.js:547:24:547:28 | value | | tests.js:547:24:547:28 | value | +| tests.js:552:35:552:37 | src | +| tests.js:552:35:552:37 | src | +| tests.js:553:14:553:16 | key | +| tests.js:553:14:553:16 | key | +| tests.js:553:14:553:16 | key | +| tests.js:557:43:557:45 | src | +| tests.js:557:43:557:45 | src | +| tests.js:557:43:557:50 | src[key] | +| tests.js:557:43:557:50 | src[key] | +| tests.js:557:43:557:50 | src[key] | +| tests.js:557:43:557:50 | src[key] | +| tests.js:557:43:557:50 | src[key] | +| tests.js:559:17:559:19 | key | +| tests.js:559:17:559:19 | key | +| tests.js:559:17:559:19 | key | +| tests.js:559:24:559:26 | src | +| tests.js:559:24:559:26 | src | +| tests.js:559:24:559:31 | src[key] | +| tests.js:559:24:559:31 | src[key] | +| tests.js:559:24:559:31 | src[key] | +| tests.js:559:24:559:31 | src[key] | +| tests.js:559:24:559:31 | src[key] | +| tests.js:559:24:559:31 | src[key] | +| tests.js:559:28:559:30 | key | +| tests.js:559:28:559:30 | key | edges | examples/PrototypePollutingFunction.js:1:16:1:18 | dst | examples/PrototypePollutingFunction.js:5:19:5:21 | dst | | examples/PrototypePollutingFunction.js:1:16:1:18 | dst | examples/PrototypePollutingFunction.js:5:19:5:21 | dst | @@ -3347,6 +3372,38 @@ edges | tests.js:545:43:545:47 | value | tests.js:542:35:542:37 | src | | tests.js:545:43:545:47 | value | tests.js:542:35:542:37 | src | | tests.js:545:43:545:47 | value | tests.js:542:35:542:37 | src | +| tests.js:552:35:552:37 | src | tests.js:557:43:557:45 | src | +| tests.js:552:35:552:37 | src | tests.js:557:43:557:45 | src | +| tests.js:552:35:552:37 | src | tests.js:559:24:559:26 | src | +| tests.js:552:35:552:37 | src | tests.js:559:24:559:26 | src | +| tests.js:553:14:553:16 | key | tests.js:559:17:559:19 | key | +| tests.js:553:14:553:16 | key | tests.js:559:17:559:19 | key | +| tests.js:553:14:553:16 | key | tests.js:559:17:559:19 | key | +| tests.js:553:14:553:16 | key | tests.js:559:17:559:19 | key | +| tests.js:553:14:553:16 | key | tests.js:559:17:559:19 | key | +| tests.js:553:14:553:16 | key | tests.js:559:17:559:19 | key | +| tests.js:553:14:553:16 | key | tests.js:559:17:559:19 | key | +| tests.js:553:14:553:16 | key | tests.js:559:28:559:30 | key | +| tests.js:553:14:553:16 | key | tests.js:559:28:559:30 | key | +| tests.js:553:14:553:16 | key | tests.js:559:28:559:30 | key | +| tests.js:553:14:553:16 | key | tests.js:559:28:559:30 | key | +| tests.js:557:43:557:45 | src | tests.js:557:43:557:50 | src[key] | +| tests.js:557:43:557:45 | src | tests.js:557:43:557:50 | src[key] | +| tests.js:557:43:557:50 | src[key] | tests.js:552:35:552:37 | src | +| tests.js:557:43:557:50 | src[key] | tests.js:552:35:552:37 | src | +| tests.js:557:43:557:50 | src[key] | tests.js:552:35:552:37 | src | +| tests.js:557:43:557:50 | src[key] | tests.js:552:35:552:37 | src | +| tests.js:557:43:557:50 | src[key] | tests.js:552:35:552:37 | src | +| tests.js:557:43:557:50 | src[key] | tests.js:552:35:552:37 | src | +| tests.js:559:24:559:26 | src | tests.js:559:24:559:31 | src[key] | +| tests.js:559:24:559:26 | src | tests.js:559:24:559:31 | src[key] | +| tests.js:559:24:559:26 | src | tests.js:559:24:559:31 | src[key] | +| tests.js:559:24:559:26 | src | tests.js:559:24:559:31 | src[key] | +| tests.js:559:24:559:31 | src[key] | tests.js:559:24:559:31 | src[key] | +| tests.js:559:28:559:30 | key | tests.js:559:24:559:31 | src[key] | +| tests.js:559:28:559:30 | key | tests.js:559:24:559:31 | src[key] | +| tests.js:559:28:559:30 | key | tests.js:559:24:559:31 | src[key] | +| tests.js:559:28:559:30 | key | tests.js:559:24:559:31 | src[key] | #select | examples/PrototypePollutingFunction.js:7:13:7:15 | dst | examples/PrototypePollutingFunction.js:2:14:2:16 | key | examples/PrototypePollutingFunction.js:7:13:7:15 | dst | Properties are copied from $@ to $@ without guarding against prototype pollution. | examples/PrototypePollutingFunction.js:2:21:2:23 | src | src | examples/PrototypePollutingFunction.js:7:13:7:15 | dst | dst | | path-assignment.js:15:13:15:18 | target | path-assignment.js:8:19:8:25 | keys[i] | path-assignment.js:15:13:15:18 | target | The property chain $@ is recursively assigned to $@ without guarding against prototype pollution. | path-assignment.js:8:19:8:25 | keys[i] | here | path-assignment.js:15:13:15:18 | target | target | diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/tests.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/tests.js index d54081d6e7d..7059e613801 100644 --- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/tests.js +++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/tests.js @@ -548,3 +548,15 @@ function mergeUsingCallback3(dst, src) { } }); } + +function copyHasOwnProperty2(dst, src) { + for (let key in src) { + // Guarding the recursive case by dst.hasOwnProperty (or Object.hasOwn) is safe, + // since '__proto__' and 'constructor' are not own properties of the destination object. + if (Object.hasOwn(dst, key)) { + copyHasOwnProperty2(dst[key], src[key]); + } else { + dst[key] = src[key]; // OK + } + } +} From 82c6c22d50a60f50ae470768c2658c3bf790e221 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 24 May 2022 14:13:53 +0200 Subject: [PATCH 086/125] make a model for hasOwnProperty calls and similar --- .../javascript/MembershipCandidates.qll | 16 ++---- .../lib/semmle/javascript/StandardLibrary.qll | 32 +++++++++++ .../CWE-915/PrototypePollutingFunction.ql | 23 +------- .../TaintBarriers/tests.expected | 1 - .../PrototypePollutingFunction.expected | 57 +++++++++++++++++++ .../PrototypePollutingFunction/tests.js | 12 ++++ 6 files changed, 108 insertions(+), 33 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/MembershipCandidates.qll b/javascript/ql/lib/semmle/javascript/MembershipCandidates.qll index 57d477c182b..607c55c9d02 100644 --- a/javascript/ql/lib/semmle/javascript/MembershipCandidates.qll +++ b/javascript/ql/lib/semmle/javascript/MembershipCandidates.qll @@ -229,18 +229,10 @@ module MembershipCandidate { membersNode = inExpr.getRightOperand() ) or - exists(MethodCallExpr hasOwn | - this = hasOwn.getArgument(0).flow() and - test = hasOwn and - hasOwn.calls(membersNode, "hasOwnProperty") - ) - or - exists(DataFlow::CallNode hasOwn | - hasOwn = DataFlow::globalVarRef("Object").getAMemberCall("hasOwn") - | - hasOwn.getArgument(0).asExpr() = membersNode and - this = hasOwn.getArgument(1) and - test = hasOwn.asExpr() + exists(HasOwnPropertyCall hasOwn | + this = hasOwn.getProperty() and + test = hasOwn.asExpr() and + membersNode = hasOwn.getObject().asExpr() ) } diff --git a/javascript/ql/lib/semmle/javascript/StandardLibrary.qll b/javascript/ql/lib/semmle/javascript/StandardLibrary.qll index d99cd0e7e0d..9366c76d9cc 100644 --- a/javascript/ql/lib/semmle/javascript/StandardLibrary.qll +++ b/javascript/ql/lib/semmle/javascript/StandardLibrary.qll @@ -192,3 +192,35 @@ class StringSplitCall extends DataFlow::MethodCallNode { bindingset[i] DataFlow::Node getASubstringRead(int i) { result = this.getAPropertyRead(i.toString()) } } + +/** + * A call to `Object.prototype.hasOwnProperty`, `Object.hasOwn`, or a library that implements + * the same functionality. + */ +class HasOwnPropertyCall extends DataFlow::Node instanceof DataFlow::CallNode { + DataFlow::Node object; + DataFlow::Node property; + + HasOwnPropertyCall() { + // Make sure we handle reflective calls since libraries love to do that. + super.getCalleeNode().getALocalSource().(DataFlow::PropRead).getPropertyName() = + "hasOwnProperty" and + object = super.getReceiver() and + property = super.getArgument(0) + or + this = + [ + DataFlow::globalVarRef("Object").getAMemberCall("hasOwn"), // + DataFlow::moduleImport("has").getACall(), // + LodashUnderscore::member("has").getACall() + ] and + object = super.getArgument(0) and + property = super.getArgument(1) + } + + /** Gets the object whose property is being checked. */ + DataFlow::Node getObject() { result = object } + + /** Gets the property being checked. */ + DataFlow::Node getProperty() { result = property } +} diff --git a/javascript/ql/src/Security/CWE-915/PrototypePollutingFunction.ql b/javascript/ql/src/Security/CWE-915/PrototypePollutingFunction.ql index 61483e4a9cb..9e5b873f663 100644 --- a/javascript/ql/src/Security/CWE-915/PrototypePollutingFunction.ql +++ b/javascript/ql/src/Security/CWE-915/PrototypePollutingFunction.ql @@ -286,7 +286,6 @@ class PropNameTracking extends DataFlow::Configuration { node instanceof DenyListEqualityGuard or node instanceof AllowListEqualityGuard or node instanceof HasOwnPropertyGuard or - node instanceof HasOwnGuard or node instanceof InExprGuard or node instanceof InstanceOfGuard or node instanceof TypeofGuard or @@ -340,32 +339,16 @@ class AllowListEqualityGuard extends DataFlow::LabeledBarrierGuardNode, ValueNod * but the destination object generally doesn't. It is therefore only a sanitizer when * used on the destination object. */ -class HasOwnPropertyGuard extends DataFlow::BarrierGuardNode, CallNode { +class HasOwnPropertyGuard extends DataFlow::BarrierGuardNode instanceof HasOwnPropertyCall { HasOwnPropertyGuard() { - // Make sure we handle reflective calls since libraries love to do that. - getCalleeNode().getALocalSource().(DataFlow::PropRead).getPropertyName() = "hasOwnProperty" and - exists(getReceiver()) and // Try to avoid `src.hasOwnProperty` by requiring that the receiver // does not locally have its properties enumerated. Typically there is no // reason to enumerate the properties of the destination object. - not arePropertiesEnumerated(getReceiver().getALocalSource()) + not arePropertiesEnumerated(super.getObject().getALocalSource()) } override predicate blocks(boolean outcome, Expr e) { - e = getArgument(0).asExpr() and outcome = true - } -} - -/** Sanitizer guard for calls to `Object.hasOwn(obj, prop)`. */ -class HasOwnGuard extends DataFlow::BarrierGuardNode, CallNode { - HasOwnGuard() { - this = DataFlow::globalVarRef("Object").getAMemberCall("hasOwn") and - // same check as in HasOwnPropertyGuard - not arePropertiesEnumerated(getArgument(0).getALocalSource()) - } - - override predicate blocks(boolean outcome, Expr e) { - e = getArgument(1).asExpr() and outcome = true + e = super.getProperty().asExpr() and outcome = true } } diff --git a/javascript/ql/test/library-tests/TaintBarriers/tests.expected b/javascript/ql/test/library-tests/TaintBarriers/tests.expected index d8cca36c7ca..3ee1223b87d 100644 --- a/javascript/ql/test/library-tests/TaintBarriers/tests.expected +++ b/javascript/ql/test/library-tests/TaintBarriers/tests.expected @@ -131,7 +131,6 @@ sanitizingGuard | tst.js:395:16:395:38 | o.hasOw ... (v.p.q) | tst.js:395:33:395:37 | v.p.q | true | | tst.js:397:16:397:36 | o.hasOw ... ty(v.p) | tst.js:397:33:397:35 | v.p | true | | tst.js:399:16:399:41 | o.hasOw ... "p.q"]) | tst.js:399:33:399:40 | v["p.q"] | true | -| tst.js:401:16:401:34 | Object.hasOwn(o, v) | tst.js:401:30:401:30 | o | true | | tst.js:401:16:401:34 | Object.hasOwn(o, v) | tst.js:401:33:401:33 | v | true | taintedSink | tst.js:2:13:2:20 | SOURCE() | tst.js:3:10:3:10 | v | diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/PrototypePollutingFunction.expected b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/PrototypePollutingFunction.expected index 65161e5df90..f16f3e4ef55 100644 --- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/PrototypePollutingFunction.expected +++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/PrototypePollutingFunction.expected @@ -1503,6 +1503,31 @@ nodes | tests.js:559:24:559:31 | src[key] | | tests.js:559:28:559:30 | key | | tests.js:559:28:559:30 | key | +| tests.js:564:35:564:37 | src | +| tests.js:564:35:564:37 | src | +| tests.js:565:14:565:16 | key | +| tests.js:565:14:565:16 | key | +| tests.js:565:14:565:16 | key | +| tests.js:569:43:569:45 | src | +| tests.js:569:43:569:45 | src | +| tests.js:569:43:569:50 | src[key] | +| tests.js:569:43:569:50 | src[key] | +| tests.js:569:43:569:50 | src[key] | +| tests.js:569:43:569:50 | src[key] | +| tests.js:569:43:569:50 | src[key] | +| tests.js:571:17:571:19 | key | +| tests.js:571:17:571:19 | key | +| tests.js:571:17:571:19 | key | +| tests.js:571:24:571:26 | src | +| tests.js:571:24:571:26 | src | +| tests.js:571:24:571:31 | src[key] | +| tests.js:571:24:571:31 | src[key] | +| tests.js:571:24:571:31 | src[key] | +| tests.js:571:24:571:31 | src[key] | +| tests.js:571:24:571:31 | src[key] | +| tests.js:571:24:571:31 | src[key] | +| tests.js:571:28:571:30 | key | +| tests.js:571:28:571:30 | key | edges | examples/PrototypePollutingFunction.js:1:16:1:18 | dst | examples/PrototypePollutingFunction.js:5:19:5:21 | dst | | examples/PrototypePollutingFunction.js:1:16:1:18 | dst | examples/PrototypePollutingFunction.js:5:19:5:21 | dst | @@ -3404,6 +3429,38 @@ edges | tests.js:559:28:559:30 | key | tests.js:559:24:559:31 | src[key] | | tests.js:559:28:559:30 | key | tests.js:559:24:559:31 | src[key] | | tests.js:559:28:559:30 | key | tests.js:559:24:559:31 | src[key] | +| tests.js:564:35:564:37 | src | tests.js:569:43:569:45 | src | +| tests.js:564:35:564:37 | src | tests.js:569:43:569:45 | src | +| tests.js:564:35:564:37 | src | tests.js:571:24:571:26 | src | +| tests.js:564:35:564:37 | src | tests.js:571:24:571:26 | src | +| tests.js:565:14:565:16 | key | tests.js:571:17:571:19 | key | +| tests.js:565:14:565:16 | key | tests.js:571:17:571:19 | key | +| tests.js:565:14:565:16 | key | tests.js:571:17:571:19 | key | +| tests.js:565:14:565:16 | key | tests.js:571:17:571:19 | key | +| tests.js:565:14:565:16 | key | tests.js:571:17:571:19 | key | +| tests.js:565:14:565:16 | key | tests.js:571:17:571:19 | key | +| tests.js:565:14:565:16 | key | tests.js:571:17:571:19 | key | +| tests.js:565:14:565:16 | key | tests.js:571:28:571:30 | key | +| tests.js:565:14:565:16 | key | tests.js:571:28:571:30 | key | +| tests.js:565:14:565:16 | key | tests.js:571:28:571:30 | key | +| tests.js:565:14:565:16 | key | tests.js:571:28:571:30 | key | +| tests.js:569:43:569:45 | src | tests.js:569:43:569:50 | src[key] | +| tests.js:569:43:569:45 | src | tests.js:569:43:569:50 | src[key] | +| tests.js:569:43:569:50 | src[key] | tests.js:564:35:564:37 | src | +| tests.js:569:43:569:50 | src[key] | tests.js:564:35:564:37 | src | +| tests.js:569:43:569:50 | src[key] | tests.js:564:35:564:37 | src | +| tests.js:569:43:569:50 | src[key] | tests.js:564:35:564:37 | src | +| tests.js:569:43:569:50 | src[key] | tests.js:564:35:564:37 | src | +| tests.js:569:43:569:50 | src[key] | tests.js:564:35:564:37 | src | +| tests.js:571:24:571:26 | src | tests.js:571:24:571:31 | src[key] | +| tests.js:571:24:571:26 | src | tests.js:571:24:571:31 | src[key] | +| tests.js:571:24:571:26 | src | tests.js:571:24:571:31 | src[key] | +| tests.js:571:24:571:26 | src | tests.js:571:24:571:31 | src[key] | +| tests.js:571:24:571:31 | src[key] | tests.js:571:24:571:31 | src[key] | +| tests.js:571:28:571:30 | key | tests.js:571:24:571:31 | src[key] | +| tests.js:571:28:571:30 | key | tests.js:571:24:571:31 | src[key] | +| tests.js:571:28:571:30 | key | tests.js:571:24:571:31 | src[key] | +| tests.js:571:28:571:30 | key | tests.js:571:24:571:31 | src[key] | #select | examples/PrototypePollutingFunction.js:7:13:7:15 | dst | examples/PrototypePollutingFunction.js:2:14:2:16 | key | examples/PrototypePollutingFunction.js:7:13:7:15 | dst | Properties are copied from $@ to $@ without guarding against prototype pollution. | examples/PrototypePollutingFunction.js:2:21:2:23 | src | src | examples/PrototypePollutingFunction.js:7:13:7:15 | dst | dst | | path-assignment.js:15:13:15:18 | target | path-assignment.js:8:19:8:25 | keys[i] | path-assignment.js:15:13:15:18 | target | The property chain $@ is recursively assigned to $@ without guarding against prototype pollution. | path-assignment.js:8:19:8:25 | keys[i] | here | path-assignment.js:15:13:15:18 | target | target | diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/tests.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/tests.js index 7059e613801..a1fc92a5776 100644 --- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/tests.js +++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/tests.js @@ -560,3 +560,15 @@ function copyHasOwnProperty2(dst, src) { } } } + +function copyHasOwnProperty3(dst, src) { + for (let key in src) { + // Guarding the recursive case by dst.hasOwnProperty (or Object.hasOwn) is safe, + // since '__proto__' and 'constructor' are not own properties of the destination object. + if (_.has(dst, key)) { + copyHasOwnProperty3(dst[key], src[key]); + } else { + dst[key] = src[key]; // OK + } + } +} From 2da001ebd759a54d7b52d88d03df265281d27bac Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 24 May 2022 22:55:59 +0200 Subject: [PATCH 087/125] bump TypeScript version to stable release --- javascript/extractor/lib/typescript/package.json | 2 +- javascript/extractor/lib/typescript/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/javascript/extractor/lib/typescript/package.json b/javascript/extractor/lib/typescript/package.json index 858719325db..6898986e749 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": "4.7.1-rc" + "typescript": "4.7.2" }, "scripts": { "build": "tsc --project tsconfig.json", diff --git a/javascript/extractor/lib/typescript/yarn.lock b/javascript/extractor/lib/typescript/yarn.lock index 4fbd1283dda..3a10c8452f0 100644 --- a/javascript/extractor/lib/typescript/yarn.lock +++ b/javascript/extractor/lib/typescript/yarn.lock @@ -6,7 +6,7 @@ version "12.7.11" resolved node-12.7.11.tgz#be879b52031cfb5d295b047f5462d8ef1a716446 -typescript@4.7.1-rc: - version "4.7.1-rc" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.1-rc.tgz#23a0517d36c56de887b4457f29e2d265647bbd7c" - integrity sha512-EQd2NVelDe6ZVc2sO1CSpuSs+RHzY8c2n/kTNQAHw4um/eAXY+ZY4IKoUpNK0wO6C5hN+XcUXR7yqT8VbwwNIQ== +typescript@4.7.2: + version "4.7.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.2.tgz#1f9aa2ceb9af87cca227813b4310fff0b51593c4" + integrity sha512-Mamb1iX2FDUpcTRzltPxgWMKy3fhg0TN378ylbktPGPK/99KbDtMQ4W1hwgsbPAsG3a0xKa1vmw4VKZQbkvz5A== From 877a9d8bcc8cbc903b34933e7be0a590496dcc3e Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 25 May 2022 09:41:17 +0200 Subject: [PATCH 088/125] JS: Fix FP in js/type-confusion-through-parameter-tampering --- ...onfusionThroughParameterTamperingQuery.qll | 28 +++++++++++++ ...onfusionThroughParameterTampering.expected | 20 +++++++++ .../test/query-tests/Security/CWE-843/tst.js | 41 +++++++++++++++---- 3 files changed, 80 insertions(+), 9 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/TypeConfusionThroughParameterTamperingQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/TypeConfusionThroughParameterTamperingQuery.qll index da6b698d97f..1b6cc31f653 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/TypeConfusionThroughParameterTamperingQuery.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/TypeConfusionThroughParameterTamperingQuery.qll @@ -27,4 +27,32 @@ class Configuration extends DataFlow::Configuration { } override predicate isBarrier(DataFlow::Node node) { node instanceof Barrier } + + override predicate isBarrierGuard(DataFlow::BarrierGuardNode guard) { + guard instanceof TypeOfTestBarrier or + guard instanceof IsArrayBarrier + } +} + +private class TypeOfTestBarrier extends DataFlow::BarrierGuardNode, DataFlow::ValueNode { + override EqualityTest astNode; + private Expr operand; + + TypeOfTestBarrier() { astNode.getAnOperand().(TypeofExpr).getOperand() = operand } + + override predicate blocks(boolean outcome, Expr e) { + e = operand and + if astNode.getAnOperand().getStringValue() = ["string", "object"] + then outcome = [true, false] // separation between string/array removes type confusion in both branches + else outcome = astNode.getPolarity() // block flow to branch where value is neither string nor array + } +} + +private class IsArrayBarrier extends DataFlow::BarrierGuardNode, DataFlow::CallNode { + IsArrayBarrier() { this = DataFlow::globalVarRef("Array").getAMemberCall("isArray").getACall() } + + override predicate blocks(boolean outcome, Expr e) { + e = getArgument(0).asExpr() and + outcome = [true, false] // separation between string/array removes type confusion in both branches + } } diff --git a/javascript/ql/test/query-tests/Security/CWE-843/TypeConfusionThroughParameterTampering.expected b/javascript/ql/test/query-tests/Security/CWE-843/TypeConfusionThroughParameterTampering.expected index 5c560d8c4ca..997d8968fcc 100644 --- a/javascript/ql/test/query-tests/Security/CWE-843/TypeConfusionThroughParameterTampering.expected +++ b/javascript/ql/test/query-tests/Security/CWE-843/TypeConfusionThroughParameterTampering.expected @@ -32,6 +32,18 @@ nodes | tst.js:81:9:81:9 | p | | tst.js:82:9:82:9 | p | | tst.js:82:9:82:9 | p | +| tst.js:90:5:90:12 | data.foo | +| tst.js:90:5:90:12 | data.foo | +| tst.js:90:5:90:12 | data.foo | +| tst.js:92:9:92:16 | data.foo | +| tst.js:92:9:92:16 | data.foo | +| tst.js:92:9:92:16 | data.foo | +| tst.js:95:9:95:16 | data.foo | +| tst.js:95:9:95:16 | data.foo | +| tst.js:95:9:95:16 | data.foo | +| tst.js:98:9:98:16 | data.foo | +| tst.js:98:9:98:16 | data.foo | +| tst.js:98:9:98:16 | data.foo | edges | tst.js:5:9:5:27 | foo | tst.js:6:5:6:7 | foo | | tst.js:5:9:5:27 | foo | tst.js:6:5:6:7 | foo | @@ -63,6 +75,10 @@ edges | tst.js:80:23:80:23 | p | tst.js:81:9:81:9 | p | | tst.js:80:23:80:23 | p | tst.js:82:9:82:9 | p | | tst.js:80:23:80:23 | p | tst.js:82:9:82:9 | p | +| tst.js:90:5:90:12 | data.foo | tst.js:90:5:90:12 | data.foo | +| tst.js:92:9:92:16 | data.foo | tst.js:92:9:92:16 | data.foo | +| tst.js:95:9:95:16 | data.foo | tst.js:95:9:95:16 | data.foo | +| tst.js:98:9:98:16 | data.foo | tst.js:98:9:98:16 | data.foo | #select | tst.js:6:5:6:7 | foo | tst.js:5:15:5:27 | req.query.foo | tst.js:6:5:6:7 | foo | Potential type confusion as $@ may be either an array or a string. | tst.js:5:15:5:27 | req.query.foo | this HTTP request parameter | | tst.js:8:5:8:7 | foo | tst.js:5:15:5:27 | req.query.foo | tst.js:8:5:8:7 | foo | Potential type confusion as $@ may be either an array or a string. | tst.js:5:15:5:27 | req.query.foo | this HTTP request parameter | @@ -75,3 +91,7 @@ edges | tst.js:46:5:46:7 | foo | tst.js:45:15:45:35 | ctx.req ... ery.foo | tst.js:46:5:46:7 | foo | Potential type confusion as $@ may be either an array or a string. | tst.js:45:15:45:35 | ctx.req ... ery.foo | this HTTP request parameter | | tst.js:81:9:81:9 | p | tst.js:77:25:77:38 | req.query.path | tst.js:81:9:81:9 | p | Potential type confusion as $@ may be either an array or a string. | tst.js:77:25:77:38 | req.query.path | this HTTP request parameter | | tst.js:82:9:82:9 | p | tst.js:77:25:77:38 | req.query.path | tst.js:82:9:82:9 | p | Potential type confusion as $@ may be either an array or a string. | tst.js:77:25:77:38 | req.query.path | this HTTP request parameter | +| tst.js:90:5:90:12 | data.foo | tst.js:90:5:90:12 | data.foo | tst.js:90:5:90:12 | data.foo | Potential type confusion as $@ may be either an array or a string. | tst.js:90:5:90:12 | data.foo | this HTTP request parameter | +| tst.js:92:9:92:16 | data.foo | tst.js:92:9:92:16 | data.foo | tst.js:92:9:92:16 | data.foo | Potential type confusion as $@ may be either an array or a string. | tst.js:92:9:92:16 | data.foo | this HTTP request parameter | +| tst.js:95:9:95:16 | data.foo | tst.js:95:9:95:16 | data.foo | tst.js:95:9:95:16 | data.foo | Potential type confusion as $@ may be either an array or a string. | tst.js:95:9:95:16 | data.foo | this HTTP request parameter | +| tst.js:98:9:98:16 | data.foo | tst.js:98:9:98:16 | data.foo | tst.js:98:9:98:16 | data.foo | Potential type confusion as $@ may be either an array or a string. | tst.js:98:9:98:16 | data.foo | this HTTP request parameter | diff --git a/javascript/ql/test/query-tests/Security/CWE-843/tst.js b/javascript/ql/test/query-tests/Security/CWE-843/tst.js index 82650bcf054..d49f7ce53d2 100644 --- a/javascript/ql/test/query-tests/Security/CWE-843/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-843/tst.js @@ -1,7 +1,7 @@ var express = require('express'); var Koa = require('koa'); -express().get('/some/path', function(req, res) { +express().get('/some/path', function (req, res) { var foo = req.query.foo; foo.indexOf(); // NOT OK @@ -41,38 +41,38 @@ express().get('/some/path', function(req, res) { foo.length; // NOT OK }); -new Koa().use(function handler(ctx){ +new Koa().use(function handler(ctx) { var foo = ctx.request.query.foo; foo.indexOf(); // NOT OK }); -express().get('/some/path/:foo', function(req, res) { +express().get('/some/path/:foo', function (req, res) { var foo = req.params.foo; foo.indexOf(); // OK }); -express().get('/some/path/:foo', function(req, res) { - if (req.query.path.length) {} // OK +express().get('/some/path/:foo', function (req, res) { + if (req.query.path.length) { } // OK req.query.path.length == 0; // OK !req.query.path.length; // OK req.query.path.length > 0; // OK }); -express().get('/some/path/:foo', function(req, res) { +express().get('/some/path/:foo', function (req, res) { let p = req.query.path; if (typeof p !== 'string') { - return; + return; } while (p.length) { // OK - p = p.substr(1); + p = p.substr(1); } p.length < 1; // OK }); -express().get('/some/path/:foo', function(req, res) { +express().get('/some/path/:foo', function (req, res) { let someObject = {}; safeGet(someObject, req.query.path).bar = 'baz'; // prototype pollution here - but flagged in `safeGet` }); @@ -84,3 +84,26 @@ function safeGet(obj, p) { } return obj[p]; } + +express().get('/foo', function (req, res) { + let data = req.query; + data.foo.indexOf(); // NOT OK + if (typeof data.foo !== 'undefined') { + data.foo.indexOf(); // NOT OK + } + if (typeof data.foo !== 'string') { + data.foo.indexOf(); // OK + } + if (typeof data.foo !== 'undefined') { + data.foo.indexOf(); // NOT OK + } +}); + +express().get('/foo', function (req, res) { + let data = req.query; + if (Array.isArray(data)) { + data.indexOf(); // OK + } else { + data.indexOf(); // OK + } +}); From efa895e91279d87b0c5f6108ea7ca8e40671c359 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 25 May 2022 10:33:39 +0000 Subject: [PATCH 089/125] update expected output --- .../TypeScript/Types/printAst.expected | 1392 ++++++++++------- .../TypeScript/Types/tests.expected | 63 +- 2 files changed, 886 insertions(+), 569 deletions(-) diff --git a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected index 8ebfa8473ec..bbe3148efb3 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected +++ b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected @@ -95,6 +95,10 @@ 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 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | @@ -1140,186 +1144,314 @@ nodes | tst.ts:293:7:293:23 | [MethodCallExpr] payload.toFixed() | semmle.label | [MethodCallExpr] payload.toFixed() | | tst.ts:293:7:293:24 | [ExprStmt] payload.toFixed(); | semmle.label | [ExprStmt] payload.toFixed(); | | tst.ts:293:15:293:21 | [Label] toFixed | semmle.label | [Label] toFixed | -| tst.ts:298:1:376:1 | [FunctionDeclStmt] functio ... ring } | semmle.label | [FunctionDeclStmt] functio ... ring } | -| tst.ts:298:1:376:1 | [FunctionDeclStmt] functio ... ring } | semmle.order | 61 | -| tst.ts:298:10:298:15 | [VarDecl] foo_47 | semmle.label | [VarDecl] foo_47 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | semmle.label | [BlockStmt] { con ... ring } | -| tst.ts:299:3:299:23 | [DeclStmt] const key = ... | semmle.label | [DeclStmt] const key = ... | -| tst.ts:299:9:299:11 | [VarDecl] key | semmle.label | [VarDecl] key | -| tst.ts:299:9:299:22 | [VariableDeclarator] key = Symbol() | semmle.label | [VariableDeclarator] key = Symbol() | -| tst.ts:299:15:299:20 | [VarRef] Symbol | semmle.label | [VarRef] Symbol | -| tst.ts:299:15:299:22 | [CallExpr] Symbol() | semmle.label | [CallExpr] Symbol() | -| tst.ts:301:3:301:60 | [DeclStmt] const numberOrString = ... | semmle.label | [DeclStmt] const numberOrString = ... | -| tst.ts:301:9:301:22 | [VarDecl] numberOrString | semmle.label | [VarDecl] numberOrString | -| tst.ts:301:9:301:59 | [VariableDeclarator] numberO ... "hello" | semmle.label | [VariableDeclarator] numberO ... "hello" | -| tst.ts:301:26:301:29 | [VarRef] Math | semmle.label | [VarRef] Math | -| tst.ts:301:26:301:36 | [DotExpr] Math.random | semmle.label | [DotExpr] Math.random | -| tst.ts:301:26:301:38 | [MethodCallExpr] Math.random() | semmle.label | [MethodCallExpr] Math.random() | -| tst.ts:301:26:301:44 | [BinaryExpr] Math.random() < 0.5 | semmle.label | [BinaryExpr] Math.random() < 0.5 | -| tst.ts:301:26:301:59 | [ConditionalExpr] Math.ra ... "hello" | semmle.label | [ConditionalExpr] Math.ra ... "hello" | -| tst.ts:301:31:301:36 | [Label] random | semmle.label | [Label] random | -| tst.ts:301:42:301:44 | [Literal] 0.5 | semmle.label | [Literal] 0.5 | -| tst.ts:301:48:301:49 | [Literal] 42 | semmle.label | [Literal] 42 | -| tst.ts:301:53:301:59 | [Literal] "hello" | semmle.label | [Literal] "hello" | -| tst.ts:303:3:305:4 | [DeclStmt] let obj = ... | semmle.label | [DeclStmt] let obj = ... | -| tst.ts:303:7:303:9 | [VarDecl] obj | semmle.label | [VarDecl] obj | -| tst.ts:303:7:305:3 | [VariableDeclarator] obj = { ... ng, } | semmle.label | [VariableDeclarator] obj = { ... ng, } | -| tst.ts:303:13:305:3 | [ObjectExpr] { [ ... ng, } | semmle.label | [ObjectExpr] { [ ... ng, } | -| tst.ts:304:5:304:25 | [Property] [key]: ... rString | semmle.label | [Property] [key]: ... rString | -| tst.ts:304:6:304:8 | [VarRef] key | semmle.label | [VarRef] key | -| tst.ts:304:12:304:25 | [VarRef] numberOrString | semmle.label | [VarRef] numberOrString | -| tst.ts:307:3:310:3 | [IfStmt] if (typ ... (); } | semmle.label | [IfStmt] if (typ ... (); } | -| tst.ts:307:7:307:21 | [UnaryExpr] typeof obj[key] | semmle.label | [UnaryExpr] typeof obj[key] | -| tst.ts:307:7:307:34 | [BinaryExpr] typeof ... string" | semmle.label | [BinaryExpr] typeof ... string" | -| tst.ts:307:14:307:16 | [VarRef] obj | semmle.label | [VarRef] obj | -| tst.ts:307:14:307:21 | [IndexExpr] obj[key] | semmle.label | [IndexExpr] obj[key] | -| tst.ts:307:18:307:20 | [VarRef] key | semmle.label | [VarRef] key | -| tst.ts:307:27:307:34 | [Literal] "string" | semmle.label | [Literal] "string" | -| tst.ts:307:37:310:3 | [BlockStmt] { l ... (); } | semmle.label | [BlockStmt] { l ... (); } | -| tst.ts:308:5:308:23 | [DeclStmt] let str = ... | semmle.label | [DeclStmt] let str = ... | -| tst.ts:308:9:308:11 | [VarDecl] str | semmle.label | [VarDecl] str | -| tst.ts:308:9:308:22 | [VariableDeclarator] str = obj[key] | semmle.label | [VariableDeclarator] str = obj[key] | -| tst.ts:308:15:308:17 | [VarRef] obj | semmle.label | [VarRef] obj | -| tst.ts:308:15:308:22 | [IndexExpr] obj[key] | semmle.label | [IndexExpr] obj[key] | -| tst.ts:308:19:308:21 | [VarRef] key | semmle.label | [VarRef] key | -| tst.ts:309:5:309:7 | [VarRef] str | semmle.label | [VarRef] str | -| tst.ts:309:5:309:19 | [DotExpr] str.toUpperCase | semmle.label | [DotExpr] str.toUpperCase | -| tst.ts:309:5:309:21 | [MethodCallExpr] str.toUpperCase() | semmle.label | [MethodCallExpr] str.toUpperCase() | -| tst.ts:309:5:309:22 | [ExprStmt] str.toUpperCase(); | semmle.label | [ExprStmt] str.toUpperCase(); | -| tst.ts:309:9:309:19 | [Label] toUpperCase | semmle.label | [Label] toUpperCase | -| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | semmle.label | [FunctionDeclStmt] functio ... void {} | -| tst.ts:314:12:314:12 | [VarDecl] f | semmle.label | [VarDecl] f | -| tst.ts:314:14:314:14 | [Identifier] T | semmle.label | [Identifier] T | -| tst.ts:314:14:314:14 | [TypeParameter] T | semmle.label | [TypeParameter] T | -| tst.ts:314:17:314:19 | [SimpleParameter] arg | semmle.label | [SimpleParameter] arg | -| tst.ts:314:22:316:29 | [InterfaceTypeExpr] { p ... void } | semmle.label | [InterfaceTypeExpr] { p ... void } | -| tst.ts:315:5:315:11 | [Label] produce | semmle.label | [Label] produce | -| tst.ts:315:5:315:30 | [FieldDeclaration] produce ... ) => T, | semmle.label | [FieldDeclaration] produce ... ) => T, | -| tst.ts:315:14:315:29 | [FunctionExpr] (n: string) => T | semmle.label | [FunctionExpr] (n: string) => T | -| tst.ts:315:14:315:29 | [FunctionTypeExpr] (n: string) => T | semmle.label | [FunctionTypeExpr] (n: string) => T | -| tst.ts:315:15:315:15 | [SimpleParameter] n | semmle.label | [SimpleParameter] n | -| tst.ts:315:18:315:23 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | -| tst.ts:315:29:315:29 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | -| tst.ts:316:5:316:11 | [Label] consume | semmle.label | [Label] consume | -| tst.ts:316:5:316:27 | [FieldDeclaration] consume ... => void | semmle.label | [FieldDeclaration] consume ... => void | -| tst.ts:316:14:316:27 | [FunctionExpr] (x: T) => void | semmle.label | [FunctionExpr] (x: T) => void | -| tst.ts:316:14:316:27 | [FunctionTypeExpr] (x: T) => void | semmle.label | [FunctionTypeExpr] (x: T) => void | -| tst.ts:316:15:316:15 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | -| tst.ts:316:18:316:18 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | -| tst.ts:316:24:316:27 | [KeywordTypeExpr] void | semmle.label | [KeywordTypeExpr] void | -| tst.ts:317:6:317:9 | [KeywordTypeExpr] void | semmle.label | [KeywordTypeExpr] void | -| tst.ts:317:11:317:12 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | -| tst.ts:317:13:317:13 | [EmptyStmt] ; | semmle.label | [EmptyStmt] ; | -| tst.ts:319:3:319:3 | [VarRef] f | semmle.label | [VarRef] f | -| tst.ts:319:3:322:4 | [CallExpr] f({ ... () }) | semmle.label | [CallExpr] f({ ... () }) | -| tst.ts:319:3:322:5 | [ExprStmt] f({ ... ) }); | semmle.label | [ExprStmt] f({ ... ) }); | -| tst.ts:319:5:322:3 | [ObjectExpr] {produce: ...} | semmle.label | [ObjectExpr] {produce: ...} | -| tst.ts:320:5:320:11 | [Label] produce | semmle.label | [Label] produce | -| tst.ts:320:5:320:19 | [Property] produce: n => n | semmle.label | [Property] produce: n => n | -| tst.ts:320:14:320:14 | [SimpleParameter] n | semmle.label | [SimpleParameter] n | -| tst.ts:320:14:320:19 | [ArrowFunctionExpr] n => n | semmle.label | [ArrowFunctionExpr] n => n | -| tst.ts:320:19:320:19 | [VarRef] n | semmle.label | [VarRef] n | -| tst.ts:321:5:321:11 | [Label] consume | semmle.label | [Label] consume | -| tst.ts:321:5:321:33 | [Property] consume ... rCase() | semmle.label | [Property] consume ... rCase() | -| tst.ts:321:14:321:14 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | -| tst.ts:321:14:321:33 | [ArrowFunctionExpr] x => x.toLowerCase() | semmle.label | [ArrowFunctionExpr] x => x.toLowerCase() | -| tst.ts:321:19:321:19 | [VarRef] x | semmle.label | [VarRef] x | -| tst.ts:321:19:321:31 | [DotExpr] x.toLowerCase | semmle.label | [DotExpr] x.toLowerCase | -| tst.ts:321:19:321:33 | [MethodCallExpr] x.toLowerCase() | semmle.label | [MethodCallExpr] x.toLowerCase() | -| tst.ts:321:21:321:31 | [Label] toLowerCase | semmle.label | [Label] toLowerCase | -| tst.ts:326:3:326:38 | [DeclStmt] const ErrorMap = ... | semmle.label | [DeclStmt] const ErrorMap = ... | -| tst.ts:326:9:326:16 | [VarDecl] ErrorMap | semmle.label | [VarDecl] ErrorMap | -| tst.ts:326:9:326:37 | [VariableDeclarator] ErrorMa ... Error> | semmle.label | [VariableDeclarator] ErrorMa ... Error> | -| tst.ts:326:20:326:22 | [VarRef] Map | semmle.label | [VarRef] Map | -| tst.ts:326:20:326:37 | [???] Map | semmle.label | [???] Map | -| tst.ts:326:24:326:29 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | -| tst.ts:326:32:326:36 | [LocalTypeAccess] Error | semmle.label | [LocalTypeAccess] Error | -| tst.ts:328:3:328:34 | [DeclStmt] const errorMap = ... | semmle.label | [DeclStmt] const errorMap = ... | -| tst.ts:328:9:328:16 | [VarDecl] errorMap | semmle.label | [VarDecl] errorMap | -| tst.ts:328:9:328:33 | [VariableDeclarator] errorMa ... orMap() | semmle.label | [VariableDeclarator] errorMa ... orMap() | -| tst.ts:328:20:328:33 | [NewExpr] new ErrorMap() | semmle.label | [NewExpr] new ErrorMap() | -| tst.ts:328:24:328:31 | [VarRef] ErrorMap | semmle.label | [VarRef] ErrorMap | -| tst.ts:332:3:335:16 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | -| tst.ts:332:8:332:18 | [Identifier] FirstString | semmle.label | [Identifier] FirstString | -| tst.ts:332:20:332:20 | [Identifier] T | semmle.label | [Identifier] T | -| tst.ts:332:20:332:20 | [TypeParameter] T | semmle.label | [TypeParameter] T | -| tst.ts:333:5:333:5 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | -| tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | semmle.label | [ConditionalTypeExpr] T exten ... : never | -| tst.ts:333:15:333:52 | [TupleTypeExpr] [infer ... nown[]] | semmle.label | [TupleTypeExpr] [infer ... nown[]] | -| tst.ts:333:16:333:37 | [InferTypeExpr] infer S ... string | semmle.label | [InferTypeExpr] infer S ... string | -| tst.ts:333:22:333:22 | [Identifier] S | semmle.label | [Identifier] S | -| tst.ts:333:22:333:37 | [TypeParameter] S extends string | semmle.label | [TypeParameter] S extends string | -| tst.ts:333:32:333:37 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | -| tst.ts:333:40:333:51 | [RestTypeExpr] ...unknown[] | semmle.label | [RestTypeExpr] ...unknown[] | -| tst.ts:333:43:333:49 | [KeywordTypeExpr] unknown | semmle.label | [KeywordTypeExpr] unknown | -| tst.ts:333:43:333:51 | [ArrayTypeExpr] unknown[] | semmle.label | [ArrayTypeExpr] unknown[] | -| tst.ts:334:11:334:11 | [LocalTypeAccess] S | semmle.label | [LocalTypeAccess] S | -| tst.ts:335:11:335:15 | [KeywordTypeExpr] never | semmle.label | [KeywordTypeExpr] never | -| tst.ts:337:3:337:53 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | -| tst.ts:337:8:337:8 | [Identifier] F | semmle.label | [Identifier] F | -| tst.ts:337:12:337:22 | [LocalTypeAccess] FirstString | semmle.label | [LocalTypeAccess] FirstString | -| tst.ts:337:12:337:52 | [GenericTypeExpr] FirstSt ... olean]> | semmle.label | [GenericTypeExpr] FirstSt ... olean]> | -| tst.ts:337:24:337:51 | [TupleTypeExpr] ['a' \| ... oolean] | semmle.label | [TupleTypeExpr] ['a' \| ... oolean] | -| tst.ts:337:25:337:27 | [LiteralTypeExpr] 'a' | semmle.label | [LiteralTypeExpr] 'a' | -| tst.ts:337:25:337:33 | [UnionTypeExpr] 'a' \| 'b' | semmle.label | [UnionTypeExpr] 'a' \| 'b' | -| tst.ts:337:31:337:33 | [LiteralTypeExpr] 'b' | semmle.label | [LiteralTypeExpr] 'b' | -| tst.ts:337:36:337:41 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | -| tst.ts:337:44:337:50 | [KeywordTypeExpr] boolean | semmle.label | [KeywordTypeExpr] boolean | -| tst.ts:339:3:339:19 | [DeclStmt] const a = ... | semmle.label | [DeclStmt] const a = ... | -| tst.ts:339:9:339:9 | [VarDecl] a | semmle.label | [VarDecl] a | -| tst.ts:339:9:339:18 | [VariableDeclarator] a: F = 'a' | semmle.label | [VariableDeclarator] a: F = 'a' | -| tst.ts:339:12:339:12 | [LocalTypeAccess] F | semmle.label | [LocalTypeAccess] F | -| tst.ts:339:16:339:18 | [Literal] 'a' | semmle.label | [Literal] 'a' | -| tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | -| tst.ts:343:13:343:17 | [Identifier] State | semmle.label | [Identifier] State | -| tst.ts:343:19:343:26 | [TypeParameter] in out T | semmle.label | [TypeParameter] in out T | -| tst.ts:343:26:343:26 | [Identifier] T | semmle.label | [Identifier] T | -| tst.ts:344:5:344:7 | [Label] get | semmle.label | [Label] get | -| tst.ts:344:5:344:17 | [FieldDeclaration] get: () => T; | semmle.label | [FieldDeclaration] get: () => T; | -| tst.ts:344:10:344:16 | [FunctionExpr] () => T | semmle.label | [FunctionExpr] () => T | -| tst.ts:344:10:344:16 | [FunctionTypeExpr] () => T | semmle.label | [FunctionTypeExpr] () => T | +| tst.ts:298:1:298:21 | [DeclStmt] const key = ... | semmle.label | [DeclStmt] const key = ... | +| tst.ts:298:1:298:21 | [DeclStmt] const key = ... | semmle.order | 61 | +| tst.ts:298:7:298:9 | [VarDecl] key | semmle.label | [VarDecl] key | +| tst.ts:298:7:298:20 | [VariableDeclarator] key = Symbol() | semmle.label | [VariableDeclarator] key = Symbol() | +| tst.ts:298:13:298:18 | [VarRef] Symbol | semmle.label | [VarRef] Symbol | +| tst.ts:298:13:298:20 | [CallExpr] Symbol() | semmle.label | [CallExpr] Symbol() | +| tst.ts:300:1:300:58 | [DeclStmt] const numberOrString = ... | semmle.label | [DeclStmt] const numberOrString = ... | +| tst.ts:300:1:300:58 | [DeclStmt] const numberOrString = ... | semmle.order | 62 | +| tst.ts:300:7:300:20 | [VarDecl] numberOrString | semmle.label | [VarDecl] numberOrString | +| tst.ts:300:7:300:57 | [VariableDeclarator] numberO ... "hello" | semmle.label | [VariableDeclarator] numberO ... "hello" | +| tst.ts:300:24:300:27 | [VarRef] Math | semmle.label | [VarRef] Math | +| tst.ts:300:24:300:34 | [DotExpr] Math.random | semmle.label | [DotExpr] Math.random | +| tst.ts:300:24:300:36 | [MethodCallExpr] Math.random() | semmle.label | [MethodCallExpr] Math.random() | +| tst.ts:300:24:300:42 | [BinaryExpr] Math.random() < 0.5 | semmle.label | [BinaryExpr] Math.random() < 0.5 | +| tst.ts:300:24:300:57 | [ConditionalExpr] Math.ra ... "hello" | semmle.label | [ConditionalExpr] Math.ra ... "hello" | +| tst.ts:300:29:300:34 | [Label] random | semmle.label | [Label] random | +| tst.ts:300:40:300:42 | [Literal] 0.5 | semmle.label | [Literal] 0.5 | +| tst.ts:300:46:300:47 | [Literal] 42 | semmle.label | [Literal] 42 | +| tst.ts:300:51:300:57 | [Literal] "hello" | semmle.label | [Literal] "hello" | +| tst.ts:302:1:304:2 | [DeclStmt] let obj = ... | semmle.label | [DeclStmt] let obj = ... | +| tst.ts:302:1:304:2 | [DeclStmt] let obj = ... | semmle.order | 63 | +| tst.ts:302:5:302:7 | [VarDecl] obj | semmle.label | [VarDecl] obj | +| tst.ts:302:5:304:1 | [VariableDeclarator] obj = { ... ring, } | semmle.label | [VariableDeclarator] obj = { ... ring, } | +| tst.ts:302:11:304:1 | [ObjectExpr] { [ke ... ring, } | semmle.label | [ObjectExpr] { [ke ... ring, } | +| tst.ts:303:3:303:23 | [Property] [key]: ... rString | semmle.label | [Property] [key]: ... rString | +| tst.ts:303:4:303:6 | [VarRef] key | semmle.label | [VarRef] key | +| tst.ts:303:10:303:23 | [VarRef] numberOrString | semmle.label | [VarRef] numberOrString | +| tst.ts:306:1:309:1 | [IfStmt] if (typ ... se(); } | semmle.label | [IfStmt] if (typ ... se(); } | +| tst.ts:306:1:309:1 | [IfStmt] if (typ ... se(); } | semmle.order | 64 | +| tst.ts:306:5:306:19 | [UnaryExpr] typeof obj[key] | semmle.label | [UnaryExpr] typeof obj[key] | +| tst.ts:306:5:306:32 | [BinaryExpr] typeof ... string" | semmle.label | [BinaryExpr] typeof ... string" | +| tst.ts:306:12:306:14 | [VarRef] obj | semmle.label | [VarRef] obj | +| tst.ts:306:12:306:19 | [IndexExpr] obj[key] | semmle.label | [IndexExpr] obj[key] | +| tst.ts:306:16:306:18 | [VarRef] key | semmle.label | [VarRef] key | +| tst.ts:306:25:306:32 | [Literal] "string" | semmle.label | [Literal] "string" | +| tst.ts:306:35:309:1 | [BlockStmt] { let ... se(); } | semmle.label | [BlockStmt] { let ... se(); } | +| tst.ts:307:3:307:21 | [DeclStmt] let str = ... | semmle.label | [DeclStmt] let str = ... | +| tst.ts:307:7:307:9 | [VarDecl] str | semmle.label | [VarDecl] str | +| tst.ts:307:7:307:20 | [VariableDeclarator] str = obj[key] | semmle.label | [VariableDeclarator] str = obj[key] | +| tst.ts:307:13:307:15 | [VarRef] obj | semmle.label | [VarRef] obj | +| tst.ts:307:13:307:20 | [IndexExpr] obj[key] | semmle.label | [IndexExpr] obj[key] | +| tst.ts:307:17:307:19 | [VarRef] key | semmle.label | [VarRef] key | +| tst.ts:308:3:308:5 | [VarRef] str | semmle.label | [VarRef] str | +| tst.ts:308:3:308:17 | [DotExpr] str.toUpperCase | semmle.label | [DotExpr] str.toUpperCase | +| tst.ts:308:3:308:19 | [MethodCallExpr] str.toUpperCase() | semmle.label | [MethodCallExpr] str.toUpperCase() | +| tst.ts:308:3:308:20 | [ExprStmt] str.toUpperCase(); | semmle.label | [ExprStmt] str.toUpperCase(); | +| tst.ts:308:7:308:17 | [Label] toUpperCase | semmle.label | [Label] toUpperCase | +| tst.ts:313:1:316:10 | [FunctionDeclStmt] functio ... void {} | semmle.label | [FunctionDeclStmt] functio ... void {} | +| tst.ts:313:1:316:10 | [FunctionDeclStmt] functio ... void {} | semmle.order | 65 | +| tst.ts:313:10:313:10 | [VarDecl] f | semmle.label | [VarDecl] f | +| tst.ts:313:12:313:12 | [Identifier] T | semmle.label | [Identifier] T | +| tst.ts:313:12:313:12 | [TypeParameter] T | semmle.label | [TypeParameter] T | +| tst.ts:313:15:313:17 | [SimpleParameter] arg | semmle.label | [SimpleParameter] arg | +| tst.ts:313:20:315:27 | [InterfaceTypeExpr] { pro ... void } | semmle.label | [InterfaceTypeExpr] { pro ... void } | +| tst.ts:314:3:314:9 | [Label] produce | semmle.label | [Label] produce | +| tst.ts:314:3:314:28 | [FieldDeclaration] produce ... ) => T, | semmle.label | [FieldDeclaration] produce ... ) => T, | +| tst.ts:314:12:314:27 | [FunctionExpr] (n: string) => T | semmle.label | [FunctionExpr] (n: string) => T | +| tst.ts:314:12:314:27 | [FunctionTypeExpr] (n: string) => T | semmle.label | [FunctionTypeExpr] (n: string) => T | +| tst.ts:314:13:314:13 | [SimpleParameter] n | semmle.label | [SimpleParameter] n | +| tst.ts:314:16:314:21 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | +| tst.ts:314:27:314:27 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | +| tst.ts:315:3:315:9 | [Label] consume | semmle.label | [Label] consume | +| tst.ts:315:3:315:25 | [FieldDeclaration] consume ... => void | semmle.label | [FieldDeclaration] consume ... => void | +| tst.ts:315:12:315:25 | [FunctionExpr] (x: T) => void | semmle.label | [FunctionExpr] (x: T) => void | +| tst.ts:315:12:315:25 | [FunctionTypeExpr] (x: T) => void | semmle.label | [FunctionTypeExpr] (x: T) => void | +| tst.ts:315:13:315:13 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | +| tst.ts:315:16:315:16 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | +| tst.ts:315:22:315:25 | [KeywordTypeExpr] void | semmle.label | [KeywordTypeExpr] void | +| tst.ts:316:4:316:7 | [KeywordTypeExpr] void | semmle.label | [KeywordTypeExpr] void | +| tst.ts:316:9:316:10 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | +| tst.ts:316:11:316:11 | [EmptyStmt] ; | semmle.label | [EmptyStmt] ; | +| tst.ts:316:11:316:11 | [EmptyStmt] ; | semmle.order | 66 | +| tst.ts:318:1:318:1 | [VarRef] f | semmle.label | [VarRef] f | +| tst.ts:318:1:321:2 | [CallExpr] f({ p ... se() }) | semmle.label | [CallExpr] f({ p ... se() }) | +| tst.ts:318:1:321:3 | [ExprStmt] f({ p ... e() }); | semmle.label | [ExprStmt] f({ p ... e() }); | +| tst.ts:318:1:321:3 | [ExprStmt] f({ p ... e() }); | semmle.order | 67 | +| tst.ts:318:3:321:1 | [ObjectExpr] {produce: ...} | semmle.label | [ObjectExpr] {produce: ...} | +| tst.ts:319:3:319:9 | [Label] produce | semmle.label | [Label] produce | +| tst.ts:319:3:319:17 | [Property] produce: n => n | semmle.label | [Property] produce: n => n | +| tst.ts:319:12:319:12 | [SimpleParameter] n | semmle.label | [SimpleParameter] n | +| tst.ts:319:12:319:17 | [ArrowFunctionExpr] n => n | semmle.label | [ArrowFunctionExpr] n => n | +| tst.ts:319:17:319:17 | [VarRef] n | semmle.label | [VarRef] n | +| tst.ts:320:3:320:9 | [Label] consume | semmle.label | [Label] consume | +| tst.ts:320:3:320:31 | [Property] consume ... rCase() | semmle.label | [Property] consume ... rCase() | +| tst.ts:320:12:320:12 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | +| tst.ts:320:12:320:31 | [ArrowFunctionExpr] x => x.toLowerCase() | semmle.label | [ArrowFunctionExpr] x => x.toLowerCase() | +| tst.ts:320:17:320:17 | [VarRef] x | semmle.label | [VarRef] x | +| tst.ts:320:17:320:29 | [DotExpr] x.toLowerCase | semmle.label | [DotExpr] x.toLowerCase | +| tst.ts:320:17:320:31 | [MethodCallExpr] x.toLowerCase() | semmle.label | [MethodCallExpr] x.toLowerCase() | +| tst.ts:320:19:320:29 | [Label] toLowerCase | semmle.label | [Label] toLowerCase | +| tst.ts:325:1:325:36 | [DeclStmt] const ErrorMap = ... | semmle.label | [DeclStmt] const ErrorMap = ... | +| tst.ts:325:1:325:36 | [DeclStmt] const ErrorMap = ... | semmle.order | 68 | +| tst.ts:325:7:325:14 | [VarDecl] ErrorMap | semmle.label | [VarDecl] ErrorMap | +| tst.ts:325:7:325:35 | [VariableDeclarator] ErrorMa ... Error> | semmle.label | [VariableDeclarator] ErrorMa ... Error> | +| tst.ts:325:18:325:20 | [VarRef] Map | semmle.label | [VarRef] Map | +| tst.ts:325:18:325:35 | [???] Map | semmle.label | [???] Map | +| tst.ts:325:22:325:27 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | +| tst.ts:325:30:325:34 | [LocalTypeAccess] Error | semmle.label | [LocalTypeAccess] Error | +| tst.ts:327:1:327:32 | [DeclStmt] const errorMap = ... | semmle.label | [DeclStmt] const errorMap = ... | +| tst.ts:327:1:327:32 | [DeclStmt] const errorMap = ... | semmle.order | 69 | +| tst.ts:327:7:327:14 | [VarDecl] errorMap | semmle.label | [VarDecl] errorMap | +| tst.ts:327:7:327:31 | [VariableDeclarator] errorMa ... orMap() | semmle.label | [VariableDeclarator] errorMa ... orMap() | +| tst.ts:327:18:327:31 | [NewExpr] new ErrorMap() | semmle.label | [NewExpr] new ErrorMap() | +| tst.ts:327:22:327:29 | [VarRef] ErrorMap | semmle.label | [VarRef] ErrorMap | +| tst.ts:331:1:334:14 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | +| tst.ts:331:1:334:14 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | semmle.order | 70 | +| tst.ts:331:6:331:16 | [Identifier] FirstString | semmle.label | [Identifier] FirstString | +| tst.ts:331:18:331:18 | [Identifier] T | semmle.label | [Identifier] T | +| tst.ts:331:18:331:18 | [TypeParameter] T | semmle.label | [TypeParameter] T | +| tst.ts:332:3:332:3 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | +| tst.ts:332:3:334:13 | [ConditionalTypeExpr] T exten ... : never | semmle.label | [ConditionalTypeExpr] T exten ... : never | +| tst.ts:332:13:332:50 | [TupleTypeExpr] [infer ... nown[]] | semmle.label | [TupleTypeExpr] [infer ... nown[]] | +| tst.ts:332:14:332:35 | [InferTypeExpr] infer S ... string | semmle.label | [InferTypeExpr] infer S ... string | +| tst.ts:332:20:332:20 | [Identifier] S | semmle.label | [Identifier] S | +| tst.ts:332:20:332:35 | [TypeParameter] S extends string | semmle.label | [TypeParameter] S extends string | +| tst.ts:332:30:332:35 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | +| tst.ts:332:38:332:49 | [RestTypeExpr] ...unknown[] | semmle.label | [RestTypeExpr] ...unknown[] | +| tst.ts:332:41:332:47 | [KeywordTypeExpr] unknown | semmle.label | [KeywordTypeExpr] unknown | +| tst.ts:332:41:332:49 | [ArrayTypeExpr] unknown[] | semmle.label | [ArrayTypeExpr] unknown[] | +| tst.ts:333:9:333:9 | [LocalTypeAccess] S | semmle.label | [LocalTypeAccess] S | +| tst.ts:334:9:334:13 | [KeywordTypeExpr] never | semmle.label | [KeywordTypeExpr] never | +| tst.ts:336:1:336:51 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | +| tst.ts:336:1:336:51 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | semmle.order | 71 | +| tst.ts:336:6:336:6 | [Identifier] F | semmle.label | [Identifier] F | +| tst.ts:336:10:336:20 | [LocalTypeAccess] FirstString | semmle.label | [LocalTypeAccess] FirstString | +| tst.ts:336:10:336:50 | [GenericTypeExpr] FirstSt ... olean]> | semmle.label | [GenericTypeExpr] FirstSt ... olean]> | +| tst.ts:336:22:336:49 | [TupleTypeExpr] ['a' \| ... oolean] | semmle.label | [TupleTypeExpr] ['a' \| ... oolean] | +| tst.ts:336:23:336:25 | [LiteralTypeExpr] 'a' | semmle.label | [LiteralTypeExpr] 'a' | +| tst.ts:336:23:336:31 | [UnionTypeExpr] 'a' \| 'b' | semmle.label | [UnionTypeExpr] 'a' \| 'b' | +| tst.ts:336:29:336:31 | [LiteralTypeExpr] 'b' | semmle.label | [LiteralTypeExpr] 'b' | +| tst.ts:336:34:336:39 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | +| tst.ts:336:42:336:48 | [KeywordTypeExpr] boolean | semmle.label | [KeywordTypeExpr] boolean | +| tst.ts:338:1:338:17 | [DeclStmt] const a = ... | semmle.label | [DeclStmt] const a = ... | +| tst.ts:338:1:338:17 | [DeclStmt] const a = ... | semmle.order | 72 | +| tst.ts:338:7:338:7 | [VarDecl] a | semmle.label | [VarDecl] a | +| tst.ts:338:7:338:16 | [VariableDeclarator] a: F = 'a' | semmle.label | [VariableDeclarator] a: F = 'a' | +| tst.ts:338:10:338:10 | [LocalTypeAccess] F | semmle.label | [LocalTypeAccess] F | +| tst.ts:338:14:338:16 | [Literal] 'a' | semmle.label | [Literal] 'a' | +| tst.ts:342:1:345:1 | [InterfaceDeclaration,TypeDefinition] interfa ... void; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... void; } | +| tst.ts:342:1:345:1 | [InterfaceDeclaration,TypeDefinition] interfa ... void; } | semmle.order | 73 | +| tst.ts:342:11:342:15 | [Identifier] State | semmle.label | [Identifier] State | +| tst.ts:342:17:342:24 | [TypeParameter] in out T | semmle.label | [TypeParameter] in out T | +| tst.ts:342:24:342:24 | [Identifier] T | semmle.label | [Identifier] T | +| tst.ts:343:3:343:5 | [Label] get | semmle.label | [Label] get | +| tst.ts:343:3:343:15 | [FieldDeclaration] get: () => T; | semmle.label | [FieldDeclaration] get: () => T; | +| tst.ts:343:8:343:14 | [FunctionExpr] () => T | semmle.label | [FunctionExpr] () => T | +| tst.ts:343:8:343:14 | [FunctionTypeExpr] () => T | semmle.label | [FunctionTypeExpr] () => T | +| tst.ts:343:14:343:14 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | +| tst.ts:344:3:344:5 | [Label] set | semmle.label | [Label] set | +| tst.ts:344:3:344:26 | [FieldDeclaration] set: (v ... > void; | semmle.label | [FieldDeclaration] set: (v ... > void; | +| tst.ts:344:8:344:25 | [FunctionExpr] (value: T) => void | semmle.label | [FunctionExpr] (value: T) => void | +| tst.ts:344:8:344:25 | [FunctionTypeExpr] (value: T) => void | semmle.label | [FunctionTypeExpr] (value: T) => void | +| tst.ts:344:9:344:13 | [SimpleParameter] value | semmle.label | [SimpleParameter] value | | tst.ts:344:16:344:16 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | -| tst.ts:345:5:345:7 | [Label] set | semmle.label | [Label] set | -| tst.ts:345:5:345:28 | [FieldDeclaration] set: (v ... > void; | semmle.label | [FieldDeclaration] set: (v ... > void; | -| tst.ts:345:10:345:27 | [FunctionExpr] (value: T) => void | semmle.label | [FunctionExpr] (value: T) => void | -| tst.ts:345:10:345:27 | [FunctionTypeExpr] (value: T) => void | semmle.label | [FunctionTypeExpr] (value: T) => void | -| tst.ts:345:11:345:15 | [SimpleParameter] value | semmle.label | [SimpleParameter] value | -| tst.ts:345:18:345:18 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | -| tst.ts:345:24:345:27 | [KeywordTypeExpr] void | semmle.label | [KeywordTypeExpr] void | -| tst.ts:348:3:351:3 | [DeclStmt] const state = ... | semmle.label | [DeclStmt] const state = ... | -| tst.ts:348:9:348:13 | [VarDecl] state | semmle.label | [VarDecl] state | -| tst.ts:348:9:351:3 | [VariableDeclarator] state: ... { } } | semmle.label | [VariableDeclarator] state: ... { } } | -| tst.ts:348:16:348:20 | [LocalTypeAccess] State | semmle.label | [LocalTypeAccess] State | -| tst.ts:348:16:348:28 | [GenericTypeExpr] State | semmle.label | [GenericTypeExpr] State | -| tst.ts:348:22:348:27 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | -| tst.ts:348:32:351:3 | [ObjectExpr] {get: ...} | semmle.label | [ObjectExpr] {get: ...} | -| tst.ts:349:5:349:7 | [Label] get | semmle.label | [Label] get | -| tst.ts:349:5:349:17 | [Property] get: () => 42 | semmle.label | [Property] get: () => 42 | -| tst.ts:349:10:349:17 | [ArrowFunctionExpr] () => 42 | semmle.label | [ArrowFunctionExpr] () => 42 | -| tst.ts:349:16:349:17 | [Literal] 42 | semmle.label | [Literal] 42 | -| tst.ts:350:5:350:7 | [Label] set | semmle.label | [Label] set | -| tst.ts:350:5:350:23 | [Property] set: (value) => { } | semmle.label | [Property] set: (value) => { } | -| tst.ts:350:10:350:23 | [ArrowFunctionExpr] (value) => { } | semmle.label | [ArrowFunctionExpr] (value) => { } | -| tst.ts:350:11:350:15 | [SimpleParameter] value | semmle.label | [SimpleParameter] value | -| tst.ts:350:21:350:23 | [BlockStmt] { } | semmle.label | [BlockStmt] { } | -| tst.ts:353:3:353:31 | [DeclStmt] const fortyTwo = ... | semmle.label | [DeclStmt] const fortyTwo = ... | -| tst.ts:353:9:353:16 | [VarDecl] fortyTwo | semmle.label | [VarDecl] fortyTwo | -| tst.ts:353:9:353:30 | [VariableDeclarator] fortyTw ... e.get() | semmle.label | [VariableDeclarator] fortyTw ... e.get() | -| tst.ts:353:20:353:24 | [VarRef] state | semmle.label | [VarRef] state | -| tst.ts:353:20:353:28 | [DotExpr] state.get | semmle.label | [DotExpr] state.get | -| tst.ts:353:20:353:30 | [MethodCallExpr] state.get() | semmle.label | [MethodCallExpr] state.get() | -| tst.ts:353:26:353:28 | [Label] get | semmle.label | [Label] get | +| tst.ts:344:22:344:25 | [KeywordTypeExpr] void | semmle.label | [KeywordTypeExpr] void | +| tst.ts:347:1:350:1 | [DeclStmt] const state = ... | semmle.label | [DeclStmt] const state = ... | +| tst.ts:347:1:350:1 | [DeclStmt] const state = ... | semmle.order | 74 | +| tst.ts:347:7:347:11 | [VarDecl] state | semmle.label | [VarDecl] state | +| tst.ts:347:7:350:1 | [VariableDeclarator] state: ... > { } } | semmle.label | [VariableDeclarator] state: ... > { } } | +| tst.ts:347:14:347:18 | [LocalTypeAccess] State | semmle.label | [LocalTypeAccess] State | +| tst.ts:347:14:347:26 | [GenericTypeExpr] State | semmle.label | [GenericTypeExpr] State | +| tst.ts:347:20:347:25 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | +| tst.ts:347:30:350:1 | [ObjectExpr] {get: ...} | semmle.label | [ObjectExpr] {get: ...} | +| tst.ts:348:3:348:5 | [Label] get | semmle.label | [Label] get | +| tst.ts:348:3:348:15 | [Property] get: () => 42 | semmle.label | [Property] get: () => 42 | +| tst.ts:348:8:348:15 | [ArrowFunctionExpr] () => 42 | semmle.label | [ArrowFunctionExpr] () => 42 | +| tst.ts:348:14:348:15 | [Literal] 42 | semmle.label | [Literal] 42 | +| tst.ts:349:3:349:5 | [Label] set | semmle.label | [Label] set | +| tst.ts:349:3:349:21 | [Property] set: (value) => { } | semmle.label | [Property] set: (value) => { } | +| tst.ts:349:8:349:21 | [ArrowFunctionExpr] (value) => { } | semmle.label | [ArrowFunctionExpr] (value) => { } | +| tst.ts:349:9:349:13 | [SimpleParameter] value | semmle.label | [SimpleParameter] value | +| tst.ts:349:19:349:21 | [BlockStmt] { } | semmle.label | [BlockStmt] { } | +| tst.ts:352:1:352:29 | [DeclStmt] const fortyTwo = ... | semmle.label | [DeclStmt] const fortyTwo = ... | +| tst.ts:352:1:352:29 | [DeclStmt] const fortyTwo = ... | semmle.order | 75 | +| tst.ts:352:7:352:14 | [VarDecl] fortyTwo | semmle.label | [VarDecl] fortyTwo | +| tst.ts:352:7:352:28 | [VariableDeclarator] fortyTw ... e.get() | semmle.label | [VariableDeclarator] fortyTw ... e.get() | +| tst.ts:352:18:352:22 | [VarRef] state | semmle.label | [VarRef] state | +| tst.ts:352:18:352:26 | [DotExpr] state.get | semmle.label | [DotExpr] state.get | +| tst.ts:352:18:352:28 | [MethodCallExpr] state.get() | semmle.label | [MethodCallExpr] state.get() | +| tst.ts:352:24:352:26 | [Label] get | semmle.label | [Label] get | +| tst.ts:356:1:356:44 | [ImportDeclaration] import ... S.mjs'; | semmle.label | [ImportDeclaration] import ... S.mjs'; | +| tst.ts:356:1:356:44 | [ImportDeclaration] import ... S.mjs'; | semmle.order | 76 | +| tst.ts:356:8:356:18 | [ImportSpecifier] tstModuleES | semmle.label | [ImportSpecifier] tstModuleES | +| tst.ts:356:8:356:18 | [VarDecl] tstModuleES | semmle.label | [VarDecl] tstModuleES | +| tst.ts:356:25:356:43 | [Literal] './tstModuleES.mjs' | semmle.label | [Literal] './tstModuleES.mjs' | +| tst.ts:358:1:358:7 | [VarRef] console | semmle.label | [VarRef] console | +| tst.ts:358:1:358:11 | [DotExpr] console.log | semmle.label | [DotExpr] console.log | +| tst.ts:358:1:358:26 | [MethodCallExpr] console ... leES()) | semmle.label | [MethodCallExpr] console ... leES()) | +| tst.ts:358:1:358:27 | [ExprStmt] console ... eES()); | semmle.label | [ExprStmt] console ... eES()); | +| tst.ts:358:1:358:27 | [ExprStmt] console ... eES()); | semmle.order | 77 | +| tst.ts:358:9:358:11 | [Label] log | semmle.label | [Label] log | +| tst.ts:358:13:358:23 | [VarRef] tstModuleES | semmle.label | [VarRef] tstModuleES | +| tst.ts:358:13:358:25 | [CallExpr] tstModuleES() | semmle.label | [CallExpr] tstModuleES() | +| tst.ts:360:1:360:50 | [ImportDeclaration] import ... S.cjs'; | semmle.label | [ImportDeclaration] import ... S.cjs'; | +| tst.ts:360:1:360:50 | [ImportDeclaration] import ... S.cjs'; | semmle.order | 78 | +| tst.ts:360:10:360:21 | [ImportSpecifier] tstModuleCJS | semmle.label | [ImportSpecifier] tstModuleCJS | +| tst.ts:360:10:360:21 | [Label] tstModuleCJS | semmle.label | [Label] tstModuleCJS | +| tst.ts:360:10:360:21 | [VarDecl] tstModuleCJS | semmle.label | [VarDecl] tstModuleCJS | +| tst.ts:360:30:360:49 | [Literal] './tstModuleCJS.cjs' | semmle.label | [Literal] './tstModuleCJS.cjs' | +| tst.ts:362:1:362:7 | [VarRef] console | semmle.label | [VarRef] console | +| tst.ts:362:1:362:11 | [DotExpr] console.log | semmle.label | [DotExpr] console.log | +| tst.ts:362:1:362:27 | [MethodCallExpr] console ... eCJS()) | semmle.label | [MethodCallExpr] console ... eCJS()) | +| tst.ts:362:1:362:28 | [ExprStmt] console ... CJS()); | semmle.label | [ExprStmt] console ... CJS()); | +| tst.ts:362:1:362:28 | [ExprStmt] console ... CJS()); | semmle.order | 79 | +| tst.ts:362:9:362:11 | [Label] log | semmle.label | [Label] log | +| tst.ts:362:13:362:24 | [VarRef] tstModuleCJS | semmle.label | [VarRef] tstModuleCJS | +| tst.ts:362:13:362:26 | [CallExpr] tstModuleCJS() | semmle.label | [CallExpr] tstModuleCJS() | +| tst.ts:368:1:368:34 | [ImportDeclaration] import ... ffixA'; | semmle.label | [ImportDeclaration] import ... ffixA'; | +| tst.ts:368:1:368:34 | [ImportDeclaration] import ... ffixA'; | semmle.order | 80 | +| tst.ts:368:8:368:13 | [ImportSpecifier] * as A | semmle.label | [ImportSpecifier] * as A | +| tst.ts:368:13:368:13 | [VarDecl] A | semmle.label | [VarDecl] A | +| tst.ts:368:20:368:33 | [Literal] './tstSuffixA' | semmle.label | [Literal] './tstSuffixA' | +| tst.ts:370:1:370:7 | [VarRef] console | semmle.label | [VarRef] console | +| tst.ts:370:1:370:11 | [DotExpr] console.log | semmle.label | [DotExpr] console.log | +| tst.ts:370:1:370:29 | [MethodCallExpr] console ... File()) | semmle.label | [MethodCallExpr] console ... File()) | +| tst.ts:370:1:370:30 | [ExprStmt] console ... ile()); | semmle.label | [ExprStmt] console ... ile()); | +| tst.ts:370:1:370:30 | [ExprStmt] console ... ile()); | semmle.order | 81 | +| tst.ts:370:9:370:11 | [Label] log | semmle.label | [Label] log | +| tst.ts:370:13:370:13 | [VarRef] A | semmle.label | [VarRef] A | +| tst.ts:370:13:370:26 | [DotExpr] A.resolvedFile | semmle.label | [DotExpr] A.resolvedFile | +| tst.ts:370:13:370:28 | [MethodCallExpr] A.resolvedFile() | semmle.label | [MethodCallExpr] A.resolvedFile() | +| tst.ts:370:15:370:26 | [Label] resolvedFile | semmle.label | [Label] resolvedFile | +| tst.ts:372:1:372:34 | [ImportDeclaration] import ... ffixB'; | semmle.label | [ImportDeclaration] import ... ffixB'; | +| tst.ts:372:1:372:34 | [ImportDeclaration] import ... ffixB'; | semmle.order | 82 | +| tst.ts:372:8:372:13 | [ImportSpecifier] * as B | semmle.label | [ImportSpecifier] * as B | +| tst.ts:372:13:372:13 | [VarDecl] B | semmle.label | [VarDecl] B | +| tst.ts:372:20:372:33 | [Literal] './tstSuffixB' | semmle.label | [Literal] './tstSuffixB' | +| tst.ts:374:1:374:7 | [VarRef] console | semmle.label | [VarRef] console | +| tst.ts:374:1:374:11 | [DotExpr] console.log | semmle.label | [DotExpr] console.log | +| tst.ts:374:1:374:29 | [MethodCallExpr] console ... File()) | semmle.label | [MethodCallExpr] console ... File()) | +| tst.ts:374:1:374:30 | [ExprStmt] console ... ile()); | semmle.label | [ExprStmt] console ... ile()); | +| tst.ts:374:1:374:30 | [ExprStmt] console ... ile()); | semmle.order | 83 | +| tst.ts:374:9:374:11 | [Label] log | semmle.label | [Label] log | +| tst.ts:374:13:374:13 | [VarRef] B | semmle.label | [VarRef] B | +| tst.ts:374:13:374:26 | [DotExpr] B.resolvedFile | semmle.label | [DotExpr] B.resolvedFile | +| tst.ts:374:13:374:28 | [MethodCallExpr] B.resolvedFile() | semmle.label | [MethodCallExpr] B.resolvedFile() | +| tst.ts:374:15:374:26 | [Label] resolvedFile | semmle.label | [Label] resolvedFile | +| 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 | 84 | +| 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' | +| tstModuleCJS.cts:1:33:1:41 | [UnionTypeExpr] 'a' \| 'b' | semmle.label | [UnionTypeExpr] 'a' \| 'b' | +| tstModuleCJS.cts:1:39:1:41 | [LiteralTypeExpr] 'b' | semmle.label | [LiteralTypeExpr] 'b' | +| tstModuleCJS.cts:1:43:3:1 | [BlockStmt] { r ... 'b'; } | semmle.label | [BlockStmt] { r ... 'b'; } | +| tstModuleCJS.cts:2:5:2:43 | [ReturnStmt] return ... : 'b'; | semmle.label | [ReturnStmt] return ... : 'b'; | +| tstModuleCJS.cts:2:12:2:15 | [VarRef] Math | semmle.label | [VarRef] Math | +| tstModuleCJS.cts:2:12:2:22 | [DotExpr] Math.random | semmle.label | [DotExpr] Math.random | +| tstModuleCJS.cts:2:12:2:24 | [MethodCallExpr] Math.random() | semmle.label | [MethodCallExpr] Math.random() | +| tstModuleCJS.cts:2:12:2:30 | [BinaryExpr] Math.random() > 0.5 | semmle.label | [BinaryExpr] Math.random() > 0.5 | +| tstModuleCJS.cts:2:12:2:42 | [ConditionalExpr] Math.ra ... ' : 'b' | semmle.label | [ConditionalExpr] Math.ra ... ' : 'b' | +| tstModuleCJS.cts:2:17:2:22 | [Label] random | semmle.label | [Label] random | +| tstModuleCJS.cts:2:28:2:30 | [Literal] 0.5 | semmle.label | [Literal] 0.5 | +| 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 | 85 | +| 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' | +| tstModuleES.mts:1:40:1:48 | [UnionTypeExpr] 'a' \| 'b' | semmle.label | [UnionTypeExpr] 'a' \| 'b' | +| tstModuleES.mts:1:46:1:48 | [LiteralTypeExpr] 'b' | semmle.label | [LiteralTypeExpr] 'b' | +| tstModuleES.mts:1:50:3:1 | [BlockStmt] { r ... 'b'; } | semmle.label | [BlockStmt] { r ... 'b'; } | +| tstModuleES.mts:2:5:2:43 | [ReturnStmt] return ... : 'b'; | semmle.label | [ReturnStmt] return ... : 'b'; | +| tstModuleES.mts:2:12:2:15 | [VarRef] Math | semmle.label | [VarRef] Math | +| tstModuleES.mts:2:12:2:22 | [DotExpr] Math.random | semmle.label | [DotExpr] Math.random | +| tstModuleES.mts:2:12:2:24 | [MethodCallExpr] Math.random() | semmle.label | [MethodCallExpr] Math.random() | +| tstModuleES.mts:2:12:2:30 | [BinaryExpr] Math.random() > 0.5 | semmle.label | [BinaryExpr] Math.random() > 0.5 | +| tstModuleES.mts:2:12:2:42 | [ConditionalExpr] Math.ra ... ' : 'b' | semmle.label | [ConditionalExpr] Math.ra ... ' : 'b' | +| tstModuleES.mts:2:17:2:22 | [Label] random | semmle.label | [Label] random | +| tstModuleES.mts:2:28:2:30 | [Literal] 0.5 | semmle.label | [Literal] 0.5 | +| 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 | 86 | +| 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' | +| tstSuffixA.ts:1:49:3:1 | [BlockStmt] { r ... .ts'; } | semmle.label | [BlockStmt] { r ... .ts'; } | +| 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 | 87 | +| 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' | +| tstSuffixB.ios.ts:1:53:3:1 | [BlockStmt] { r ... .ts'; } | semmle.label | [BlockStmt] { r ... .ts'; } | +| 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 | 88 | +| 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' | +| tstSuffixB.ts:1:49:3:1 | [BlockStmt] { r ... .ts'; } | semmle.label | [BlockStmt] { r ... .ts'; } | +| 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 | 62 | +| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.order | 89 | | 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 | 63 | +| type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.order | 90 | | 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 | 64 | +| type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.order | 91 | | 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 | @@ -1331,14 +1463,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 | 65 | +| type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.order | 92 | | 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 | 66 | +| type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.order | 93 | | 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 | @@ -1354,12 +1486,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 | 67 | +| type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.order | 94 | | 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 | 68 | +| type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.order | 95 | | 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 | @@ -1375,7 +1507,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 | 69 | +| type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.order | 96 | | 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 | @@ -1400,12 +1532,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 | 70 | +| type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 97 | | 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 | 71 | +| type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.order | 98 | | 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] {} | @@ -1413,36 +1545,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 | 72 | +| type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.order | 99 | | 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 | 73 | +| type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.order | 100 | | 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 | 74 | +| type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.order | 101 | | 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 | 75 | +| type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.order | 102 | | 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 | 76 | +| type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.order | 103 | | 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 | 77 | +| type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 104 | | 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 | 78 | +| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.order | 105 | | 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 | @@ -1450,14 +1582,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 | 79 | +| type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.order | 106 | | 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 | 80 | +| type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.order | 107 | | 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() {} | @@ -1469,14 +1601,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 | 81 | +| type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.order | 108 | | 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 | 82 | +| type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.order | 109 | | 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 | @@ -1485,29 +1617,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 | 83 | +| type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.order | 110 | | 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 | 84 | +| type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.order | 111 | | 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 | 85 | +| type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.order | 112 | | 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 | 86 | +| type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.order | 113 | | 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 | 87 | +| type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.order | 114 | | 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 | @@ -1652,8 +1784,16 @@ edges | file://:0:0:0:0 | (Arguments) | tst.ts:282:17:287:3 | [ObjectExpr] {kind: ...} | semmle.order | 0 | | file://:0:0:0:0 | (Arguments) | tst.ts:285:19:285:35 | [MethodCallExpr] val.toUpperCase() | semmle.label | 0 | | file://:0:0:0:0 | (Arguments) | tst.ts:285:19:285:35 | [MethodCallExpr] val.toUpperCase() | semmle.order | 0 | -| file://:0:0:0:0 | (Arguments) | tst.ts:319:5:322:3 | [ObjectExpr] {produce: ...} | semmle.label | 0 | -| file://:0:0:0:0 | (Arguments) | tst.ts:319:5:322:3 | [ObjectExpr] {produce: ...} | semmle.order | 0 | +| file://:0:0:0:0 | (Arguments) | tst.ts:318:3:321:1 | [ObjectExpr] {produce: ...} | semmle.label | 0 | +| file://:0:0:0:0 | (Arguments) | tst.ts:318:3:321:1 | [ObjectExpr] {produce: ...} | semmle.order | 0 | +| file://:0:0:0:0 | (Arguments) | tst.ts:358:13:358:25 | [CallExpr] tstModuleES() | semmle.label | 0 | +| file://:0:0:0:0 | (Arguments) | tst.ts:358:13:358:25 | [CallExpr] tstModuleES() | semmle.order | 0 | +| file://:0:0:0:0 | (Arguments) | tst.ts:362:13:362:26 | [CallExpr] tstModuleCJS() | semmle.label | 0 | +| file://:0:0:0:0 | (Arguments) | tst.ts:362:13:362:26 | [CallExpr] tstModuleCJS() | semmle.order | 0 | +| file://:0:0:0:0 | (Arguments) | tst.ts:370:13:370:28 | [MethodCallExpr] A.resolvedFile() | semmle.label | 0 | +| file://:0:0:0:0 | (Arguments) | tst.ts:370:13:370:28 | [MethodCallExpr] A.resolvedFile() | semmle.order | 0 | +| file://:0:0:0:0 | (Arguments) | tst.ts:374:13:374:28 | [MethodCallExpr] B.resolvedFile() | semmle.label | 0 | +| file://:0:0:0:0 | (Arguments) | tst.ts:374:13:374:28 | [MethodCallExpr] B.resolvedFile() | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | tst.ts:14:17:14:17 | [SimpleParameter] x | semmle.label | 0 | | file://:0:0:0:0 | (Parameters) | tst.ts:14:17:14:17 | [SimpleParameter] x | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | tst.ts:14:28:14:28 | [SimpleParameter] y | semmle.label | 1 | @@ -1712,20 +1852,20 @@ edges | file://:0:0:0:0 | (Parameters) | tst.ts:291:21:291:24 | [SimpleParameter] kind | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | tst.ts:291:27:291:33 | [SimpleParameter] payload | semmle.label | 1 | | file://:0:0:0:0 | (Parameters) | tst.ts:291:27:291:33 | [SimpleParameter] payload | semmle.order | 1 | -| file://:0:0:0:0 | (Parameters) | tst.ts:314:17:314:19 | [SimpleParameter] arg | semmle.label | 0 | -| file://:0:0:0:0 | (Parameters) | tst.ts:314:17:314:19 | [SimpleParameter] arg | semmle.order | 0 | -| file://:0:0:0:0 | (Parameters) | tst.ts:315:15:315:15 | [SimpleParameter] n | semmle.label | 0 | -| file://:0:0:0:0 | (Parameters) | tst.ts:315:15:315:15 | [SimpleParameter] n | semmle.order | 0 | -| file://:0:0:0:0 | (Parameters) | tst.ts:316:15:316:15 | [SimpleParameter] x | semmle.label | 0 | -| file://:0:0:0:0 | (Parameters) | tst.ts:316:15:316:15 | [SimpleParameter] x | semmle.order | 0 | -| file://:0:0:0:0 | (Parameters) | tst.ts:320:14:320:14 | [SimpleParameter] n | semmle.label | 0 | -| file://:0:0:0:0 | (Parameters) | tst.ts:320:14:320:14 | [SimpleParameter] n | semmle.order | 0 | -| file://:0:0:0:0 | (Parameters) | tst.ts:321:14:321:14 | [SimpleParameter] x | semmle.label | 0 | -| file://:0:0:0:0 | (Parameters) | tst.ts:321:14:321:14 | [SimpleParameter] x | semmle.order | 0 | -| file://:0:0:0:0 | (Parameters) | tst.ts:345:11:345:15 | [SimpleParameter] value | semmle.label | 0 | -| file://:0:0:0:0 | (Parameters) | tst.ts:345:11:345:15 | [SimpleParameter] value | semmle.order | 0 | -| file://:0:0:0:0 | (Parameters) | tst.ts:350:11:350:15 | [SimpleParameter] value | semmle.label | 0 | -| file://:0:0:0:0 | (Parameters) | tst.ts:350:11:350:15 | [SimpleParameter] value | semmle.order | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:313:15:313:17 | [SimpleParameter] arg | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:313:15:313:17 | [SimpleParameter] arg | semmle.order | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:314:13:314:13 | [SimpleParameter] n | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:314:13:314:13 | [SimpleParameter] n | semmle.order | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:315:13:315:13 | [SimpleParameter] x | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:315:13:315:13 | [SimpleParameter] x | semmle.order | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:319:12:319:12 | [SimpleParameter] n | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:319:12:319:12 | [SimpleParameter] n | semmle.order | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:320:12:320:12 | [SimpleParameter] x | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:320:12:320:12 | [SimpleParameter] x | semmle.order | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:344:9:344:13 | [SimpleParameter] value | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:344:9:344:13 | [SimpleParameter] value | semmle.order | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:349:9:349:13 | [SimpleParameter] value | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:349:9:349:13 | [SimpleParameter] value | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | type_alias.ts:14:10:14:17 | [SimpleParameter] property | semmle.label | 0 | | file://:0:0:0:0 | (Parameters) | type_alias.ts:14:10:14:17 | [SimpleParameter] property | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | type_alias.ts:21:19:21:21 | [SimpleParameter] key | semmle.label | 0 | @@ -1736,14 +1876,14 @@ edges | file://:0:0:0:0 | (TypeParameters) | tst.ts:272:6:272:11 | [TypeParameter] K in P | semmle.order | 0 | | file://:0:0:0:0 | (TypeParameters) | tst.ts:278:26:278:48 | [TypeParameter] K exten ... TypeMap | semmle.label | 0 | | file://:0:0:0:0 | (TypeParameters) | tst.ts:278:26:278:48 | [TypeParameter] K exten ... TypeMap | semmle.order | 0 | -| file://:0:0:0:0 | (TypeParameters) | tst.ts:314:14:314:14 | [TypeParameter] T | semmle.label | 0 | -| file://:0:0:0:0 | (TypeParameters) | tst.ts:314:14:314:14 | [TypeParameter] T | semmle.order | 0 | -| file://:0:0:0:0 | (TypeParameters) | tst.ts:332:20:332:20 | [TypeParameter] T | semmle.label | 0 | -| file://:0:0:0:0 | (TypeParameters) | tst.ts:332:20:332:20 | [TypeParameter] T | semmle.order | 0 | -| file://:0:0:0:0 | (TypeParameters) | tst.ts:333:22:333:37 | [TypeParameter] S extends string | semmle.label | 0 | -| file://:0:0:0:0 | (TypeParameters) | tst.ts:333:22:333:37 | [TypeParameter] S extends string | semmle.order | 0 | -| file://:0:0:0:0 | (TypeParameters) | tst.ts:343:19:343:26 | [TypeParameter] in out T | semmle.label | 0 | -| file://:0:0:0:0 | (TypeParameters) | tst.ts:343:19:343:26 | [TypeParameter] in out T | semmle.order | 0 | +| file://:0:0:0:0 | (TypeParameters) | tst.ts:313:12:313:12 | [TypeParameter] T | semmle.label | 0 | +| file://:0:0:0:0 | (TypeParameters) | tst.ts:313:12:313:12 | [TypeParameter] T | semmle.order | 0 | +| file://:0:0:0:0 | (TypeParameters) | tst.ts:331:18:331:18 | [TypeParameter] T | semmle.label | 0 | +| file://:0:0:0:0 | (TypeParameters) | tst.ts:331:18:331:18 | [TypeParameter] T | semmle.order | 0 | +| file://:0:0:0:0 | (TypeParameters) | tst.ts:332:20:332:35 | [TypeParameter] S extends string | semmle.label | 0 | +| file://:0:0:0:0 | (TypeParameters) | tst.ts:332:20:332:35 | [TypeParameter] S extends string | semmle.order | 0 | +| file://:0:0:0:0 | (TypeParameters) | tst.ts:342:17:342:24 | [TypeParameter] in out T | semmle.label | 0 | +| file://:0:0:0:0 | (TypeParameters) | tst.ts:342:17:342:24 | [TypeParameter] in out T | semmle.order | 0 | | file://:0:0:0:0 | (TypeParameters) | type_alias.ts:5:19:5:19 | [TypeParameter] T | semmle.label | 0 | | file://:0:0:0:0 | (TypeParameters) | type_alias.ts:5:19:5:19 | [TypeParameter] T | semmle.order | 0 | | file://:0:0:0:0 | (TypeParameters) | type_definitions.ts:3:13:3:13 | [TypeParameter] S | semmle.label | 0 | @@ -3556,340 +3696,488 @@ edges | tst.ts:293:7:293:23 | [MethodCallExpr] payload.toFixed() | tst.ts:293:7:293:21 | [DotExpr] payload.toFixed | semmle.order | 0 | | tst.ts:293:7:293:24 | [ExprStmt] payload.toFixed(); | tst.ts:293:7:293:23 | [MethodCallExpr] payload.toFixed() | semmle.label | 1 | | tst.ts:293:7:293:24 | [ExprStmt] payload.toFixed(); | tst.ts:293:7:293:23 | [MethodCallExpr] payload.toFixed() | semmle.order | 1 | -| tst.ts:298:1:376:1 | [FunctionDeclStmt] functio ... ring } | tst.ts:298:10:298:15 | [VarDecl] foo_47 | semmle.label | 0 | -| tst.ts:298:1:376:1 | [FunctionDeclStmt] functio ... ring } | tst.ts:298:10:298:15 | [VarDecl] foo_47 | semmle.order | 0 | -| tst.ts:298:1:376:1 | [FunctionDeclStmt] functio ... ring } | tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | semmle.label | 5 | -| tst.ts:298:1:376:1 | [FunctionDeclStmt] functio ... ring } | tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | semmle.order | 5 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:299:3:299:23 | [DeclStmt] const key = ... | semmle.label | 1 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:299:3:299:23 | [DeclStmt] const key = ... | semmle.order | 1 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:301:3:301:60 | [DeclStmt] const numberOrString = ... | semmle.label | 2 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:301:3:301:60 | [DeclStmt] const numberOrString = ... | semmle.order | 2 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:303:3:305:4 | [DeclStmt] let obj = ... | semmle.label | 3 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:303:3:305:4 | [DeclStmt] let obj = ... | semmle.order | 3 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:307:3:310:3 | [IfStmt] if (typ ... (); } | semmle.label | 4 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:307:3:310:3 | [IfStmt] if (typ ... (); } | semmle.order | 4 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | semmle.label | 5 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | semmle.order | 5 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:317:13:317:13 | [EmptyStmt] ; | semmle.label | 6 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:317:13:317:13 | [EmptyStmt] ; | semmle.order | 6 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:319:3:322:5 | [ExprStmt] f({ ... ) }); | semmle.label | 7 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:319:3:322:5 | [ExprStmt] f({ ... ) }); | semmle.order | 7 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:326:3:326:38 | [DeclStmt] const ErrorMap = ... | semmle.label | 8 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:326:3:326:38 | [DeclStmt] const ErrorMap = ... | semmle.order | 8 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:328:3:328:34 | [DeclStmt] const errorMap = ... | semmle.label | 9 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:328:3:328:34 | [DeclStmt] const errorMap = ... | semmle.order | 9 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:332:3:335:16 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | semmle.label | 10 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:332:3:335:16 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | semmle.order | 10 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:337:3:337:53 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | semmle.label | 11 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:337:3:337:53 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | semmle.order | 11 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:339:3:339:19 | [DeclStmt] const a = ... | semmle.label | 12 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:339:3:339:19 | [DeclStmt] const a = ... | semmle.order | 12 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | semmle.label | 13 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | semmle.order | 13 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:348:3:351:3 | [DeclStmt] const state = ... | semmle.label | 14 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:348:3:351:3 | [DeclStmt] const state = ... | semmle.order | 14 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:353:3:353:31 | [DeclStmt] const fortyTwo = ... | semmle.label | 15 | -| tst.ts:298:19:376:1 | [BlockStmt] { con ... ring } | tst.ts:353:3:353:31 | [DeclStmt] const fortyTwo = ... | semmle.order | 15 | -| tst.ts:299:3:299:23 | [DeclStmt] const key = ... | tst.ts:299:9:299:22 | [VariableDeclarator] key = Symbol() | semmle.label | 1 | -| tst.ts:299:3:299:23 | [DeclStmt] const key = ... | tst.ts:299:9:299:22 | [VariableDeclarator] key = Symbol() | semmle.order | 1 | -| tst.ts:299:9:299:22 | [VariableDeclarator] key = Symbol() | tst.ts:299:9:299:11 | [VarDecl] key | semmle.label | 1 | -| tst.ts:299:9:299:22 | [VariableDeclarator] key = Symbol() | tst.ts:299:9:299:11 | [VarDecl] key | semmle.order | 1 | -| tst.ts:299:9:299:22 | [VariableDeclarator] key = Symbol() | tst.ts:299:15:299:22 | [CallExpr] Symbol() | semmle.label | 2 | -| tst.ts:299:9:299:22 | [VariableDeclarator] key = Symbol() | tst.ts:299:15:299:22 | [CallExpr] Symbol() | semmle.order | 2 | -| tst.ts:299:15:299:22 | [CallExpr] Symbol() | tst.ts:299:15:299:20 | [VarRef] Symbol | semmle.label | 0 | -| tst.ts:299:15:299:22 | [CallExpr] Symbol() | tst.ts:299:15:299:20 | [VarRef] Symbol | semmle.order | 0 | -| tst.ts:301:3:301:60 | [DeclStmt] const numberOrString = ... | tst.ts:301:9:301:59 | [VariableDeclarator] numberO ... "hello" | semmle.label | 1 | -| tst.ts:301:3:301:60 | [DeclStmt] const numberOrString = ... | tst.ts:301:9:301:59 | [VariableDeclarator] numberO ... "hello" | semmle.order | 1 | -| tst.ts:301:9:301:59 | [VariableDeclarator] numberO ... "hello" | tst.ts:301:9:301:22 | [VarDecl] numberOrString | semmle.label | 1 | -| tst.ts:301:9:301:59 | [VariableDeclarator] numberO ... "hello" | tst.ts:301:9:301:22 | [VarDecl] numberOrString | semmle.order | 1 | -| tst.ts:301:9:301:59 | [VariableDeclarator] numberO ... "hello" | tst.ts:301:26:301:59 | [ConditionalExpr] Math.ra ... "hello" | semmle.label | 2 | -| tst.ts:301:9:301:59 | [VariableDeclarator] numberO ... "hello" | tst.ts:301:26:301:59 | [ConditionalExpr] Math.ra ... "hello" | semmle.order | 2 | -| tst.ts:301:26:301:36 | [DotExpr] Math.random | tst.ts:301:26:301:29 | [VarRef] Math | semmle.label | 1 | -| tst.ts:301:26:301:36 | [DotExpr] Math.random | tst.ts:301:26:301:29 | [VarRef] Math | semmle.order | 1 | -| tst.ts:301:26:301:36 | [DotExpr] Math.random | tst.ts:301:31:301:36 | [Label] random | semmle.label | 2 | -| tst.ts:301:26:301:36 | [DotExpr] Math.random | tst.ts:301:31:301:36 | [Label] random | semmle.order | 2 | -| tst.ts:301:26:301:38 | [MethodCallExpr] Math.random() | tst.ts:301:26:301:36 | [DotExpr] Math.random | semmle.label | 0 | -| tst.ts:301:26:301:38 | [MethodCallExpr] Math.random() | tst.ts:301:26:301:36 | [DotExpr] Math.random | semmle.order | 0 | -| tst.ts:301:26:301:44 | [BinaryExpr] Math.random() < 0.5 | tst.ts:301:26:301:38 | [MethodCallExpr] Math.random() | semmle.label | 1 | -| tst.ts:301:26:301:44 | [BinaryExpr] Math.random() < 0.5 | tst.ts:301:26:301:38 | [MethodCallExpr] Math.random() | semmle.order | 1 | -| tst.ts:301:26:301:44 | [BinaryExpr] Math.random() < 0.5 | tst.ts:301:42:301:44 | [Literal] 0.5 | semmle.label | 2 | -| tst.ts:301:26:301:44 | [BinaryExpr] Math.random() < 0.5 | tst.ts:301:42:301:44 | [Literal] 0.5 | semmle.order | 2 | -| tst.ts:301:26:301:59 | [ConditionalExpr] Math.ra ... "hello" | tst.ts:301:26:301:44 | [BinaryExpr] Math.random() < 0.5 | semmle.label | 1 | -| tst.ts:301:26:301:59 | [ConditionalExpr] Math.ra ... "hello" | tst.ts:301:26:301:44 | [BinaryExpr] Math.random() < 0.5 | semmle.order | 1 | -| tst.ts:301:26:301:59 | [ConditionalExpr] Math.ra ... "hello" | tst.ts:301:48:301:49 | [Literal] 42 | semmle.label | 2 | -| tst.ts:301:26:301:59 | [ConditionalExpr] Math.ra ... "hello" | tst.ts:301:48:301:49 | [Literal] 42 | semmle.order | 2 | -| tst.ts:301:26:301:59 | [ConditionalExpr] Math.ra ... "hello" | tst.ts:301:53:301:59 | [Literal] "hello" | semmle.label | 3 | -| tst.ts:301:26:301:59 | [ConditionalExpr] Math.ra ... "hello" | tst.ts:301:53:301:59 | [Literal] "hello" | semmle.order | 3 | -| tst.ts:303:3:305:4 | [DeclStmt] let obj = ... | tst.ts:303:7:305:3 | [VariableDeclarator] obj = { ... ng, } | semmle.label | 1 | -| tst.ts:303:3:305:4 | [DeclStmt] let obj = ... | tst.ts:303:7:305:3 | [VariableDeclarator] obj = { ... ng, } | semmle.order | 1 | -| tst.ts:303:7:305:3 | [VariableDeclarator] obj = { ... ng, } | tst.ts:303:7:303:9 | [VarDecl] obj | semmle.label | 1 | -| tst.ts:303:7:305:3 | [VariableDeclarator] obj = { ... ng, } | tst.ts:303:7:303:9 | [VarDecl] obj | semmle.order | 1 | -| tst.ts:303:7:305:3 | [VariableDeclarator] obj = { ... ng, } | tst.ts:303:13:305:3 | [ObjectExpr] { [ ... ng, } | semmle.label | 2 | -| tst.ts:303:7:305:3 | [VariableDeclarator] obj = { ... ng, } | tst.ts:303:13:305:3 | [ObjectExpr] { [ ... ng, } | semmle.order | 2 | -| tst.ts:303:13:305:3 | [ObjectExpr] { [ ... ng, } | tst.ts:304:5:304:25 | [Property] [key]: ... rString | semmle.label | 1 | -| tst.ts:303:13:305:3 | [ObjectExpr] { [ ... ng, } | tst.ts:304:5:304:25 | [Property] [key]: ... rString | semmle.order | 1 | -| tst.ts:304:5:304:25 | [Property] [key]: ... rString | tst.ts:304:6:304:8 | [VarRef] key | semmle.label | 1 | -| tst.ts:304:5:304:25 | [Property] [key]: ... rString | tst.ts:304:6:304:8 | [VarRef] key | semmle.order | 1 | -| tst.ts:304:5:304:25 | [Property] [key]: ... rString | tst.ts:304:12:304:25 | [VarRef] numberOrString | semmle.label | 2 | -| tst.ts:304:5:304:25 | [Property] [key]: ... rString | tst.ts:304:12:304:25 | [VarRef] numberOrString | semmle.order | 2 | -| tst.ts:307:3:310:3 | [IfStmt] if (typ ... (); } | tst.ts:307:7:307:34 | [BinaryExpr] typeof ... string" | semmle.label | 1 | -| tst.ts:307:3:310:3 | [IfStmt] if (typ ... (); } | tst.ts:307:7:307:34 | [BinaryExpr] typeof ... string" | semmle.order | 1 | -| tst.ts:307:3:310:3 | [IfStmt] if (typ ... (); } | tst.ts:307:37:310:3 | [BlockStmt] { l ... (); } | semmle.label | 2 | -| tst.ts:307:3:310:3 | [IfStmt] if (typ ... (); } | tst.ts:307:37:310:3 | [BlockStmt] { l ... (); } | semmle.order | 2 | -| tst.ts:307:7:307:21 | [UnaryExpr] typeof obj[key] | tst.ts:307:14:307:21 | [IndexExpr] obj[key] | semmle.label | 1 | -| tst.ts:307:7:307:21 | [UnaryExpr] typeof obj[key] | tst.ts:307:14:307:21 | [IndexExpr] obj[key] | semmle.order | 1 | -| tst.ts:307:7:307:34 | [BinaryExpr] typeof ... string" | tst.ts:307:7:307:21 | [UnaryExpr] typeof obj[key] | semmle.label | 1 | -| tst.ts:307:7:307:34 | [BinaryExpr] typeof ... string" | tst.ts:307:7:307:21 | [UnaryExpr] typeof obj[key] | semmle.order | 1 | -| tst.ts:307:7:307:34 | [BinaryExpr] typeof ... string" | tst.ts:307:27:307:34 | [Literal] "string" | semmle.label | 2 | -| tst.ts:307:7:307:34 | [BinaryExpr] typeof ... string" | tst.ts:307:27:307:34 | [Literal] "string" | semmle.order | 2 | -| tst.ts:307:14:307:21 | [IndexExpr] obj[key] | tst.ts:307:14:307:16 | [VarRef] obj | semmle.label | 1 | -| tst.ts:307:14:307:21 | [IndexExpr] obj[key] | tst.ts:307:14:307:16 | [VarRef] obj | semmle.order | 1 | -| tst.ts:307:14:307:21 | [IndexExpr] obj[key] | tst.ts:307:18:307:20 | [VarRef] key | semmle.label | 2 | -| tst.ts:307:14:307:21 | [IndexExpr] obj[key] | tst.ts:307:18:307:20 | [VarRef] key | semmle.order | 2 | -| tst.ts:307:37:310:3 | [BlockStmt] { l ... (); } | tst.ts:308:5:308:23 | [DeclStmt] let str = ... | semmle.label | 1 | -| tst.ts:307:37:310:3 | [BlockStmt] { l ... (); } | tst.ts:308:5:308:23 | [DeclStmt] let str = ... | semmle.order | 1 | -| tst.ts:307:37:310:3 | [BlockStmt] { l ... (); } | tst.ts:309:5:309:22 | [ExprStmt] str.toUpperCase(); | semmle.label | 2 | -| tst.ts:307:37:310:3 | [BlockStmt] { l ... (); } | tst.ts:309:5:309:22 | [ExprStmt] str.toUpperCase(); | semmle.order | 2 | -| tst.ts:308:5:308:23 | [DeclStmt] let str = ... | tst.ts:308:9:308:22 | [VariableDeclarator] str = obj[key] | semmle.label | 1 | -| tst.ts:308:5:308:23 | [DeclStmt] let str = ... | tst.ts:308:9:308:22 | [VariableDeclarator] str = obj[key] | semmle.order | 1 | -| tst.ts:308:9:308:22 | [VariableDeclarator] str = obj[key] | tst.ts:308:9:308:11 | [VarDecl] str | semmle.label | 1 | -| tst.ts:308:9:308:22 | [VariableDeclarator] str = obj[key] | tst.ts:308:9:308:11 | [VarDecl] str | semmle.order | 1 | -| tst.ts:308:9:308:22 | [VariableDeclarator] str = obj[key] | tst.ts:308:15:308:22 | [IndexExpr] obj[key] | semmle.label | 2 | -| tst.ts:308:9:308:22 | [VariableDeclarator] str = obj[key] | tst.ts:308:15:308:22 | [IndexExpr] obj[key] | semmle.order | 2 | -| tst.ts:308:15:308:22 | [IndexExpr] obj[key] | tst.ts:308:15:308:17 | [VarRef] obj | semmle.label | 1 | -| tst.ts:308:15:308:22 | [IndexExpr] obj[key] | tst.ts:308:15:308:17 | [VarRef] obj | semmle.order | 1 | -| tst.ts:308:15:308:22 | [IndexExpr] obj[key] | tst.ts:308:19:308:21 | [VarRef] key | semmle.label | 2 | -| tst.ts:308:15:308:22 | [IndexExpr] obj[key] | tst.ts:308:19:308:21 | [VarRef] key | semmle.order | 2 | -| tst.ts:309:5:309:19 | [DotExpr] str.toUpperCase | tst.ts:309:5:309:7 | [VarRef] str | semmle.label | 1 | -| tst.ts:309:5:309:19 | [DotExpr] str.toUpperCase | tst.ts:309:5:309:7 | [VarRef] str | semmle.order | 1 | -| tst.ts:309:5:309:19 | [DotExpr] str.toUpperCase | tst.ts:309:9:309:19 | [Label] toUpperCase | semmle.label | 2 | -| tst.ts:309:5:309:19 | [DotExpr] str.toUpperCase | tst.ts:309:9:309:19 | [Label] toUpperCase | semmle.order | 2 | -| tst.ts:309:5:309:21 | [MethodCallExpr] str.toUpperCase() | tst.ts:309:5:309:19 | [DotExpr] str.toUpperCase | semmle.label | 0 | -| tst.ts:309:5:309:21 | [MethodCallExpr] str.toUpperCase() | tst.ts:309:5:309:19 | [DotExpr] str.toUpperCase | semmle.order | 0 | -| tst.ts:309:5:309:22 | [ExprStmt] str.toUpperCase(); | tst.ts:309:5:309:21 | [MethodCallExpr] str.toUpperCase() | semmle.label | 1 | -| tst.ts:309:5:309:22 | [ExprStmt] str.toUpperCase(); | tst.ts:309:5:309:21 | [MethodCallExpr] str.toUpperCase() | semmle.order | 1 | -| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | -| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | -| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | file://:0:0:0:0 | (TypeParameters) | semmle.label | 2 | -| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | file://:0:0:0:0 | (TypeParameters) | semmle.order | 2 | -| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | tst.ts:314:12:314:12 | [VarDecl] f | semmle.label | 0 | -| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | tst.ts:314:12:314:12 | [VarDecl] f | semmle.order | 0 | -| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | tst.ts:317:6:317:9 | [KeywordTypeExpr] void | semmle.label | 4 | -| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | tst.ts:317:6:317:9 | [KeywordTypeExpr] void | semmle.order | 4 | -| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | tst.ts:317:11:317:12 | [BlockStmt] {} | semmle.label | 5 | -| tst.ts:314:3:317:12 | [FunctionDeclStmt] functio ... void {} | tst.ts:317:11:317:12 | [BlockStmt] {} | semmle.order | 5 | -| tst.ts:314:14:314:14 | [TypeParameter] T | tst.ts:314:14:314:14 | [Identifier] T | semmle.label | 1 | -| tst.ts:314:14:314:14 | [TypeParameter] T | tst.ts:314:14:314:14 | [Identifier] T | semmle.order | 1 | -| tst.ts:314:17:314:19 | [SimpleParameter] arg | tst.ts:314:22:316:29 | [InterfaceTypeExpr] { p ... void } | semmle.label | 0 | -| tst.ts:314:17:314:19 | [SimpleParameter] arg | tst.ts:314:22:316:29 | [InterfaceTypeExpr] { p ... void } | semmle.order | 0 | -| tst.ts:314:22:316:29 | [InterfaceTypeExpr] { p ... void } | tst.ts:315:5:315:30 | [FieldDeclaration] produce ... ) => T, | semmle.label | 1 | -| tst.ts:314:22:316:29 | [InterfaceTypeExpr] { p ... void } | tst.ts:315:5:315:30 | [FieldDeclaration] produce ... ) => T, | semmle.order | 1 | -| tst.ts:314:22:316:29 | [InterfaceTypeExpr] { p ... void } | tst.ts:316:5:316:27 | [FieldDeclaration] consume ... => void | semmle.label | 2 | -| tst.ts:314:22:316:29 | [InterfaceTypeExpr] { p ... void } | tst.ts:316:5:316:27 | [FieldDeclaration] consume ... => void | semmle.order | 2 | -| tst.ts:315:5:315:30 | [FieldDeclaration] produce ... ) => T, | tst.ts:315:5:315:11 | [Label] produce | semmle.label | 1 | -| tst.ts:315:5:315:30 | [FieldDeclaration] produce ... ) => T, | tst.ts:315:5:315:11 | [Label] produce | semmle.order | 1 | -| tst.ts:315:5:315:30 | [FieldDeclaration] produce ... ) => T, | tst.ts:315:14:315:29 | [FunctionTypeExpr] (n: string) => T | semmle.label | 2 | -| tst.ts:315:5:315:30 | [FieldDeclaration] produce ... ) => T, | tst.ts:315:14:315:29 | [FunctionTypeExpr] (n: string) => T | semmle.order | 2 | -| tst.ts:315:14:315:29 | [FunctionExpr] (n: string) => T | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | -| tst.ts:315:14:315:29 | [FunctionExpr] (n: string) => T | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | -| tst.ts:315:14:315:29 | [FunctionExpr] (n: string) => T | tst.ts:315:29:315:29 | [LocalTypeAccess] T | semmle.label | 4 | -| tst.ts:315:14:315:29 | [FunctionExpr] (n: string) => T | tst.ts:315:29:315:29 | [LocalTypeAccess] T | semmle.order | 4 | -| tst.ts:315:14:315:29 | [FunctionTypeExpr] (n: string) => T | tst.ts:315:14:315:29 | [FunctionExpr] (n: string) => T | semmle.label | 1 | -| tst.ts:315:14:315:29 | [FunctionTypeExpr] (n: string) => T | tst.ts:315:14:315:29 | [FunctionExpr] (n: string) => T | semmle.order | 1 | -| tst.ts:315:15:315:15 | [SimpleParameter] n | tst.ts:315:18:315:23 | [KeywordTypeExpr] string | semmle.label | 0 | -| tst.ts:315:15:315:15 | [SimpleParameter] n | tst.ts:315:18:315:23 | [KeywordTypeExpr] string | semmle.order | 0 | -| tst.ts:316:5:316:27 | [FieldDeclaration] consume ... => void | tst.ts:316:5:316:11 | [Label] consume | semmle.label | 1 | -| tst.ts:316:5:316:27 | [FieldDeclaration] consume ... => void | tst.ts:316:5:316:11 | [Label] consume | semmle.order | 1 | -| tst.ts:316:5:316:27 | [FieldDeclaration] consume ... => void | tst.ts:316:14:316:27 | [FunctionTypeExpr] (x: T) => void | semmle.label | 2 | -| tst.ts:316:5:316:27 | [FieldDeclaration] consume ... => void | tst.ts:316:14:316:27 | [FunctionTypeExpr] (x: T) => void | semmle.order | 2 | -| tst.ts:316:14:316:27 | [FunctionExpr] (x: T) => void | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | -| tst.ts:316:14:316:27 | [FunctionExpr] (x: T) => void | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | -| tst.ts:316:14:316:27 | [FunctionExpr] (x: T) => void | tst.ts:316:24:316:27 | [KeywordTypeExpr] void | semmle.label | 4 | -| tst.ts:316:14:316:27 | [FunctionExpr] (x: T) => void | tst.ts:316:24:316:27 | [KeywordTypeExpr] void | semmle.order | 4 | -| tst.ts:316:14:316:27 | [FunctionTypeExpr] (x: T) => void | tst.ts:316:14:316:27 | [FunctionExpr] (x: T) => void | semmle.label | 1 | -| tst.ts:316:14:316:27 | [FunctionTypeExpr] (x: T) => void | tst.ts:316:14:316:27 | [FunctionExpr] (x: T) => void | semmle.order | 1 | -| tst.ts:316:15:316:15 | [SimpleParameter] x | tst.ts:316:18:316:18 | [LocalTypeAccess] T | semmle.label | 0 | -| tst.ts:316:15:316:15 | [SimpleParameter] x | tst.ts:316:18:316:18 | [LocalTypeAccess] T | semmle.order | 0 | -| tst.ts:319:3:322:4 | [CallExpr] f({ ... () }) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | -| tst.ts:319:3:322:4 | [CallExpr] f({ ... () }) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | -| tst.ts:319:3:322:4 | [CallExpr] f({ ... () }) | tst.ts:319:3:319:3 | [VarRef] f | semmle.label | 0 | -| tst.ts:319:3:322:4 | [CallExpr] f({ ... () }) | tst.ts:319:3:319:3 | [VarRef] f | semmle.order | 0 | -| tst.ts:319:3:322:5 | [ExprStmt] f({ ... ) }); | tst.ts:319:3:322:4 | [CallExpr] f({ ... () }) | semmle.label | 1 | -| tst.ts:319:3:322:5 | [ExprStmt] f({ ... ) }); | tst.ts:319:3:322:4 | [CallExpr] f({ ... () }) | semmle.order | 1 | -| tst.ts:319:5:322:3 | [ObjectExpr] {produce: ...} | tst.ts:320:5:320:19 | [Property] produce: n => n | semmle.label | 1 | -| tst.ts:319:5:322:3 | [ObjectExpr] {produce: ...} | tst.ts:320:5:320:19 | [Property] produce: n => n | semmle.order | 1 | -| tst.ts:319:5:322:3 | [ObjectExpr] {produce: ...} | tst.ts:321:5:321:33 | [Property] consume ... rCase() | semmle.label | 2 | -| tst.ts:319:5:322:3 | [ObjectExpr] {produce: ...} | tst.ts:321:5:321:33 | [Property] consume ... rCase() | semmle.order | 2 | -| tst.ts:320:5:320:19 | [Property] produce: n => n | tst.ts:320:5:320:11 | [Label] produce | semmle.label | 1 | -| tst.ts:320:5:320:19 | [Property] produce: n => n | tst.ts:320:5:320:11 | [Label] produce | semmle.order | 1 | -| tst.ts:320:5:320:19 | [Property] produce: n => n | tst.ts:320:14:320:19 | [ArrowFunctionExpr] n => n | semmle.label | 2 | -| tst.ts:320:5:320:19 | [Property] produce: n => n | tst.ts:320:14:320:19 | [ArrowFunctionExpr] n => n | semmle.order | 2 | -| tst.ts:320:14:320:19 | [ArrowFunctionExpr] n => n | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | -| tst.ts:320:14:320:19 | [ArrowFunctionExpr] n => n | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | -| tst.ts:320:14:320:19 | [ArrowFunctionExpr] n => n | tst.ts:320:19:320:19 | [VarRef] n | semmle.label | 5 | -| tst.ts:320:14:320:19 | [ArrowFunctionExpr] n => n | tst.ts:320:19:320:19 | [VarRef] n | semmle.order | 5 | -| tst.ts:321:5:321:33 | [Property] consume ... rCase() | tst.ts:321:5:321:11 | [Label] consume | semmle.label | 1 | -| tst.ts:321:5:321:33 | [Property] consume ... rCase() | tst.ts:321:5:321:11 | [Label] consume | semmle.order | 1 | -| tst.ts:321:5:321:33 | [Property] consume ... rCase() | tst.ts:321:14:321:33 | [ArrowFunctionExpr] x => x.toLowerCase() | semmle.label | 2 | -| tst.ts:321:5:321:33 | [Property] consume ... rCase() | tst.ts:321:14:321:33 | [ArrowFunctionExpr] x => x.toLowerCase() | semmle.order | 2 | -| tst.ts:321:14:321:33 | [ArrowFunctionExpr] x => x.toLowerCase() | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | -| tst.ts:321:14:321:33 | [ArrowFunctionExpr] x => x.toLowerCase() | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | -| tst.ts:321:14:321:33 | [ArrowFunctionExpr] x => x.toLowerCase() | tst.ts:321:19:321:33 | [MethodCallExpr] x.toLowerCase() | semmle.label | 5 | -| tst.ts:321:14:321:33 | [ArrowFunctionExpr] x => x.toLowerCase() | tst.ts:321:19:321:33 | [MethodCallExpr] x.toLowerCase() | semmle.order | 5 | -| tst.ts:321:19:321:31 | [DotExpr] x.toLowerCase | tst.ts:321:19:321:19 | [VarRef] x | semmle.label | 1 | -| tst.ts:321:19:321:31 | [DotExpr] x.toLowerCase | tst.ts:321:19:321:19 | [VarRef] x | semmle.order | 1 | -| tst.ts:321:19:321:31 | [DotExpr] x.toLowerCase | tst.ts:321:21:321:31 | [Label] toLowerCase | semmle.label | 2 | -| tst.ts:321:19:321:31 | [DotExpr] x.toLowerCase | tst.ts:321:21:321:31 | [Label] toLowerCase | semmle.order | 2 | -| tst.ts:321:19:321:33 | [MethodCallExpr] x.toLowerCase() | tst.ts:321:19:321:31 | [DotExpr] x.toLowerCase | semmle.label | 0 | -| tst.ts:321:19:321:33 | [MethodCallExpr] x.toLowerCase() | tst.ts:321:19:321:31 | [DotExpr] x.toLowerCase | semmle.order | 0 | -| tst.ts:326:3:326:38 | [DeclStmt] const ErrorMap = ... | tst.ts:326:9:326:37 | [VariableDeclarator] ErrorMa ... Error> | semmle.label | 1 | -| tst.ts:326:3:326:38 | [DeclStmt] const ErrorMap = ... | tst.ts:326:9:326:37 | [VariableDeclarator] ErrorMa ... Error> | semmle.order | 1 | -| tst.ts:326:9:326:37 | [VariableDeclarator] ErrorMa ... Error> | tst.ts:326:9:326:16 | [VarDecl] ErrorMap | semmle.label | 1 | -| tst.ts:326:9:326:37 | [VariableDeclarator] ErrorMa ... Error> | tst.ts:326:9:326:16 | [VarDecl] ErrorMap | semmle.order | 1 | -| tst.ts:326:9:326:37 | [VariableDeclarator] ErrorMa ... Error> | tst.ts:326:20:326:37 | [???] Map | semmle.label | 2 | -| tst.ts:326:9:326:37 | [VariableDeclarator] ErrorMa ... Error> | tst.ts:326:20:326:37 | [???] Map | semmle.order | 2 | -| tst.ts:326:20:326:37 | [???] Map | tst.ts:326:20:326:22 | [VarRef] Map | semmle.label | 1 | -| tst.ts:326:20:326:37 | [???] Map | tst.ts:326:20:326:22 | [VarRef] Map | semmle.order | 1 | -| tst.ts:326:20:326:37 | [???] Map | tst.ts:326:24:326:29 | [KeywordTypeExpr] string | semmle.label | 2 | -| tst.ts:326:20:326:37 | [???] Map | tst.ts:326:24:326:29 | [KeywordTypeExpr] string | semmle.order | 2 | -| tst.ts:326:20:326:37 | [???] Map | tst.ts:326:32:326:36 | [LocalTypeAccess] Error | semmle.label | 3 | -| tst.ts:326:20:326:37 | [???] Map | tst.ts:326:32:326:36 | [LocalTypeAccess] Error | semmle.order | 3 | -| tst.ts:328:3:328:34 | [DeclStmt] const errorMap = ... | tst.ts:328:9:328:33 | [VariableDeclarator] errorMa ... orMap() | semmle.label | 1 | -| tst.ts:328:3:328:34 | [DeclStmt] const errorMap = ... | tst.ts:328:9:328:33 | [VariableDeclarator] errorMa ... orMap() | semmle.order | 1 | -| tst.ts:328:9:328:33 | [VariableDeclarator] errorMa ... orMap() | tst.ts:328:9:328:16 | [VarDecl] errorMap | semmle.label | 1 | -| tst.ts:328:9:328:33 | [VariableDeclarator] errorMa ... orMap() | tst.ts:328:9:328:16 | [VarDecl] errorMap | semmle.order | 1 | -| tst.ts:328:9:328:33 | [VariableDeclarator] errorMa ... orMap() | tst.ts:328:20:328:33 | [NewExpr] new ErrorMap() | semmle.label | 2 | -| tst.ts:328:9:328:33 | [VariableDeclarator] errorMa ... orMap() | tst.ts:328:20:328:33 | [NewExpr] new ErrorMap() | semmle.order | 2 | -| tst.ts:328:20:328:33 | [NewExpr] new ErrorMap() | tst.ts:328:24:328:31 | [VarRef] ErrorMap | semmle.label | 0 | -| tst.ts:328:20:328:33 | [NewExpr] new ErrorMap() | tst.ts:328:24:328:31 | [VarRef] ErrorMap | semmle.order | 0 | -| tst.ts:332:3:335:16 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | -| tst.ts:332:3:335:16 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | -| tst.ts:332:3:335:16 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | tst.ts:332:8:332:18 | [Identifier] FirstString | semmle.label | 1 | -| tst.ts:332:3:335:16 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | tst.ts:332:8:332:18 | [Identifier] FirstString | semmle.order | 1 | -| tst.ts:332:3:335:16 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | semmle.label | 3 | -| tst.ts:332:3:335:16 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | semmle.order | 3 | -| tst.ts:332:20:332:20 | [TypeParameter] T | tst.ts:332:20:332:20 | [Identifier] T | semmle.label | 1 | -| tst.ts:332:20:332:20 | [TypeParameter] T | tst.ts:332:20:332:20 | [Identifier] T | semmle.order | 1 | -| tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | tst.ts:333:5:333:5 | [LocalTypeAccess] T | semmle.label | 1 | -| tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | tst.ts:333:5:333:5 | [LocalTypeAccess] T | semmle.order | 1 | -| tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | tst.ts:333:15:333:52 | [TupleTypeExpr] [infer ... nown[]] | semmle.label | 2 | -| tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | tst.ts:333:15:333:52 | [TupleTypeExpr] [infer ... nown[]] | semmle.order | 2 | -| tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | tst.ts:334:11:334:11 | [LocalTypeAccess] S | semmle.label | 3 | -| tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | tst.ts:334:11:334:11 | [LocalTypeAccess] S | semmle.order | 3 | -| tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | tst.ts:335:11:335:15 | [KeywordTypeExpr] never | semmle.label | 4 | -| tst.ts:333:5:335:15 | [ConditionalTypeExpr] T exten ... : never | tst.ts:335:11:335:15 | [KeywordTypeExpr] never | semmle.order | 4 | -| tst.ts:333:15:333:52 | [TupleTypeExpr] [infer ... nown[]] | tst.ts:333:16:333:37 | [InferTypeExpr] infer S ... string | semmle.label | 1 | -| tst.ts:333:15:333:52 | [TupleTypeExpr] [infer ... nown[]] | tst.ts:333:16:333:37 | [InferTypeExpr] infer S ... string | semmle.order | 1 | -| tst.ts:333:15:333:52 | [TupleTypeExpr] [infer ... nown[]] | tst.ts:333:40:333:51 | [RestTypeExpr] ...unknown[] | semmle.label | 2 | -| tst.ts:333:15:333:52 | [TupleTypeExpr] [infer ... nown[]] | tst.ts:333:40:333:51 | [RestTypeExpr] ...unknown[] | semmle.order | 2 | -| tst.ts:333:16:333:37 | [InferTypeExpr] infer S ... string | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | -| tst.ts:333:16:333:37 | [InferTypeExpr] infer S ... string | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | -| tst.ts:333:22:333:37 | [TypeParameter] S extends string | tst.ts:333:22:333:22 | [Identifier] S | semmle.label | 1 | -| tst.ts:333:22:333:37 | [TypeParameter] S extends string | tst.ts:333:22:333:22 | [Identifier] S | semmle.order | 1 | -| tst.ts:333:22:333:37 | [TypeParameter] S extends string | tst.ts:333:32:333:37 | [KeywordTypeExpr] string | semmle.label | 2 | -| tst.ts:333:22:333:37 | [TypeParameter] S extends string | tst.ts:333:32:333:37 | [KeywordTypeExpr] string | semmle.order | 2 | -| tst.ts:333:40:333:51 | [RestTypeExpr] ...unknown[] | tst.ts:333:43:333:51 | [ArrayTypeExpr] unknown[] | semmle.label | 1 | -| tst.ts:333:40:333:51 | [RestTypeExpr] ...unknown[] | tst.ts:333:43:333:51 | [ArrayTypeExpr] unknown[] | semmle.order | 1 | -| tst.ts:333:43:333:51 | [ArrayTypeExpr] unknown[] | tst.ts:333:43:333:49 | [KeywordTypeExpr] unknown | semmle.label | 1 | -| tst.ts:333:43:333:51 | [ArrayTypeExpr] unknown[] | tst.ts:333:43:333:49 | [KeywordTypeExpr] unknown | semmle.order | 1 | -| tst.ts:337:3:337:53 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | tst.ts:337:8:337:8 | [Identifier] F | semmle.label | 1 | -| tst.ts:337:3:337:53 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | tst.ts:337:8:337:8 | [Identifier] F | semmle.order | 1 | -| tst.ts:337:3:337:53 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | tst.ts:337:12:337:52 | [GenericTypeExpr] FirstSt ... olean]> | semmle.label | 2 | -| tst.ts:337:3:337:53 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | tst.ts:337:12:337:52 | [GenericTypeExpr] FirstSt ... olean]> | semmle.order | 2 | -| tst.ts:337:12:337:52 | [GenericTypeExpr] FirstSt ... olean]> | tst.ts:337:12:337:22 | [LocalTypeAccess] FirstString | semmle.label | 1 | -| tst.ts:337:12:337:52 | [GenericTypeExpr] FirstSt ... olean]> | tst.ts:337:12:337:22 | [LocalTypeAccess] FirstString | semmle.order | 1 | -| tst.ts:337:12:337:52 | [GenericTypeExpr] FirstSt ... olean]> | tst.ts:337:24:337:51 | [TupleTypeExpr] ['a' \| ... oolean] | semmle.label | 2 | -| tst.ts:337:12:337:52 | [GenericTypeExpr] FirstSt ... olean]> | tst.ts:337:24:337:51 | [TupleTypeExpr] ['a' \| ... oolean] | semmle.order | 2 | -| tst.ts:337:24:337:51 | [TupleTypeExpr] ['a' \| ... oolean] | tst.ts:337:25:337:33 | [UnionTypeExpr] 'a' \| 'b' | semmle.label | 1 | -| tst.ts:337:24:337:51 | [TupleTypeExpr] ['a' \| ... oolean] | tst.ts:337:25:337:33 | [UnionTypeExpr] 'a' \| 'b' | semmle.order | 1 | -| tst.ts:337:24:337:51 | [TupleTypeExpr] ['a' \| ... oolean] | tst.ts:337:36:337:41 | [KeywordTypeExpr] number | semmle.label | 2 | -| tst.ts:337:24:337:51 | [TupleTypeExpr] ['a' \| ... oolean] | tst.ts:337:36:337:41 | [KeywordTypeExpr] number | semmle.order | 2 | -| tst.ts:337:24:337:51 | [TupleTypeExpr] ['a' \| ... oolean] | tst.ts:337:44:337:50 | [KeywordTypeExpr] boolean | semmle.label | 3 | -| tst.ts:337:24:337:51 | [TupleTypeExpr] ['a' \| ... oolean] | tst.ts:337:44:337:50 | [KeywordTypeExpr] boolean | semmle.order | 3 | -| tst.ts:337:25:337:33 | [UnionTypeExpr] 'a' \| 'b' | tst.ts:337:25:337:27 | [LiteralTypeExpr] 'a' | semmle.label | 1 | -| tst.ts:337:25:337:33 | [UnionTypeExpr] 'a' \| 'b' | tst.ts:337:25:337:27 | [LiteralTypeExpr] 'a' | semmle.order | 1 | -| tst.ts:337:25:337:33 | [UnionTypeExpr] 'a' \| 'b' | tst.ts:337:31:337:33 | [LiteralTypeExpr] 'b' | semmle.label | 2 | -| tst.ts:337:25:337:33 | [UnionTypeExpr] 'a' \| 'b' | tst.ts:337:31:337:33 | [LiteralTypeExpr] 'b' | semmle.order | 2 | -| tst.ts:339:3:339:19 | [DeclStmt] const a = ... | tst.ts:339:9:339:18 | [VariableDeclarator] a: F = 'a' | semmle.label | 1 | -| tst.ts:339:3:339:19 | [DeclStmt] const a = ... | tst.ts:339:9:339:18 | [VariableDeclarator] a: F = 'a' | semmle.order | 1 | -| tst.ts:339:9:339:18 | [VariableDeclarator] a: F = 'a' | tst.ts:339:9:339:9 | [VarDecl] a | semmle.label | 1 | -| tst.ts:339:9:339:18 | [VariableDeclarator] a: F = 'a' | tst.ts:339:9:339:9 | [VarDecl] a | semmle.order | 1 | -| tst.ts:339:9:339:18 | [VariableDeclarator] a: F = 'a' | tst.ts:339:12:339:12 | [LocalTypeAccess] F | semmle.label | 2 | -| tst.ts:339:9:339:18 | [VariableDeclarator] a: F = 'a' | tst.ts:339:12:339:12 | [LocalTypeAccess] F | semmle.order | 2 | -| tst.ts:339:9:339:18 | [VariableDeclarator] a: F = 'a' | tst.ts:339:16:339:18 | [Literal] 'a' | semmle.label | 3 | -| tst.ts:339:9:339:18 | [VariableDeclarator] a: F = 'a' | tst.ts:339:16:339:18 | [Literal] 'a' | semmle.order | 3 | -| tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | -| tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | -| tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | tst.ts:343:13:343:17 | [Identifier] State | semmle.label | 1 | -| tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | tst.ts:343:13:343:17 | [Identifier] State | semmle.order | 1 | -| tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | tst.ts:344:5:344:17 | [FieldDeclaration] get: () => T; | semmle.label | 3 | -| tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | tst.ts:344:5:344:17 | [FieldDeclaration] get: () => T; | semmle.order | 3 | -| tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | tst.ts:345:5:345:28 | [FieldDeclaration] set: (v ... > void; | semmle.label | 4 | -| tst.ts:343:3:346:3 | [InterfaceDeclaration,TypeDefinition] interfa ... id; } | tst.ts:345:5:345:28 | [FieldDeclaration] set: (v ... > void; | semmle.order | 4 | -| tst.ts:343:19:343:26 | [TypeParameter] in out T | tst.ts:343:26:343:26 | [Identifier] T | semmle.label | 1 | -| tst.ts:343:19:343:26 | [TypeParameter] in out T | tst.ts:343:26:343:26 | [Identifier] T | semmle.order | 1 | -| tst.ts:344:5:344:17 | [FieldDeclaration] get: () => T; | tst.ts:344:5:344:7 | [Label] get | semmle.label | 1 | -| tst.ts:344:5:344:17 | [FieldDeclaration] get: () => T; | tst.ts:344:5:344:7 | [Label] get | semmle.order | 1 | -| tst.ts:344:5:344:17 | [FieldDeclaration] get: () => T; | tst.ts:344:10:344:16 | [FunctionTypeExpr] () => T | semmle.label | 2 | -| tst.ts:344:5:344:17 | [FieldDeclaration] get: () => T; | tst.ts:344:10:344:16 | [FunctionTypeExpr] () => T | semmle.order | 2 | -| tst.ts:344:10:344:16 | [FunctionExpr] () => T | tst.ts:344:16:344:16 | [LocalTypeAccess] T | semmle.label | 4 | -| tst.ts:344:10:344:16 | [FunctionExpr] () => T | tst.ts:344:16:344:16 | [LocalTypeAccess] T | semmle.order | 4 | -| tst.ts:344:10:344:16 | [FunctionTypeExpr] () => T | tst.ts:344:10:344:16 | [FunctionExpr] () => T | semmle.label | 1 | -| tst.ts:344:10:344:16 | [FunctionTypeExpr] () => T | tst.ts:344:10:344:16 | [FunctionExpr] () => T | semmle.order | 1 | -| tst.ts:345:5:345:28 | [FieldDeclaration] set: (v ... > void; | tst.ts:345:5:345:7 | [Label] set | semmle.label | 1 | -| tst.ts:345:5:345:28 | [FieldDeclaration] set: (v ... > void; | tst.ts:345:5:345:7 | [Label] set | semmle.order | 1 | -| tst.ts:345:5:345:28 | [FieldDeclaration] set: (v ... > void; | tst.ts:345:10:345:27 | [FunctionTypeExpr] (value: T) => void | semmle.label | 2 | -| tst.ts:345:5:345:28 | [FieldDeclaration] set: (v ... > void; | tst.ts:345:10:345:27 | [FunctionTypeExpr] (value: T) => void | semmle.order | 2 | -| tst.ts:345:10:345:27 | [FunctionExpr] (value: T) => void | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | -| tst.ts:345:10:345:27 | [FunctionExpr] (value: T) => void | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | -| tst.ts:345:10:345:27 | [FunctionExpr] (value: T) => void | tst.ts:345:24:345:27 | [KeywordTypeExpr] void | semmle.label | 4 | -| tst.ts:345:10:345:27 | [FunctionExpr] (value: T) => void | tst.ts:345:24:345:27 | [KeywordTypeExpr] void | semmle.order | 4 | -| tst.ts:345:10:345:27 | [FunctionTypeExpr] (value: T) => void | tst.ts:345:10:345:27 | [FunctionExpr] (value: T) => void | semmle.label | 1 | -| tst.ts:345:10:345:27 | [FunctionTypeExpr] (value: T) => void | tst.ts:345:10:345:27 | [FunctionExpr] (value: T) => void | semmle.order | 1 | -| tst.ts:345:11:345:15 | [SimpleParameter] value | tst.ts:345:18:345:18 | [LocalTypeAccess] T | semmle.label | 0 | -| tst.ts:345:11:345:15 | [SimpleParameter] value | tst.ts:345:18:345:18 | [LocalTypeAccess] T | semmle.order | 0 | -| tst.ts:348:3:351:3 | [DeclStmt] const state = ... | tst.ts:348:9:351:3 | [VariableDeclarator] state: ... { } } | semmle.label | 1 | -| tst.ts:348:3:351:3 | [DeclStmt] const state = ... | tst.ts:348:9:351:3 | [VariableDeclarator] state: ... { } } | semmle.order | 1 | -| tst.ts:348:9:351:3 | [VariableDeclarator] state: ... { } } | tst.ts:348:9:348:13 | [VarDecl] state | semmle.label | 1 | -| tst.ts:348:9:351:3 | [VariableDeclarator] state: ... { } } | tst.ts:348:9:348:13 | [VarDecl] state | semmle.order | 1 | -| tst.ts:348:9:351:3 | [VariableDeclarator] state: ... { } } | tst.ts:348:16:348:28 | [GenericTypeExpr] State | semmle.label | 2 | -| tst.ts:348:9:351:3 | [VariableDeclarator] state: ... { } } | tst.ts:348:16:348:28 | [GenericTypeExpr] State | semmle.order | 2 | -| tst.ts:348:9:351:3 | [VariableDeclarator] state: ... { } } | tst.ts:348:32:351:3 | [ObjectExpr] {get: ...} | semmle.label | 3 | -| tst.ts:348:9:351:3 | [VariableDeclarator] state: ... { } } | tst.ts:348:32:351:3 | [ObjectExpr] {get: ...} | semmle.order | 3 | -| tst.ts:348:16:348:28 | [GenericTypeExpr] State | tst.ts:348:16:348:20 | [LocalTypeAccess] State | semmle.label | 1 | -| tst.ts:348:16:348:28 | [GenericTypeExpr] State | tst.ts:348:16:348:20 | [LocalTypeAccess] State | semmle.order | 1 | -| tst.ts:348:16:348:28 | [GenericTypeExpr] State | tst.ts:348:22:348:27 | [KeywordTypeExpr] number | semmle.label | 2 | -| tst.ts:348:16:348:28 | [GenericTypeExpr] State | tst.ts:348:22:348:27 | [KeywordTypeExpr] number | semmle.order | 2 | -| tst.ts:348:32:351:3 | [ObjectExpr] {get: ...} | tst.ts:349:5:349:17 | [Property] get: () => 42 | semmle.label | 1 | -| tst.ts:348:32:351:3 | [ObjectExpr] {get: ...} | tst.ts:349:5:349:17 | [Property] get: () => 42 | semmle.order | 1 | -| tst.ts:348:32:351:3 | [ObjectExpr] {get: ...} | tst.ts:350:5:350:23 | [Property] set: (value) => { } | semmle.label | 2 | -| tst.ts:348:32:351:3 | [ObjectExpr] {get: ...} | tst.ts:350:5:350:23 | [Property] set: (value) => { } | semmle.order | 2 | -| tst.ts:349:5:349:17 | [Property] get: () => 42 | tst.ts:349:5:349:7 | [Label] get | semmle.label | 1 | -| tst.ts:349:5:349:17 | [Property] get: () => 42 | tst.ts:349:5:349:7 | [Label] get | semmle.order | 1 | -| tst.ts:349:5:349:17 | [Property] get: () => 42 | tst.ts:349:10:349:17 | [ArrowFunctionExpr] () => 42 | semmle.label | 2 | -| tst.ts:349:5:349:17 | [Property] get: () => 42 | tst.ts:349:10:349:17 | [ArrowFunctionExpr] () => 42 | semmle.order | 2 | -| tst.ts:349:10:349:17 | [ArrowFunctionExpr] () => 42 | tst.ts:349:16:349:17 | [Literal] 42 | semmle.label | 5 | -| tst.ts:349:10:349:17 | [ArrowFunctionExpr] () => 42 | tst.ts:349:16:349:17 | [Literal] 42 | semmle.order | 5 | -| tst.ts:350:5:350:23 | [Property] set: (value) => { } | tst.ts:350:5:350:7 | [Label] set | semmle.label | 1 | -| tst.ts:350:5:350:23 | [Property] set: (value) => { } | tst.ts:350:5:350:7 | [Label] set | semmle.order | 1 | -| tst.ts:350:5:350:23 | [Property] set: (value) => { } | tst.ts:350:10:350:23 | [ArrowFunctionExpr] (value) => { } | semmle.label | 2 | -| tst.ts:350:5:350:23 | [Property] set: (value) => { } | tst.ts:350:10:350:23 | [ArrowFunctionExpr] (value) => { } | semmle.order | 2 | -| tst.ts:350:10:350:23 | [ArrowFunctionExpr] (value) => { } | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | -| tst.ts:350:10:350:23 | [ArrowFunctionExpr] (value) => { } | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | -| tst.ts:350:10:350:23 | [ArrowFunctionExpr] (value) => { } | tst.ts:350:21:350:23 | [BlockStmt] { } | semmle.label | 5 | -| tst.ts:350:10:350:23 | [ArrowFunctionExpr] (value) => { } | tst.ts:350:21:350:23 | [BlockStmt] { } | semmle.order | 5 | -| tst.ts:353:3:353:31 | [DeclStmt] const fortyTwo = ... | tst.ts:353:9:353:30 | [VariableDeclarator] fortyTw ... e.get() | semmle.label | 1 | -| tst.ts:353:3:353:31 | [DeclStmt] const fortyTwo = ... | tst.ts:353:9:353:30 | [VariableDeclarator] fortyTw ... e.get() | semmle.order | 1 | -| tst.ts:353:9:353:30 | [VariableDeclarator] fortyTw ... e.get() | tst.ts:353:9:353:16 | [VarDecl] fortyTwo | semmle.label | 1 | -| tst.ts:353:9:353:30 | [VariableDeclarator] fortyTw ... e.get() | tst.ts:353:9:353:16 | [VarDecl] fortyTwo | semmle.order | 1 | -| tst.ts:353:9:353:30 | [VariableDeclarator] fortyTw ... e.get() | tst.ts:353:20:353:30 | [MethodCallExpr] state.get() | semmle.label | 2 | -| tst.ts:353:9:353:30 | [VariableDeclarator] fortyTw ... e.get() | tst.ts:353:20:353:30 | [MethodCallExpr] state.get() | semmle.order | 2 | -| tst.ts:353:20:353:28 | [DotExpr] state.get | tst.ts:353:20:353:24 | [VarRef] state | semmle.label | 1 | -| tst.ts:353:20:353:28 | [DotExpr] state.get | tst.ts:353:20:353:24 | [VarRef] state | semmle.order | 1 | -| tst.ts:353:20:353:28 | [DotExpr] state.get | tst.ts:353:26:353:28 | [Label] get | semmle.label | 2 | -| tst.ts:353:20:353:28 | [DotExpr] state.get | tst.ts:353:26:353:28 | [Label] get | semmle.order | 2 | -| tst.ts:353:20:353:30 | [MethodCallExpr] state.get() | tst.ts:353:20:353:28 | [DotExpr] state.get | semmle.label | 0 | -| tst.ts:353:20:353:30 | [MethodCallExpr] state.get() | tst.ts:353:20:353:28 | [DotExpr] state.get | semmle.order | 0 | +| tst.ts:298:1:298:21 | [DeclStmt] const key = ... | tst.ts:298:7:298:20 | [VariableDeclarator] key = Symbol() | semmle.label | 1 | +| tst.ts:298:1:298:21 | [DeclStmt] const key = ... | tst.ts:298:7:298:20 | [VariableDeclarator] key = Symbol() | semmle.order | 1 | +| tst.ts:298:7:298:20 | [VariableDeclarator] key = Symbol() | tst.ts:298:7:298:9 | [VarDecl] key | semmle.label | 1 | +| tst.ts:298:7:298:20 | [VariableDeclarator] key = Symbol() | tst.ts:298:7:298:9 | [VarDecl] key | semmle.order | 1 | +| tst.ts:298:7:298:20 | [VariableDeclarator] key = Symbol() | tst.ts:298:13:298:20 | [CallExpr] Symbol() | semmle.label | 2 | +| tst.ts:298:7:298:20 | [VariableDeclarator] key = Symbol() | tst.ts:298:13:298:20 | [CallExpr] Symbol() | semmle.order | 2 | +| tst.ts:298:13:298:20 | [CallExpr] Symbol() | tst.ts:298:13:298:18 | [VarRef] Symbol | semmle.label | 0 | +| tst.ts:298:13:298:20 | [CallExpr] Symbol() | tst.ts:298:13:298:18 | [VarRef] Symbol | semmle.order | 0 | +| tst.ts:300:1:300:58 | [DeclStmt] const numberOrString = ... | tst.ts:300:7:300:57 | [VariableDeclarator] numberO ... "hello" | semmle.label | 1 | +| tst.ts:300:1:300:58 | [DeclStmt] const numberOrString = ... | tst.ts:300:7:300:57 | [VariableDeclarator] numberO ... "hello" | semmle.order | 1 | +| tst.ts:300:7:300:57 | [VariableDeclarator] numberO ... "hello" | tst.ts:300:7:300:20 | [VarDecl] numberOrString | semmle.label | 1 | +| tst.ts:300:7:300:57 | [VariableDeclarator] numberO ... "hello" | tst.ts:300:7:300:20 | [VarDecl] numberOrString | semmle.order | 1 | +| tst.ts:300:7:300:57 | [VariableDeclarator] numberO ... "hello" | tst.ts:300:24:300:57 | [ConditionalExpr] Math.ra ... "hello" | semmle.label | 2 | +| tst.ts:300:7:300:57 | [VariableDeclarator] numberO ... "hello" | tst.ts:300:24:300:57 | [ConditionalExpr] Math.ra ... "hello" | semmle.order | 2 | +| tst.ts:300:24:300:34 | [DotExpr] Math.random | tst.ts:300:24:300:27 | [VarRef] Math | semmle.label | 1 | +| tst.ts:300:24:300:34 | [DotExpr] Math.random | tst.ts:300:24:300:27 | [VarRef] Math | semmle.order | 1 | +| tst.ts:300:24:300:34 | [DotExpr] Math.random | tst.ts:300:29:300:34 | [Label] random | semmle.label | 2 | +| tst.ts:300:24:300:34 | [DotExpr] Math.random | tst.ts:300:29:300:34 | [Label] random | semmle.order | 2 | +| tst.ts:300:24:300:36 | [MethodCallExpr] Math.random() | tst.ts:300:24:300:34 | [DotExpr] Math.random | semmle.label | 0 | +| tst.ts:300:24:300:36 | [MethodCallExpr] Math.random() | tst.ts:300:24:300:34 | [DotExpr] Math.random | semmle.order | 0 | +| tst.ts:300:24:300:42 | [BinaryExpr] Math.random() < 0.5 | tst.ts:300:24:300:36 | [MethodCallExpr] Math.random() | semmle.label | 1 | +| tst.ts:300:24:300:42 | [BinaryExpr] Math.random() < 0.5 | tst.ts:300:24:300:36 | [MethodCallExpr] Math.random() | semmle.order | 1 | +| tst.ts:300:24:300:42 | [BinaryExpr] Math.random() < 0.5 | tst.ts:300:40:300:42 | [Literal] 0.5 | semmle.label | 2 | +| tst.ts:300:24:300:42 | [BinaryExpr] Math.random() < 0.5 | tst.ts:300:40:300:42 | [Literal] 0.5 | semmle.order | 2 | +| tst.ts:300:24:300:57 | [ConditionalExpr] Math.ra ... "hello" | tst.ts:300:24:300:42 | [BinaryExpr] Math.random() < 0.5 | semmle.label | 1 | +| tst.ts:300:24:300:57 | [ConditionalExpr] Math.ra ... "hello" | tst.ts:300:24:300:42 | [BinaryExpr] Math.random() < 0.5 | semmle.order | 1 | +| tst.ts:300:24:300:57 | [ConditionalExpr] Math.ra ... "hello" | tst.ts:300:46:300:47 | [Literal] 42 | semmle.label | 2 | +| tst.ts:300:24:300:57 | [ConditionalExpr] Math.ra ... "hello" | tst.ts:300:46:300:47 | [Literal] 42 | semmle.order | 2 | +| tst.ts:300:24:300:57 | [ConditionalExpr] Math.ra ... "hello" | tst.ts:300:51:300:57 | [Literal] "hello" | semmle.label | 3 | +| tst.ts:300:24:300:57 | [ConditionalExpr] Math.ra ... "hello" | tst.ts:300:51:300:57 | [Literal] "hello" | semmle.order | 3 | +| tst.ts:302:1:304:2 | [DeclStmt] let obj = ... | tst.ts:302:5:304:1 | [VariableDeclarator] obj = { ... ring, } | semmle.label | 1 | +| tst.ts:302:1:304:2 | [DeclStmt] let obj = ... | tst.ts:302:5:304:1 | [VariableDeclarator] obj = { ... ring, } | semmle.order | 1 | +| tst.ts:302:5:304:1 | [VariableDeclarator] obj = { ... ring, } | tst.ts:302:5:302:7 | [VarDecl] obj | semmle.label | 1 | +| tst.ts:302:5:304:1 | [VariableDeclarator] obj = { ... ring, } | tst.ts:302:5:302:7 | [VarDecl] obj | semmle.order | 1 | +| tst.ts:302:5:304:1 | [VariableDeclarator] obj = { ... ring, } | tst.ts:302:11:304:1 | [ObjectExpr] { [ke ... ring, } | semmle.label | 2 | +| tst.ts:302:5:304:1 | [VariableDeclarator] obj = { ... ring, } | tst.ts:302:11:304:1 | [ObjectExpr] { [ke ... ring, } | semmle.order | 2 | +| tst.ts:302:11:304:1 | [ObjectExpr] { [ke ... ring, } | tst.ts:303:3:303:23 | [Property] [key]: ... rString | semmle.label | 1 | +| tst.ts:302:11:304:1 | [ObjectExpr] { [ke ... ring, } | tst.ts:303:3:303:23 | [Property] [key]: ... rString | semmle.order | 1 | +| tst.ts:303:3:303:23 | [Property] [key]: ... rString | tst.ts:303:4:303:6 | [VarRef] key | semmle.label | 1 | +| tst.ts:303:3:303:23 | [Property] [key]: ... rString | tst.ts:303:4:303:6 | [VarRef] key | semmle.order | 1 | +| tst.ts:303:3:303:23 | [Property] [key]: ... rString | tst.ts:303:10:303:23 | [VarRef] numberOrString | semmle.label | 2 | +| tst.ts:303:3:303:23 | [Property] [key]: ... rString | tst.ts:303:10:303:23 | [VarRef] numberOrString | semmle.order | 2 | +| tst.ts:306:1:309:1 | [IfStmt] if (typ ... se(); } | tst.ts:306:5:306:32 | [BinaryExpr] typeof ... string" | semmle.label | 1 | +| tst.ts:306:1:309:1 | [IfStmt] if (typ ... se(); } | tst.ts:306:5:306:32 | [BinaryExpr] typeof ... string" | semmle.order | 1 | +| tst.ts:306:1:309:1 | [IfStmt] if (typ ... se(); } | tst.ts:306:35:309:1 | [BlockStmt] { let ... se(); } | semmle.label | 2 | +| tst.ts:306:1:309:1 | [IfStmt] if (typ ... se(); } | tst.ts:306:35:309:1 | [BlockStmt] { let ... se(); } | semmle.order | 2 | +| tst.ts:306:5:306:19 | [UnaryExpr] typeof obj[key] | tst.ts:306:12:306:19 | [IndexExpr] obj[key] | semmle.label | 1 | +| tst.ts:306:5:306:19 | [UnaryExpr] typeof obj[key] | tst.ts:306:12:306:19 | [IndexExpr] obj[key] | semmle.order | 1 | +| tst.ts:306:5:306:32 | [BinaryExpr] typeof ... string" | tst.ts:306:5:306:19 | [UnaryExpr] typeof obj[key] | semmle.label | 1 | +| tst.ts:306:5:306:32 | [BinaryExpr] typeof ... string" | tst.ts:306:5:306:19 | [UnaryExpr] typeof obj[key] | semmle.order | 1 | +| tst.ts:306:5:306:32 | [BinaryExpr] typeof ... string" | tst.ts:306:25:306:32 | [Literal] "string" | semmle.label | 2 | +| tst.ts:306:5:306:32 | [BinaryExpr] typeof ... string" | tst.ts:306:25:306:32 | [Literal] "string" | semmle.order | 2 | +| tst.ts:306:12:306:19 | [IndexExpr] obj[key] | tst.ts:306:12:306:14 | [VarRef] obj | semmle.label | 1 | +| tst.ts:306:12:306:19 | [IndexExpr] obj[key] | tst.ts:306:12:306:14 | [VarRef] obj | semmle.order | 1 | +| tst.ts:306:12:306:19 | [IndexExpr] obj[key] | tst.ts:306:16:306:18 | [VarRef] key | semmle.label | 2 | +| tst.ts:306:12:306:19 | [IndexExpr] obj[key] | tst.ts:306:16:306:18 | [VarRef] key | semmle.order | 2 | +| tst.ts:306:35:309:1 | [BlockStmt] { let ... se(); } | tst.ts:307:3:307:21 | [DeclStmt] let str = ... | semmle.label | 1 | +| tst.ts:306:35:309:1 | [BlockStmt] { let ... se(); } | tst.ts:307:3:307:21 | [DeclStmt] let str = ... | semmle.order | 1 | +| tst.ts:306:35:309:1 | [BlockStmt] { let ... se(); } | tst.ts:308:3:308:20 | [ExprStmt] str.toUpperCase(); | semmle.label | 2 | +| tst.ts:306:35:309:1 | [BlockStmt] { let ... se(); } | tst.ts:308:3:308:20 | [ExprStmt] str.toUpperCase(); | semmle.order | 2 | +| tst.ts:307:3:307:21 | [DeclStmt] let str = ... | tst.ts:307:7:307:20 | [VariableDeclarator] str = obj[key] | semmle.label | 1 | +| tst.ts:307:3:307:21 | [DeclStmt] let str = ... | tst.ts:307:7:307:20 | [VariableDeclarator] str = obj[key] | semmle.order | 1 | +| tst.ts:307:7:307:20 | [VariableDeclarator] str = obj[key] | tst.ts:307:7:307:9 | [VarDecl] str | semmle.label | 1 | +| tst.ts:307:7:307:20 | [VariableDeclarator] str = obj[key] | tst.ts:307:7:307:9 | [VarDecl] str | semmle.order | 1 | +| tst.ts:307:7:307:20 | [VariableDeclarator] str = obj[key] | tst.ts:307:13:307:20 | [IndexExpr] obj[key] | semmle.label | 2 | +| tst.ts:307:7:307:20 | [VariableDeclarator] str = obj[key] | tst.ts:307:13:307:20 | [IndexExpr] obj[key] | semmle.order | 2 | +| tst.ts:307:13:307:20 | [IndexExpr] obj[key] | tst.ts:307:13:307:15 | [VarRef] obj | semmle.label | 1 | +| tst.ts:307:13:307:20 | [IndexExpr] obj[key] | tst.ts:307:13:307:15 | [VarRef] obj | semmle.order | 1 | +| tst.ts:307:13:307:20 | [IndexExpr] obj[key] | tst.ts:307:17:307:19 | [VarRef] key | semmle.label | 2 | +| tst.ts:307:13:307:20 | [IndexExpr] obj[key] | tst.ts:307:17:307:19 | [VarRef] key | semmle.order | 2 | +| tst.ts:308:3:308:17 | [DotExpr] str.toUpperCase | tst.ts:308:3:308:5 | [VarRef] str | semmle.label | 1 | +| tst.ts:308:3:308:17 | [DotExpr] str.toUpperCase | tst.ts:308:3:308:5 | [VarRef] str | semmle.order | 1 | +| tst.ts:308:3:308:17 | [DotExpr] str.toUpperCase | tst.ts:308:7:308:17 | [Label] toUpperCase | semmle.label | 2 | +| tst.ts:308:3:308:17 | [DotExpr] str.toUpperCase | tst.ts:308:7:308:17 | [Label] toUpperCase | semmle.order | 2 | +| tst.ts:308:3:308:19 | [MethodCallExpr] str.toUpperCase() | tst.ts:308:3:308:17 | [DotExpr] str.toUpperCase | semmle.label | 0 | +| tst.ts:308:3:308:19 | [MethodCallExpr] str.toUpperCase() | tst.ts:308:3:308:17 | [DotExpr] str.toUpperCase | semmle.order | 0 | +| tst.ts:308:3:308:20 | [ExprStmt] str.toUpperCase(); | tst.ts:308:3:308:19 | [MethodCallExpr] str.toUpperCase() | semmle.label | 1 | +| tst.ts:308:3:308:20 | [ExprStmt] str.toUpperCase(); | tst.ts:308:3:308:19 | [MethodCallExpr] str.toUpperCase() | semmle.order | 1 | +| tst.ts:313:1:316:10 | [FunctionDeclStmt] functio ... void {} | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| tst.ts:313:1:316:10 | [FunctionDeclStmt] functio ... void {} | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| tst.ts:313:1:316:10 | [FunctionDeclStmt] functio ... void {} | file://:0:0:0:0 | (TypeParameters) | semmle.label | 2 | +| tst.ts:313:1:316:10 | [FunctionDeclStmt] functio ... void {} | file://:0:0:0:0 | (TypeParameters) | semmle.order | 2 | +| tst.ts:313:1:316:10 | [FunctionDeclStmt] functio ... void {} | tst.ts:313:10:313:10 | [VarDecl] f | semmle.label | 0 | +| tst.ts:313:1:316:10 | [FunctionDeclStmt] functio ... void {} | tst.ts:313:10:313:10 | [VarDecl] f | semmle.order | 0 | +| tst.ts:313:1:316:10 | [FunctionDeclStmt] functio ... void {} | tst.ts:316:4:316:7 | [KeywordTypeExpr] void | semmle.label | 4 | +| tst.ts:313:1:316:10 | [FunctionDeclStmt] functio ... void {} | tst.ts:316:4:316:7 | [KeywordTypeExpr] void | semmle.order | 4 | +| tst.ts:313:1:316:10 | [FunctionDeclStmt] functio ... void {} | tst.ts:316:9:316:10 | [BlockStmt] {} | semmle.label | 5 | +| tst.ts:313:1:316:10 | [FunctionDeclStmt] functio ... void {} | tst.ts:316:9:316:10 | [BlockStmt] {} | semmle.order | 5 | +| tst.ts:313:12:313:12 | [TypeParameter] T | tst.ts:313:12:313:12 | [Identifier] T | semmle.label | 1 | +| tst.ts:313:12:313:12 | [TypeParameter] T | tst.ts:313:12:313:12 | [Identifier] T | semmle.order | 1 | +| tst.ts:313:15:313:17 | [SimpleParameter] arg | tst.ts:313:20:315:27 | [InterfaceTypeExpr] { pro ... void } | semmle.label | 0 | +| tst.ts:313:15:313:17 | [SimpleParameter] arg | tst.ts:313:20:315:27 | [InterfaceTypeExpr] { pro ... void } | semmle.order | 0 | +| tst.ts:313:20:315:27 | [InterfaceTypeExpr] { pro ... void } | tst.ts:314:3:314:28 | [FieldDeclaration] produce ... ) => T, | semmle.label | 1 | +| tst.ts:313:20:315:27 | [InterfaceTypeExpr] { pro ... void } | tst.ts:314:3:314:28 | [FieldDeclaration] produce ... ) => T, | semmle.order | 1 | +| tst.ts:313:20:315:27 | [InterfaceTypeExpr] { pro ... void } | tst.ts:315:3:315:25 | [FieldDeclaration] consume ... => void | semmle.label | 2 | +| tst.ts:313:20:315:27 | [InterfaceTypeExpr] { pro ... void } | tst.ts:315:3:315:25 | [FieldDeclaration] consume ... => void | semmle.order | 2 | +| tst.ts:314:3:314:28 | [FieldDeclaration] produce ... ) => T, | tst.ts:314:3:314:9 | [Label] produce | semmle.label | 1 | +| tst.ts:314:3:314:28 | [FieldDeclaration] produce ... ) => T, | tst.ts:314:3:314:9 | [Label] produce | semmle.order | 1 | +| tst.ts:314:3:314:28 | [FieldDeclaration] produce ... ) => T, | tst.ts:314:12:314:27 | [FunctionTypeExpr] (n: string) => T | semmle.label | 2 | +| tst.ts:314:3:314:28 | [FieldDeclaration] produce ... ) => T, | tst.ts:314:12:314:27 | [FunctionTypeExpr] (n: string) => T | semmle.order | 2 | +| tst.ts:314:12:314:27 | [FunctionExpr] (n: string) => T | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| tst.ts:314:12:314:27 | [FunctionExpr] (n: string) => T | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| tst.ts:314:12:314:27 | [FunctionExpr] (n: string) => T | tst.ts:314:27:314:27 | [LocalTypeAccess] T | semmle.label | 4 | +| tst.ts:314:12:314:27 | [FunctionExpr] (n: string) => T | tst.ts:314:27:314:27 | [LocalTypeAccess] T | semmle.order | 4 | +| tst.ts:314:12:314:27 | [FunctionTypeExpr] (n: string) => T | tst.ts:314:12:314:27 | [FunctionExpr] (n: string) => T | semmle.label | 1 | +| tst.ts:314:12:314:27 | [FunctionTypeExpr] (n: string) => T | tst.ts:314:12:314:27 | [FunctionExpr] (n: string) => T | semmle.order | 1 | +| tst.ts:314:13:314:13 | [SimpleParameter] n | tst.ts:314:16:314:21 | [KeywordTypeExpr] string | semmle.label | 0 | +| tst.ts:314:13:314:13 | [SimpleParameter] n | tst.ts:314:16:314:21 | [KeywordTypeExpr] string | semmle.order | 0 | +| tst.ts:315:3:315:25 | [FieldDeclaration] consume ... => void | tst.ts:315:3:315:9 | [Label] consume | semmle.label | 1 | +| tst.ts:315:3:315:25 | [FieldDeclaration] consume ... => void | tst.ts:315:3:315:9 | [Label] consume | semmle.order | 1 | +| tst.ts:315:3:315:25 | [FieldDeclaration] consume ... => void | tst.ts:315:12:315:25 | [FunctionTypeExpr] (x: T) => void | semmle.label | 2 | +| tst.ts:315:3:315:25 | [FieldDeclaration] consume ... => void | tst.ts:315:12:315:25 | [FunctionTypeExpr] (x: T) => void | semmle.order | 2 | +| tst.ts:315:12:315:25 | [FunctionExpr] (x: T) => void | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| tst.ts:315:12:315:25 | [FunctionExpr] (x: T) => void | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| tst.ts:315:12:315:25 | [FunctionExpr] (x: T) => void | tst.ts:315:22:315:25 | [KeywordTypeExpr] void | semmle.label | 4 | +| tst.ts:315:12:315:25 | [FunctionExpr] (x: T) => void | tst.ts:315:22:315:25 | [KeywordTypeExpr] void | semmle.order | 4 | +| tst.ts:315:12:315:25 | [FunctionTypeExpr] (x: T) => void | tst.ts:315:12:315:25 | [FunctionExpr] (x: T) => void | semmle.label | 1 | +| tst.ts:315:12:315:25 | [FunctionTypeExpr] (x: T) => void | tst.ts:315:12:315:25 | [FunctionExpr] (x: T) => void | semmle.order | 1 | +| tst.ts:315:13:315:13 | [SimpleParameter] x | tst.ts:315:16:315:16 | [LocalTypeAccess] T | semmle.label | 0 | +| tst.ts:315:13:315:13 | [SimpleParameter] x | tst.ts:315:16:315:16 | [LocalTypeAccess] T | semmle.order | 0 | +| tst.ts:318:1:321:2 | [CallExpr] f({ p ... se() }) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | +| tst.ts:318:1:321:2 | [CallExpr] f({ p ... se() }) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | +| tst.ts:318:1:321:2 | [CallExpr] f({ p ... se() }) | tst.ts:318:1:318:1 | [VarRef] f | semmle.label | 0 | +| tst.ts:318:1:321:2 | [CallExpr] f({ p ... se() }) | tst.ts:318:1:318:1 | [VarRef] f | semmle.order | 0 | +| tst.ts:318:1:321:3 | [ExprStmt] f({ p ... e() }); | tst.ts:318:1:321:2 | [CallExpr] f({ p ... se() }) | semmle.label | 1 | +| tst.ts:318:1:321:3 | [ExprStmt] f({ p ... e() }); | tst.ts:318:1:321:2 | [CallExpr] f({ p ... se() }) | semmle.order | 1 | +| tst.ts:318:3:321:1 | [ObjectExpr] {produce: ...} | tst.ts:319:3:319:17 | [Property] produce: n => n | semmle.label | 1 | +| tst.ts:318:3:321:1 | [ObjectExpr] {produce: ...} | tst.ts:319:3:319:17 | [Property] produce: n => n | semmle.order | 1 | +| tst.ts:318:3:321:1 | [ObjectExpr] {produce: ...} | tst.ts:320:3:320:31 | [Property] consume ... rCase() | semmle.label | 2 | +| tst.ts:318:3:321:1 | [ObjectExpr] {produce: ...} | tst.ts:320:3:320:31 | [Property] consume ... rCase() | semmle.order | 2 | +| tst.ts:319:3:319:17 | [Property] produce: n => n | tst.ts:319:3:319:9 | [Label] produce | semmle.label | 1 | +| tst.ts:319:3:319:17 | [Property] produce: n => n | tst.ts:319:3:319:9 | [Label] produce | semmle.order | 1 | +| tst.ts:319:3:319:17 | [Property] produce: n => n | tst.ts:319:12:319:17 | [ArrowFunctionExpr] n => n | semmle.label | 2 | +| tst.ts:319:3:319:17 | [Property] produce: n => n | tst.ts:319:12:319:17 | [ArrowFunctionExpr] n => n | semmle.order | 2 | +| tst.ts:319:12:319:17 | [ArrowFunctionExpr] n => n | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| tst.ts:319:12:319:17 | [ArrowFunctionExpr] n => n | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| tst.ts:319:12:319:17 | [ArrowFunctionExpr] n => n | tst.ts:319:17:319:17 | [VarRef] n | semmle.label | 5 | +| tst.ts:319:12:319:17 | [ArrowFunctionExpr] n => n | tst.ts:319:17:319:17 | [VarRef] n | semmle.order | 5 | +| tst.ts:320:3:320:31 | [Property] consume ... rCase() | tst.ts:320:3:320:9 | [Label] consume | semmle.label | 1 | +| tst.ts:320:3:320:31 | [Property] consume ... rCase() | tst.ts:320:3:320:9 | [Label] consume | semmle.order | 1 | +| tst.ts:320:3:320:31 | [Property] consume ... rCase() | tst.ts:320:12:320:31 | [ArrowFunctionExpr] x => x.toLowerCase() | semmle.label | 2 | +| tst.ts:320:3:320:31 | [Property] consume ... rCase() | tst.ts:320:12:320:31 | [ArrowFunctionExpr] x => x.toLowerCase() | semmle.order | 2 | +| tst.ts:320:12:320:31 | [ArrowFunctionExpr] x => x.toLowerCase() | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| tst.ts:320:12:320:31 | [ArrowFunctionExpr] x => x.toLowerCase() | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| tst.ts:320:12:320:31 | [ArrowFunctionExpr] x => x.toLowerCase() | tst.ts:320:17:320:31 | [MethodCallExpr] x.toLowerCase() | semmle.label | 5 | +| tst.ts:320:12:320:31 | [ArrowFunctionExpr] x => x.toLowerCase() | tst.ts:320:17:320:31 | [MethodCallExpr] x.toLowerCase() | semmle.order | 5 | +| tst.ts:320:17:320:29 | [DotExpr] x.toLowerCase | tst.ts:320:17:320:17 | [VarRef] x | semmle.label | 1 | +| tst.ts:320:17:320:29 | [DotExpr] x.toLowerCase | tst.ts:320:17:320:17 | [VarRef] x | semmle.order | 1 | +| tst.ts:320:17:320:29 | [DotExpr] x.toLowerCase | tst.ts:320:19:320:29 | [Label] toLowerCase | semmle.label | 2 | +| tst.ts:320:17:320:29 | [DotExpr] x.toLowerCase | tst.ts:320:19:320:29 | [Label] toLowerCase | semmle.order | 2 | +| tst.ts:320:17:320:31 | [MethodCallExpr] x.toLowerCase() | tst.ts:320:17:320:29 | [DotExpr] x.toLowerCase | semmle.label | 0 | +| tst.ts:320:17:320:31 | [MethodCallExpr] x.toLowerCase() | tst.ts:320:17:320:29 | [DotExpr] x.toLowerCase | semmle.order | 0 | +| tst.ts:325:1:325:36 | [DeclStmt] const ErrorMap = ... | tst.ts:325:7:325:35 | [VariableDeclarator] ErrorMa ... Error> | semmle.label | 1 | +| tst.ts:325:1:325:36 | [DeclStmt] const ErrorMap = ... | tst.ts:325:7:325:35 | [VariableDeclarator] ErrorMa ... Error> | semmle.order | 1 | +| tst.ts:325:7:325:35 | [VariableDeclarator] ErrorMa ... Error> | tst.ts:325:7:325:14 | [VarDecl] ErrorMap | semmle.label | 1 | +| tst.ts:325:7:325:35 | [VariableDeclarator] ErrorMa ... Error> | tst.ts:325:7:325:14 | [VarDecl] ErrorMap | semmle.order | 1 | +| tst.ts:325:7:325:35 | [VariableDeclarator] ErrorMa ... Error> | tst.ts:325:18:325:35 | [???] Map | semmle.label | 2 | +| tst.ts:325:7:325:35 | [VariableDeclarator] ErrorMa ... Error> | tst.ts:325:18:325:35 | [???] Map | semmle.order | 2 | +| tst.ts:325:18:325:35 | [???] Map | tst.ts:325:18:325:20 | [VarRef] Map | semmle.label | 1 | +| tst.ts:325:18:325:35 | [???] Map | tst.ts:325:18:325:20 | [VarRef] Map | semmle.order | 1 | +| tst.ts:325:18:325:35 | [???] Map | tst.ts:325:22:325:27 | [KeywordTypeExpr] string | semmle.label | 2 | +| tst.ts:325:18:325:35 | [???] Map | tst.ts:325:22:325:27 | [KeywordTypeExpr] string | semmle.order | 2 | +| tst.ts:325:18:325:35 | [???] Map | tst.ts:325:30:325:34 | [LocalTypeAccess] Error | semmle.label | 3 | +| tst.ts:325:18:325:35 | [???] Map | tst.ts:325:30:325:34 | [LocalTypeAccess] Error | semmle.order | 3 | +| tst.ts:327:1:327:32 | [DeclStmt] const errorMap = ... | tst.ts:327:7:327:31 | [VariableDeclarator] errorMa ... orMap() | semmle.label | 1 | +| tst.ts:327:1:327:32 | [DeclStmt] const errorMap = ... | tst.ts:327:7:327:31 | [VariableDeclarator] errorMa ... orMap() | semmle.order | 1 | +| tst.ts:327:7:327:31 | [VariableDeclarator] errorMa ... orMap() | tst.ts:327:7:327:14 | [VarDecl] errorMap | semmle.label | 1 | +| tst.ts:327:7:327:31 | [VariableDeclarator] errorMa ... orMap() | tst.ts:327:7:327:14 | [VarDecl] errorMap | semmle.order | 1 | +| tst.ts:327:7:327:31 | [VariableDeclarator] errorMa ... orMap() | tst.ts:327:18:327:31 | [NewExpr] new ErrorMap() | semmle.label | 2 | +| tst.ts:327:7:327:31 | [VariableDeclarator] errorMa ... orMap() | tst.ts:327:18:327:31 | [NewExpr] new ErrorMap() | semmle.order | 2 | +| tst.ts:327:18:327:31 | [NewExpr] new ErrorMap() | tst.ts:327:22:327:29 | [VarRef] ErrorMap | semmle.label | 0 | +| tst.ts:327:18:327:31 | [NewExpr] new ErrorMap() | tst.ts:327:22:327:29 | [VarRef] ErrorMap | semmle.order | 0 | +| tst.ts:331:1:334:14 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | +| tst.ts:331:1:334:14 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | +| tst.ts:331:1:334:14 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | tst.ts:331:6:331:16 | [Identifier] FirstString | semmle.label | 1 | +| tst.ts:331:1:334:14 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | tst.ts:331:6:331:16 | [Identifier] FirstString | semmle.order | 1 | +| tst.ts:331:1:334:14 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | tst.ts:332:3:334:13 | [ConditionalTypeExpr] T exten ... : never | semmle.label | 3 | +| tst.ts:331:1:334:14 | [TypeAliasDeclaration,TypeDefinition] type Fi ... never; | tst.ts:332:3:334:13 | [ConditionalTypeExpr] T exten ... : never | semmle.order | 3 | +| tst.ts:331:18:331:18 | [TypeParameter] T | tst.ts:331:18:331:18 | [Identifier] T | semmle.label | 1 | +| tst.ts:331:18:331:18 | [TypeParameter] T | tst.ts:331:18:331:18 | [Identifier] T | semmle.order | 1 | +| tst.ts:332:3:334:13 | [ConditionalTypeExpr] T exten ... : never | tst.ts:332:3:332:3 | [LocalTypeAccess] T | semmle.label | 1 | +| tst.ts:332:3:334:13 | [ConditionalTypeExpr] T exten ... : never | tst.ts:332:3:332:3 | [LocalTypeAccess] T | semmle.order | 1 | +| tst.ts:332:3:334:13 | [ConditionalTypeExpr] T exten ... : never | tst.ts:332:13:332:50 | [TupleTypeExpr] [infer ... nown[]] | semmle.label | 2 | +| tst.ts:332:3:334:13 | [ConditionalTypeExpr] T exten ... : never | tst.ts:332:13:332:50 | [TupleTypeExpr] [infer ... nown[]] | semmle.order | 2 | +| tst.ts:332:3:334:13 | [ConditionalTypeExpr] T exten ... : never | tst.ts:333:9:333:9 | [LocalTypeAccess] S | semmle.label | 3 | +| tst.ts:332:3:334:13 | [ConditionalTypeExpr] T exten ... : never | tst.ts:333:9:333:9 | [LocalTypeAccess] S | semmle.order | 3 | +| tst.ts:332:3:334:13 | [ConditionalTypeExpr] T exten ... : never | tst.ts:334:9:334:13 | [KeywordTypeExpr] never | semmle.label | 4 | +| tst.ts:332:3:334:13 | [ConditionalTypeExpr] T exten ... : never | tst.ts:334:9:334:13 | [KeywordTypeExpr] never | semmle.order | 4 | +| tst.ts:332:13:332:50 | [TupleTypeExpr] [infer ... nown[]] | tst.ts:332:14:332:35 | [InferTypeExpr] infer S ... string | semmle.label | 1 | +| tst.ts:332:13:332:50 | [TupleTypeExpr] [infer ... nown[]] | tst.ts:332:14:332:35 | [InferTypeExpr] infer S ... string | semmle.order | 1 | +| tst.ts:332:13:332:50 | [TupleTypeExpr] [infer ... nown[]] | tst.ts:332:38:332:49 | [RestTypeExpr] ...unknown[] | semmle.label | 2 | +| tst.ts:332:13:332:50 | [TupleTypeExpr] [infer ... nown[]] | tst.ts:332:38:332:49 | [RestTypeExpr] ...unknown[] | semmle.order | 2 | +| tst.ts:332:14:332:35 | [InferTypeExpr] infer S ... string | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | +| tst.ts:332:14:332:35 | [InferTypeExpr] infer S ... string | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | +| tst.ts:332:20:332:35 | [TypeParameter] S extends string | tst.ts:332:20:332:20 | [Identifier] S | semmle.label | 1 | +| tst.ts:332:20:332:35 | [TypeParameter] S extends string | tst.ts:332:20:332:20 | [Identifier] S | semmle.order | 1 | +| tst.ts:332:20:332:35 | [TypeParameter] S extends string | tst.ts:332:30:332:35 | [KeywordTypeExpr] string | semmle.label | 2 | +| tst.ts:332:20:332:35 | [TypeParameter] S extends string | tst.ts:332:30:332:35 | [KeywordTypeExpr] string | semmle.order | 2 | +| tst.ts:332:38:332:49 | [RestTypeExpr] ...unknown[] | tst.ts:332:41:332:49 | [ArrayTypeExpr] unknown[] | semmle.label | 1 | +| tst.ts:332:38:332:49 | [RestTypeExpr] ...unknown[] | tst.ts:332:41:332:49 | [ArrayTypeExpr] unknown[] | semmle.order | 1 | +| tst.ts:332:41:332:49 | [ArrayTypeExpr] unknown[] | tst.ts:332:41:332:47 | [KeywordTypeExpr] unknown | semmle.label | 1 | +| tst.ts:332:41:332:49 | [ArrayTypeExpr] unknown[] | tst.ts:332:41:332:47 | [KeywordTypeExpr] unknown | semmle.order | 1 | +| tst.ts:336:1:336:51 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | tst.ts:336:6:336:6 | [Identifier] F | semmle.label | 1 | +| tst.ts:336:1:336:51 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | tst.ts:336:6:336:6 | [Identifier] F | semmle.order | 1 | +| tst.ts:336:1:336:51 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | tst.ts:336:10:336:50 | [GenericTypeExpr] FirstSt ... olean]> | semmle.label | 2 | +| tst.ts:336:1:336:51 | [TypeAliasDeclaration,TypeDefinition] type F ... lean]>; | tst.ts:336:10:336:50 | [GenericTypeExpr] FirstSt ... olean]> | semmle.order | 2 | +| tst.ts:336:10:336:50 | [GenericTypeExpr] FirstSt ... olean]> | tst.ts:336:10:336:20 | [LocalTypeAccess] FirstString | semmle.label | 1 | +| tst.ts:336:10:336:50 | [GenericTypeExpr] FirstSt ... olean]> | tst.ts:336:10:336:20 | [LocalTypeAccess] FirstString | semmle.order | 1 | +| tst.ts:336:10:336:50 | [GenericTypeExpr] FirstSt ... olean]> | tst.ts:336:22:336:49 | [TupleTypeExpr] ['a' \| ... oolean] | semmle.label | 2 | +| tst.ts:336:10:336:50 | [GenericTypeExpr] FirstSt ... olean]> | tst.ts:336:22:336:49 | [TupleTypeExpr] ['a' \| ... oolean] | semmle.order | 2 | +| tst.ts:336:22:336:49 | [TupleTypeExpr] ['a' \| ... oolean] | tst.ts:336:23:336:31 | [UnionTypeExpr] 'a' \| 'b' | semmle.label | 1 | +| tst.ts:336:22:336:49 | [TupleTypeExpr] ['a' \| ... oolean] | tst.ts:336:23:336:31 | [UnionTypeExpr] 'a' \| 'b' | semmle.order | 1 | +| tst.ts:336:22:336:49 | [TupleTypeExpr] ['a' \| ... oolean] | tst.ts:336:34:336:39 | [KeywordTypeExpr] number | semmle.label | 2 | +| tst.ts:336:22:336:49 | [TupleTypeExpr] ['a' \| ... oolean] | tst.ts:336:34:336:39 | [KeywordTypeExpr] number | semmle.order | 2 | +| tst.ts:336:22:336:49 | [TupleTypeExpr] ['a' \| ... oolean] | tst.ts:336:42:336:48 | [KeywordTypeExpr] boolean | semmle.label | 3 | +| tst.ts:336:22:336:49 | [TupleTypeExpr] ['a' \| ... oolean] | tst.ts:336:42:336:48 | [KeywordTypeExpr] boolean | semmle.order | 3 | +| tst.ts:336:23:336:31 | [UnionTypeExpr] 'a' \| 'b' | tst.ts:336:23:336:25 | [LiteralTypeExpr] 'a' | semmle.label | 1 | +| tst.ts:336:23:336:31 | [UnionTypeExpr] 'a' \| 'b' | tst.ts:336:23:336:25 | [LiteralTypeExpr] 'a' | semmle.order | 1 | +| tst.ts:336:23:336:31 | [UnionTypeExpr] 'a' \| 'b' | tst.ts:336:29:336:31 | [LiteralTypeExpr] 'b' | semmle.label | 2 | +| tst.ts:336:23:336:31 | [UnionTypeExpr] 'a' \| 'b' | tst.ts:336:29:336:31 | [LiteralTypeExpr] 'b' | semmle.order | 2 | +| tst.ts:338:1:338:17 | [DeclStmt] const a = ... | tst.ts:338:7:338:16 | [VariableDeclarator] a: F = 'a' | semmle.label | 1 | +| tst.ts:338:1:338:17 | [DeclStmt] const a = ... | tst.ts:338:7:338:16 | [VariableDeclarator] a: F = 'a' | semmle.order | 1 | +| tst.ts:338:7:338:16 | [VariableDeclarator] a: F = 'a' | tst.ts:338:7:338:7 | [VarDecl] a | semmle.label | 1 | +| tst.ts:338:7:338:16 | [VariableDeclarator] a: F = 'a' | tst.ts:338:7:338:7 | [VarDecl] a | semmle.order | 1 | +| tst.ts:338:7:338:16 | [VariableDeclarator] a: F = 'a' | tst.ts:338:10:338:10 | [LocalTypeAccess] F | semmle.label | 2 | +| tst.ts:338:7:338:16 | [VariableDeclarator] a: F = 'a' | tst.ts:338:10:338:10 | [LocalTypeAccess] F | semmle.order | 2 | +| tst.ts:338:7:338:16 | [VariableDeclarator] a: F = 'a' | tst.ts:338:14:338:16 | [Literal] 'a' | semmle.label | 3 | +| tst.ts:338:7:338:16 | [VariableDeclarator] a: F = 'a' | tst.ts:338:14:338:16 | [Literal] 'a' | semmle.order | 3 | +| tst.ts:342:1:345:1 | [InterfaceDeclaration,TypeDefinition] interfa ... void; } | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | +| tst.ts:342:1:345:1 | [InterfaceDeclaration,TypeDefinition] interfa ... void; } | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | +| tst.ts:342:1:345:1 | [InterfaceDeclaration,TypeDefinition] interfa ... void; } | tst.ts:342:11:342:15 | [Identifier] State | semmle.label | 1 | +| tst.ts:342:1:345:1 | [InterfaceDeclaration,TypeDefinition] interfa ... void; } | tst.ts:342:11:342:15 | [Identifier] State | semmle.order | 1 | +| tst.ts:342:1:345:1 | [InterfaceDeclaration,TypeDefinition] interfa ... void; } | tst.ts:343:3:343:15 | [FieldDeclaration] get: () => T; | semmle.label | 3 | +| tst.ts:342:1:345:1 | [InterfaceDeclaration,TypeDefinition] interfa ... void; } | tst.ts:343:3:343:15 | [FieldDeclaration] get: () => T; | semmle.order | 3 | +| tst.ts:342:1:345:1 | [InterfaceDeclaration,TypeDefinition] interfa ... void; } | tst.ts:344:3:344:26 | [FieldDeclaration] set: (v ... > void; | semmle.label | 4 | +| tst.ts:342:1:345:1 | [InterfaceDeclaration,TypeDefinition] interfa ... void; } | tst.ts:344:3:344:26 | [FieldDeclaration] set: (v ... > void; | semmle.order | 4 | +| tst.ts:342:17:342:24 | [TypeParameter] in out T | tst.ts:342:24:342:24 | [Identifier] T | semmle.label | 1 | +| tst.ts:342:17:342:24 | [TypeParameter] in out T | tst.ts:342:24:342:24 | [Identifier] T | semmle.order | 1 | +| tst.ts:343:3:343:15 | [FieldDeclaration] get: () => T; | tst.ts:343:3:343:5 | [Label] get | semmle.label | 1 | +| tst.ts:343:3:343:15 | [FieldDeclaration] get: () => T; | tst.ts:343:3:343:5 | [Label] get | semmle.order | 1 | +| tst.ts:343:3:343:15 | [FieldDeclaration] get: () => T; | tst.ts:343:8:343:14 | [FunctionTypeExpr] () => T | semmle.label | 2 | +| tst.ts:343:3:343:15 | [FieldDeclaration] get: () => T; | tst.ts:343:8:343:14 | [FunctionTypeExpr] () => T | semmle.order | 2 | +| tst.ts:343:8:343:14 | [FunctionExpr] () => T | tst.ts:343:14:343:14 | [LocalTypeAccess] T | semmle.label | 4 | +| tst.ts:343:8:343:14 | [FunctionExpr] () => T | tst.ts:343:14:343:14 | [LocalTypeAccess] T | semmle.order | 4 | +| tst.ts:343:8:343:14 | [FunctionTypeExpr] () => T | tst.ts:343:8:343:14 | [FunctionExpr] () => T | semmle.label | 1 | +| tst.ts:343:8:343:14 | [FunctionTypeExpr] () => T | tst.ts:343:8:343:14 | [FunctionExpr] () => T | semmle.order | 1 | +| tst.ts:344:3:344:26 | [FieldDeclaration] set: (v ... > void; | tst.ts:344:3:344:5 | [Label] set | semmle.label | 1 | +| tst.ts:344:3:344:26 | [FieldDeclaration] set: (v ... > void; | tst.ts:344:3:344:5 | [Label] set | semmle.order | 1 | +| tst.ts:344:3:344:26 | [FieldDeclaration] set: (v ... > void; | tst.ts:344:8:344:25 | [FunctionTypeExpr] (value: T) => void | semmle.label | 2 | +| tst.ts:344:3:344:26 | [FieldDeclaration] set: (v ... > void; | tst.ts:344:8:344:25 | [FunctionTypeExpr] (value: T) => void | semmle.order | 2 | +| tst.ts:344:8:344:25 | [FunctionExpr] (value: T) => void | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| tst.ts:344:8:344:25 | [FunctionExpr] (value: T) => void | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| tst.ts:344:8:344:25 | [FunctionExpr] (value: T) => void | tst.ts:344:22:344:25 | [KeywordTypeExpr] void | semmle.label | 4 | +| tst.ts:344:8:344:25 | [FunctionExpr] (value: T) => void | tst.ts:344:22:344:25 | [KeywordTypeExpr] void | semmle.order | 4 | +| tst.ts:344:8:344:25 | [FunctionTypeExpr] (value: T) => void | tst.ts:344:8:344:25 | [FunctionExpr] (value: T) => void | semmle.label | 1 | +| tst.ts:344:8:344:25 | [FunctionTypeExpr] (value: T) => void | tst.ts:344:8:344:25 | [FunctionExpr] (value: T) => void | semmle.order | 1 | +| tst.ts:344:9:344:13 | [SimpleParameter] value | tst.ts:344:16:344:16 | [LocalTypeAccess] T | semmle.label | 0 | +| tst.ts:344:9:344:13 | [SimpleParameter] value | tst.ts:344:16:344:16 | [LocalTypeAccess] T | semmle.order | 0 | +| tst.ts:347:1:350:1 | [DeclStmt] const state = ... | tst.ts:347:7:350:1 | [VariableDeclarator] state: ... > { } } | semmle.label | 1 | +| tst.ts:347:1:350:1 | [DeclStmt] const state = ... | tst.ts:347:7:350:1 | [VariableDeclarator] state: ... > { } } | semmle.order | 1 | +| tst.ts:347:7:350:1 | [VariableDeclarator] state: ... > { } } | tst.ts:347:7:347:11 | [VarDecl] state | semmle.label | 1 | +| tst.ts:347:7:350:1 | [VariableDeclarator] state: ... > { } } | tst.ts:347:7:347:11 | [VarDecl] state | semmle.order | 1 | +| tst.ts:347:7:350:1 | [VariableDeclarator] state: ... > { } } | tst.ts:347:14:347:26 | [GenericTypeExpr] State | semmle.label | 2 | +| tst.ts:347:7:350:1 | [VariableDeclarator] state: ... > { } } | tst.ts:347:14:347:26 | [GenericTypeExpr] State | semmle.order | 2 | +| tst.ts:347:7:350:1 | [VariableDeclarator] state: ... > { } } | tst.ts:347:30:350:1 | [ObjectExpr] {get: ...} | semmle.label | 3 | +| tst.ts:347:7:350:1 | [VariableDeclarator] state: ... > { } } | tst.ts:347:30:350:1 | [ObjectExpr] {get: ...} | semmle.order | 3 | +| tst.ts:347:14:347:26 | [GenericTypeExpr] State | tst.ts:347:14:347:18 | [LocalTypeAccess] State | semmle.label | 1 | +| tst.ts:347:14:347:26 | [GenericTypeExpr] State | tst.ts:347:14:347:18 | [LocalTypeAccess] State | semmle.order | 1 | +| tst.ts:347:14:347:26 | [GenericTypeExpr] State | tst.ts:347:20:347:25 | [KeywordTypeExpr] number | semmle.label | 2 | +| tst.ts:347:14:347:26 | [GenericTypeExpr] State | tst.ts:347:20:347:25 | [KeywordTypeExpr] number | semmle.order | 2 | +| tst.ts:347:30:350:1 | [ObjectExpr] {get: ...} | tst.ts:348:3:348:15 | [Property] get: () => 42 | semmle.label | 1 | +| tst.ts:347:30:350:1 | [ObjectExpr] {get: ...} | tst.ts:348:3:348:15 | [Property] get: () => 42 | semmle.order | 1 | +| tst.ts:347:30:350:1 | [ObjectExpr] {get: ...} | tst.ts:349:3:349:21 | [Property] set: (value) => { } | semmle.label | 2 | +| tst.ts:347:30:350:1 | [ObjectExpr] {get: ...} | tst.ts:349:3:349:21 | [Property] set: (value) => { } | semmle.order | 2 | +| tst.ts:348:3:348:15 | [Property] get: () => 42 | tst.ts:348:3:348:5 | [Label] get | semmle.label | 1 | +| tst.ts:348:3:348:15 | [Property] get: () => 42 | tst.ts:348:3:348:5 | [Label] get | semmle.order | 1 | +| tst.ts:348:3:348:15 | [Property] get: () => 42 | tst.ts:348:8:348:15 | [ArrowFunctionExpr] () => 42 | semmle.label | 2 | +| tst.ts:348:3:348:15 | [Property] get: () => 42 | tst.ts:348:8:348:15 | [ArrowFunctionExpr] () => 42 | semmle.order | 2 | +| tst.ts:348:8:348:15 | [ArrowFunctionExpr] () => 42 | tst.ts:348:14:348:15 | [Literal] 42 | semmle.label | 5 | +| tst.ts:348:8:348:15 | [ArrowFunctionExpr] () => 42 | tst.ts:348:14:348:15 | [Literal] 42 | semmle.order | 5 | +| tst.ts:349:3:349:21 | [Property] set: (value) => { } | tst.ts:349:3:349:5 | [Label] set | semmle.label | 1 | +| tst.ts:349:3:349:21 | [Property] set: (value) => { } | tst.ts:349:3:349:5 | [Label] set | semmle.order | 1 | +| tst.ts:349:3:349:21 | [Property] set: (value) => { } | tst.ts:349:8:349:21 | [ArrowFunctionExpr] (value) => { } | semmle.label | 2 | +| tst.ts:349:3:349:21 | [Property] set: (value) => { } | tst.ts:349:8:349:21 | [ArrowFunctionExpr] (value) => { } | semmle.order | 2 | +| tst.ts:349:8:349:21 | [ArrowFunctionExpr] (value) => { } | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| tst.ts:349:8:349:21 | [ArrowFunctionExpr] (value) => { } | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| tst.ts:349:8:349:21 | [ArrowFunctionExpr] (value) => { } | tst.ts:349:19:349:21 | [BlockStmt] { } | semmle.label | 5 | +| tst.ts:349:8:349:21 | [ArrowFunctionExpr] (value) => { } | tst.ts:349:19:349:21 | [BlockStmt] { } | semmle.order | 5 | +| tst.ts:352:1:352:29 | [DeclStmt] const fortyTwo = ... | tst.ts:352:7:352:28 | [VariableDeclarator] fortyTw ... e.get() | semmle.label | 1 | +| tst.ts:352:1:352:29 | [DeclStmt] const fortyTwo = ... | tst.ts:352:7:352:28 | [VariableDeclarator] fortyTw ... e.get() | semmle.order | 1 | +| tst.ts:352:7:352:28 | [VariableDeclarator] fortyTw ... e.get() | tst.ts:352:7:352:14 | [VarDecl] fortyTwo | semmle.label | 1 | +| tst.ts:352:7:352:28 | [VariableDeclarator] fortyTw ... e.get() | tst.ts:352:7:352:14 | [VarDecl] fortyTwo | semmle.order | 1 | +| tst.ts:352:7:352:28 | [VariableDeclarator] fortyTw ... e.get() | tst.ts:352:18:352:28 | [MethodCallExpr] state.get() | semmle.label | 2 | +| tst.ts:352:7:352:28 | [VariableDeclarator] fortyTw ... e.get() | tst.ts:352:18:352:28 | [MethodCallExpr] state.get() | semmle.order | 2 | +| tst.ts:352:18:352:26 | [DotExpr] state.get | tst.ts:352:18:352:22 | [VarRef] state | semmle.label | 1 | +| tst.ts:352:18:352:26 | [DotExpr] state.get | tst.ts:352:18:352:22 | [VarRef] state | semmle.order | 1 | +| tst.ts:352:18:352:26 | [DotExpr] state.get | tst.ts:352:24:352:26 | [Label] get | semmle.label | 2 | +| tst.ts:352:18:352:26 | [DotExpr] state.get | tst.ts:352:24:352:26 | [Label] get | semmle.order | 2 | +| tst.ts:352:18:352:28 | [MethodCallExpr] state.get() | tst.ts:352:18:352:26 | [DotExpr] state.get | semmle.label | 0 | +| tst.ts:352:18:352:28 | [MethodCallExpr] state.get() | tst.ts:352:18:352:26 | [DotExpr] state.get | semmle.order | 0 | +| tst.ts:356:1:356:44 | [ImportDeclaration] import ... S.mjs'; | tst.ts:356:8:356:18 | [ImportSpecifier] tstModuleES | semmle.label | 1 | +| tst.ts:356:1:356:44 | [ImportDeclaration] import ... S.mjs'; | tst.ts:356:8:356:18 | [ImportSpecifier] tstModuleES | semmle.order | 1 | +| tst.ts:356:1:356:44 | [ImportDeclaration] import ... S.mjs'; | tst.ts:356:25:356:43 | [Literal] './tstModuleES.mjs' | semmle.label | 2 | +| tst.ts:356:1:356:44 | [ImportDeclaration] import ... S.mjs'; | tst.ts:356:25:356:43 | [Literal] './tstModuleES.mjs' | semmle.order | 2 | +| tst.ts:356:8:356:18 | [ImportSpecifier] tstModuleES | tst.ts:356:8:356:18 | [VarDecl] tstModuleES | semmle.label | 1 | +| tst.ts:356:8:356:18 | [ImportSpecifier] tstModuleES | tst.ts:356:8:356:18 | [VarDecl] tstModuleES | semmle.order | 1 | +| tst.ts:358:1:358:11 | [DotExpr] console.log | tst.ts:358:1:358:7 | [VarRef] console | semmle.label | 1 | +| tst.ts:358:1:358:11 | [DotExpr] console.log | tst.ts:358:1:358:7 | [VarRef] console | semmle.order | 1 | +| tst.ts:358:1:358:11 | [DotExpr] console.log | tst.ts:358:9:358:11 | [Label] log | semmle.label | 2 | +| tst.ts:358:1:358:11 | [DotExpr] console.log | tst.ts:358:9:358:11 | [Label] log | semmle.order | 2 | +| tst.ts:358:1:358:26 | [MethodCallExpr] console ... leES()) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | +| tst.ts:358:1:358:26 | [MethodCallExpr] console ... leES()) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | +| tst.ts:358:1:358:26 | [MethodCallExpr] console ... leES()) | tst.ts:358:1:358:11 | [DotExpr] console.log | semmle.label | 0 | +| tst.ts:358:1:358:26 | [MethodCallExpr] console ... leES()) | tst.ts:358:1:358:11 | [DotExpr] console.log | semmle.order | 0 | +| tst.ts:358:1:358:27 | [ExprStmt] console ... eES()); | tst.ts:358:1:358:26 | [MethodCallExpr] console ... leES()) | semmle.label | 1 | +| tst.ts:358:1:358:27 | [ExprStmt] console ... eES()); | tst.ts:358:1:358:26 | [MethodCallExpr] console ... leES()) | semmle.order | 1 | +| tst.ts:358:13:358:25 | [CallExpr] tstModuleES() | tst.ts:358:13:358:23 | [VarRef] tstModuleES | semmle.label | 0 | +| tst.ts:358:13:358:25 | [CallExpr] tstModuleES() | tst.ts:358:13:358:23 | [VarRef] tstModuleES | semmle.order | 0 | +| tst.ts:360:1:360:50 | [ImportDeclaration] import ... S.cjs'; | tst.ts:360:10:360:21 | [ImportSpecifier] tstModuleCJS | semmle.label | 1 | +| tst.ts:360:1:360:50 | [ImportDeclaration] import ... S.cjs'; | tst.ts:360:10:360:21 | [ImportSpecifier] tstModuleCJS | semmle.order | 1 | +| tst.ts:360:1:360:50 | [ImportDeclaration] import ... S.cjs'; | tst.ts:360:30:360:49 | [Literal] './tstModuleCJS.cjs' | semmle.label | 2 | +| tst.ts:360:1:360:50 | [ImportDeclaration] import ... S.cjs'; | tst.ts:360:30:360:49 | [Literal] './tstModuleCJS.cjs' | semmle.order | 2 | +| tst.ts:360:10:360:21 | [ImportSpecifier] tstModuleCJS | tst.ts:360:10:360:21 | [Label] tstModuleCJS | semmle.label | 1 | +| tst.ts:360:10:360:21 | [ImportSpecifier] tstModuleCJS | tst.ts:360:10:360:21 | [Label] tstModuleCJS | semmle.order | 1 | +| tst.ts:360:10:360:21 | [ImportSpecifier] tstModuleCJS | tst.ts:360:10:360:21 | [VarDecl] tstModuleCJS | semmle.label | 2 | +| tst.ts:360:10:360:21 | [ImportSpecifier] tstModuleCJS | tst.ts:360:10:360:21 | [VarDecl] tstModuleCJS | semmle.order | 2 | +| tst.ts:362:1:362:11 | [DotExpr] console.log | tst.ts:362:1:362:7 | [VarRef] console | semmle.label | 1 | +| tst.ts:362:1:362:11 | [DotExpr] console.log | tst.ts:362:1:362:7 | [VarRef] console | semmle.order | 1 | +| tst.ts:362:1:362:11 | [DotExpr] console.log | tst.ts:362:9:362:11 | [Label] log | semmle.label | 2 | +| tst.ts:362:1:362:11 | [DotExpr] console.log | tst.ts:362:9:362:11 | [Label] log | semmle.order | 2 | +| tst.ts:362:1:362:27 | [MethodCallExpr] console ... eCJS()) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | +| tst.ts:362:1:362:27 | [MethodCallExpr] console ... eCJS()) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | +| tst.ts:362:1:362:27 | [MethodCallExpr] console ... eCJS()) | tst.ts:362:1:362:11 | [DotExpr] console.log | semmle.label | 0 | +| tst.ts:362:1:362:27 | [MethodCallExpr] console ... eCJS()) | tst.ts:362:1:362:11 | [DotExpr] console.log | semmle.order | 0 | +| tst.ts:362:1:362:28 | [ExprStmt] console ... CJS()); | tst.ts:362:1:362:27 | [MethodCallExpr] console ... eCJS()) | semmle.label | 1 | +| tst.ts:362:1:362:28 | [ExprStmt] console ... CJS()); | tst.ts:362:1:362:27 | [MethodCallExpr] console ... eCJS()) | semmle.order | 1 | +| tst.ts:362:13:362:26 | [CallExpr] tstModuleCJS() | tst.ts:362:13:362:24 | [VarRef] tstModuleCJS | semmle.label | 0 | +| tst.ts:362:13:362:26 | [CallExpr] tstModuleCJS() | tst.ts:362:13:362:24 | [VarRef] tstModuleCJS | semmle.order | 0 | +| tst.ts:368:1:368:34 | [ImportDeclaration] import ... ffixA'; | tst.ts:368:8:368:13 | [ImportSpecifier] * as A | semmle.label | 1 | +| tst.ts:368:1:368:34 | [ImportDeclaration] import ... ffixA'; | tst.ts:368:8:368:13 | [ImportSpecifier] * as A | semmle.order | 1 | +| tst.ts:368:1:368:34 | [ImportDeclaration] import ... ffixA'; | tst.ts:368:20:368:33 | [Literal] './tstSuffixA' | semmle.label | 2 | +| tst.ts:368:1:368:34 | [ImportDeclaration] import ... ffixA'; | tst.ts:368:20:368:33 | [Literal] './tstSuffixA' | semmle.order | 2 | +| tst.ts:368:8:368:13 | [ImportSpecifier] * as A | tst.ts:368:13:368:13 | [VarDecl] A | semmle.label | 1 | +| tst.ts:368:8:368:13 | [ImportSpecifier] * as A | tst.ts:368:13:368:13 | [VarDecl] A | semmle.order | 1 | +| tst.ts:370:1:370:11 | [DotExpr] console.log | tst.ts:370:1:370:7 | [VarRef] console | semmle.label | 1 | +| tst.ts:370:1:370:11 | [DotExpr] console.log | tst.ts:370:1:370:7 | [VarRef] console | semmle.order | 1 | +| tst.ts:370:1:370:11 | [DotExpr] console.log | tst.ts:370:9:370:11 | [Label] log | semmle.label | 2 | +| tst.ts:370:1:370:11 | [DotExpr] console.log | tst.ts:370:9:370:11 | [Label] log | semmle.order | 2 | +| tst.ts:370:1:370:29 | [MethodCallExpr] console ... File()) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | +| tst.ts:370:1:370:29 | [MethodCallExpr] console ... File()) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | +| tst.ts:370:1:370:29 | [MethodCallExpr] console ... File()) | tst.ts:370:1:370:11 | [DotExpr] console.log | semmle.label | 0 | +| tst.ts:370:1:370:29 | [MethodCallExpr] console ... File()) | tst.ts:370:1:370:11 | [DotExpr] console.log | semmle.order | 0 | +| tst.ts:370:1:370:30 | [ExprStmt] console ... ile()); | tst.ts:370:1:370:29 | [MethodCallExpr] console ... File()) | semmle.label | 1 | +| tst.ts:370:1:370:30 | [ExprStmt] console ... ile()); | tst.ts:370:1:370:29 | [MethodCallExpr] console ... File()) | semmle.order | 1 | +| tst.ts:370:13:370:26 | [DotExpr] A.resolvedFile | tst.ts:370:13:370:13 | [VarRef] A | semmle.label | 1 | +| tst.ts:370:13:370:26 | [DotExpr] A.resolvedFile | tst.ts:370:13:370:13 | [VarRef] A | semmle.order | 1 | +| tst.ts:370:13:370:26 | [DotExpr] A.resolvedFile | tst.ts:370:15:370:26 | [Label] resolvedFile | semmle.label | 2 | +| tst.ts:370:13:370:26 | [DotExpr] A.resolvedFile | tst.ts:370:15:370:26 | [Label] resolvedFile | semmle.order | 2 | +| tst.ts:370:13:370:28 | [MethodCallExpr] A.resolvedFile() | tst.ts:370:13:370:26 | [DotExpr] A.resolvedFile | semmle.label | 0 | +| tst.ts:370:13:370:28 | [MethodCallExpr] A.resolvedFile() | tst.ts:370:13:370:26 | [DotExpr] A.resolvedFile | semmle.order | 0 | +| tst.ts:372:1:372:34 | [ImportDeclaration] import ... ffixB'; | tst.ts:372:8:372:13 | [ImportSpecifier] * as B | semmle.label | 1 | +| tst.ts:372:1:372:34 | [ImportDeclaration] import ... ffixB'; | tst.ts:372:8:372:13 | [ImportSpecifier] * as B | semmle.order | 1 | +| tst.ts:372:1:372:34 | [ImportDeclaration] import ... ffixB'; | tst.ts:372:20:372:33 | [Literal] './tstSuffixB' | semmle.label | 2 | +| tst.ts:372:1:372:34 | [ImportDeclaration] import ... ffixB'; | tst.ts:372:20:372:33 | [Literal] './tstSuffixB' | semmle.order | 2 | +| tst.ts:372:8:372:13 | [ImportSpecifier] * as B | tst.ts:372:13:372:13 | [VarDecl] B | semmle.label | 1 | +| tst.ts:372:8:372:13 | [ImportSpecifier] * as B | tst.ts:372:13:372:13 | [VarDecl] B | semmle.order | 1 | +| tst.ts:374:1:374:11 | [DotExpr] console.log | tst.ts:374:1:374:7 | [VarRef] console | semmle.label | 1 | +| tst.ts:374:1:374:11 | [DotExpr] console.log | tst.ts:374:1:374:7 | [VarRef] console | semmle.order | 1 | +| tst.ts:374:1:374:11 | [DotExpr] console.log | tst.ts:374:9:374:11 | [Label] log | semmle.label | 2 | +| tst.ts:374:1:374:11 | [DotExpr] console.log | tst.ts:374:9:374:11 | [Label] log | semmle.order | 2 | +| tst.ts:374:1:374:29 | [MethodCallExpr] console ... File()) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | +| tst.ts:374:1:374:29 | [MethodCallExpr] console ... File()) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | +| tst.ts:374:1:374:29 | [MethodCallExpr] console ... File()) | tst.ts:374:1:374:11 | [DotExpr] console.log | semmle.label | 0 | +| tst.ts:374:1:374:29 | [MethodCallExpr] console ... File()) | tst.ts:374:1:374:11 | [DotExpr] console.log | semmle.order | 0 | +| tst.ts:374:1:374:30 | [ExprStmt] console ... ile()); | tst.ts:374:1:374:29 | [MethodCallExpr] console ... File()) | semmle.label | 1 | +| tst.ts:374:1:374:30 | [ExprStmt] console ... ile()); | tst.ts:374:1:374:29 | [MethodCallExpr] console ... File()) | semmle.order | 1 | +| tst.ts:374:13:374:26 | [DotExpr] B.resolvedFile | tst.ts:374:13:374:13 | [VarRef] B | semmle.label | 1 | +| tst.ts:374:13:374:26 | [DotExpr] B.resolvedFile | tst.ts:374:13:374:13 | [VarRef] B | semmle.order | 1 | +| tst.ts:374:13:374:26 | [DotExpr] B.resolvedFile | tst.ts:374:15:374:26 | [Label] resolvedFile | semmle.label | 2 | +| tst.ts:374:13:374:26 | [DotExpr] B.resolvedFile | tst.ts:374:15:374:26 | [Label] resolvedFile | semmle.order | 2 | +| tst.ts:374:13:374:28 | [MethodCallExpr] B.resolvedFile() | tst.ts:374:13:374:26 | [DotExpr] B.resolvedFile | semmle.label | 0 | +| tst.ts:374:13:374:28 | [MethodCallExpr] B.resolvedFile() | tst.ts:374:13:374:26 | [DotExpr] B.resolvedFile | semmle.order | 0 | +| 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 | +| tstModuleCJS.cts:1:8:3:1 | [FunctionDeclStmt] functio ... 'b'; } | tstModuleCJS.cts:1:17:1:28 | [VarDecl] tstModuleCJS | semmle.order | 0 | +| tstModuleCJS.cts:1:8:3:1 | [FunctionDeclStmt] functio ... 'b'; } | tstModuleCJS.cts:1:33:1:41 | [UnionTypeExpr] 'a' \| 'b' | semmle.label | 4 | +| tstModuleCJS.cts:1:8:3:1 | [FunctionDeclStmt] functio ... 'b'; } | tstModuleCJS.cts:1:33:1:41 | [UnionTypeExpr] 'a' \| 'b' | semmle.order | 4 | +| tstModuleCJS.cts:1:8:3:1 | [FunctionDeclStmt] functio ... 'b'; } | tstModuleCJS.cts:1:43:3:1 | [BlockStmt] { r ... 'b'; } | semmle.label | 5 | +| tstModuleCJS.cts:1:8:3:1 | [FunctionDeclStmt] functio ... 'b'; } | tstModuleCJS.cts:1:43:3:1 | [BlockStmt] { r ... 'b'; } | semmle.order | 5 | +| tstModuleCJS.cts:1:33:1:41 | [UnionTypeExpr] 'a' \| 'b' | tstModuleCJS.cts:1:33:1:35 | [LiteralTypeExpr] 'a' | semmle.label | 1 | +| tstModuleCJS.cts:1:33:1:41 | [UnionTypeExpr] 'a' \| 'b' | tstModuleCJS.cts:1:33:1:35 | [LiteralTypeExpr] 'a' | semmle.order | 1 | +| tstModuleCJS.cts:1:33:1:41 | [UnionTypeExpr] 'a' \| 'b' | tstModuleCJS.cts:1:39:1:41 | [LiteralTypeExpr] 'b' | semmle.label | 2 | +| tstModuleCJS.cts:1:33:1:41 | [UnionTypeExpr] 'a' \| 'b' | tstModuleCJS.cts:1:39:1:41 | [LiteralTypeExpr] 'b' | semmle.order | 2 | +| tstModuleCJS.cts:1:43:3:1 | [BlockStmt] { r ... 'b'; } | tstModuleCJS.cts:2:5:2:43 | [ReturnStmt] return ... : 'b'; | semmle.label | 1 | +| tstModuleCJS.cts:1:43:3:1 | [BlockStmt] { r ... 'b'; } | tstModuleCJS.cts:2:5:2:43 | [ReturnStmt] return ... : 'b'; | semmle.order | 1 | +| tstModuleCJS.cts:2:5:2:43 | [ReturnStmt] return ... : 'b'; | tstModuleCJS.cts:2:12:2:42 | [ConditionalExpr] Math.ra ... ' : 'b' | semmle.label | 1 | +| tstModuleCJS.cts:2:5:2:43 | [ReturnStmt] return ... : 'b'; | tstModuleCJS.cts:2:12:2:42 | [ConditionalExpr] Math.ra ... ' : 'b' | semmle.order | 1 | +| tstModuleCJS.cts:2:12:2:22 | [DotExpr] Math.random | tstModuleCJS.cts:2:12:2:15 | [VarRef] Math | semmle.label | 1 | +| tstModuleCJS.cts:2:12:2:22 | [DotExpr] Math.random | tstModuleCJS.cts:2:12:2:15 | [VarRef] Math | semmle.order | 1 | +| tstModuleCJS.cts:2:12:2:22 | [DotExpr] Math.random | tstModuleCJS.cts:2:17:2:22 | [Label] random | semmle.label | 2 | +| tstModuleCJS.cts:2:12:2:22 | [DotExpr] Math.random | tstModuleCJS.cts:2:17:2:22 | [Label] random | semmle.order | 2 | +| tstModuleCJS.cts:2:12:2:24 | [MethodCallExpr] Math.random() | tstModuleCJS.cts:2:12:2:22 | [DotExpr] Math.random | semmle.label | 0 | +| tstModuleCJS.cts:2:12:2:24 | [MethodCallExpr] Math.random() | tstModuleCJS.cts:2:12:2:22 | [DotExpr] Math.random | semmle.order | 0 | +| tstModuleCJS.cts:2:12:2:30 | [BinaryExpr] Math.random() > 0.5 | tstModuleCJS.cts:2:12:2:24 | [MethodCallExpr] Math.random() | semmle.label | 1 | +| tstModuleCJS.cts:2:12:2:30 | [BinaryExpr] Math.random() > 0.5 | tstModuleCJS.cts:2:12:2:24 | [MethodCallExpr] Math.random() | semmle.order | 1 | +| tstModuleCJS.cts:2:12:2:30 | [BinaryExpr] Math.random() > 0.5 | tstModuleCJS.cts:2:28:2:30 | [Literal] 0.5 | semmle.label | 2 | +| tstModuleCJS.cts:2:12:2:30 | [BinaryExpr] Math.random() > 0.5 | tstModuleCJS.cts:2:28:2:30 | [Literal] 0.5 | semmle.order | 2 | +| tstModuleCJS.cts:2:12:2:42 | [ConditionalExpr] Math.ra ... ' : 'b' | tstModuleCJS.cts:2:12:2:30 | [BinaryExpr] Math.random() > 0.5 | semmle.label | 1 | +| tstModuleCJS.cts:2:12:2:42 | [ConditionalExpr] Math.ra ... ' : 'b' | tstModuleCJS.cts:2:12:2:30 | [BinaryExpr] Math.random() > 0.5 | semmle.order | 1 | +| tstModuleCJS.cts:2:12:2:42 | [ConditionalExpr] Math.ra ... ' : 'b' | tstModuleCJS.cts:2:34:2:36 | [Literal] 'a' | semmle.label | 2 | +| tstModuleCJS.cts:2:12:2:42 | [ConditionalExpr] Math.ra ... ' : 'b' | tstModuleCJS.cts:2:34:2:36 | [Literal] 'a' | semmle.order | 2 | +| tstModuleCJS.cts:2:12:2:42 | [ConditionalExpr] Math.ra ... ' : 'b' | tstModuleCJS.cts:2:40:2:42 | [Literal] 'b' | semmle.label | 3 | +| tstModuleCJS.cts:2:12:2:42 | [ConditionalExpr] Math.ra ... ' : 'b' | tstModuleCJS.cts:2:40:2:42 | [Literal] 'b' | semmle.order | 3 | +| tstModuleES.mts:1:1:3:1 | [ExportDeclaration] export ... 'b'; } | tstModuleES.mts:1:16:3:1 | [FunctionDeclStmt] functio ... 'b'; } | semmle.label | 1 | +| tstModuleES.mts:1:1:3:1 | [ExportDeclaration] export ... 'b'; } | tstModuleES.mts:1:16:3:1 | [FunctionDeclStmt] functio ... 'b'; } | semmle.order | 1 | +| tstModuleES.mts:1:16:3:1 | [FunctionDeclStmt] functio ... 'b'; } | tstModuleES.mts:1:25:1:35 | [VarDecl] tstModuleES | semmle.label | 0 | +| tstModuleES.mts:1:16:3:1 | [FunctionDeclStmt] functio ... 'b'; } | tstModuleES.mts:1:25:1:35 | [VarDecl] tstModuleES | semmle.order | 0 | +| tstModuleES.mts:1:16:3:1 | [FunctionDeclStmt] functio ... 'b'; } | tstModuleES.mts:1:40:1:48 | [UnionTypeExpr] 'a' \| 'b' | semmle.label | 4 | +| tstModuleES.mts:1:16:3:1 | [FunctionDeclStmt] functio ... 'b'; } | tstModuleES.mts:1:40:1:48 | [UnionTypeExpr] 'a' \| 'b' | semmle.order | 4 | +| tstModuleES.mts:1:16:3:1 | [FunctionDeclStmt] functio ... 'b'; } | tstModuleES.mts:1:50:3:1 | [BlockStmt] { r ... 'b'; } | semmle.label | 5 | +| tstModuleES.mts:1:16:3:1 | [FunctionDeclStmt] functio ... 'b'; } | tstModuleES.mts:1:50:3:1 | [BlockStmt] { r ... 'b'; } | semmle.order | 5 | +| tstModuleES.mts:1:40:1:48 | [UnionTypeExpr] 'a' \| 'b' | tstModuleES.mts:1:40:1:42 | [LiteralTypeExpr] 'a' | semmle.label | 1 | +| tstModuleES.mts:1:40:1:48 | [UnionTypeExpr] 'a' \| 'b' | tstModuleES.mts:1:40:1:42 | [LiteralTypeExpr] 'a' | semmle.order | 1 | +| tstModuleES.mts:1:40:1:48 | [UnionTypeExpr] 'a' \| 'b' | tstModuleES.mts:1:46:1:48 | [LiteralTypeExpr] 'b' | semmle.label | 2 | +| tstModuleES.mts:1:40:1:48 | [UnionTypeExpr] 'a' \| 'b' | tstModuleES.mts:1:46:1:48 | [LiteralTypeExpr] 'b' | semmle.order | 2 | +| tstModuleES.mts:1:50:3:1 | [BlockStmt] { r ... 'b'; } | tstModuleES.mts:2:5:2:43 | [ReturnStmt] return ... : 'b'; | semmle.label | 1 | +| tstModuleES.mts:1:50:3:1 | [BlockStmt] { r ... 'b'; } | tstModuleES.mts:2:5:2:43 | [ReturnStmt] return ... : 'b'; | semmle.order | 1 | +| tstModuleES.mts:2:5:2:43 | [ReturnStmt] return ... : 'b'; | tstModuleES.mts:2:12:2:42 | [ConditionalExpr] Math.ra ... ' : 'b' | semmle.label | 1 | +| tstModuleES.mts:2:5:2:43 | [ReturnStmt] return ... : 'b'; | tstModuleES.mts:2:12:2:42 | [ConditionalExpr] Math.ra ... ' : 'b' | semmle.order | 1 | +| tstModuleES.mts:2:12:2:22 | [DotExpr] Math.random | tstModuleES.mts:2:12:2:15 | [VarRef] Math | semmle.label | 1 | +| tstModuleES.mts:2:12:2:22 | [DotExpr] Math.random | tstModuleES.mts:2:12:2:15 | [VarRef] Math | semmle.order | 1 | +| tstModuleES.mts:2:12:2:22 | [DotExpr] Math.random | tstModuleES.mts:2:17:2:22 | [Label] random | semmle.label | 2 | +| tstModuleES.mts:2:12:2:22 | [DotExpr] Math.random | tstModuleES.mts:2:17:2:22 | [Label] random | semmle.order | 2 | +| tstModuleES.mts:2:12:2:24 | [MethodCallExpr] Math.random() | tstModuleES.mts:2:12:2:22 | [DotExpr] Math.random | semmle.label | 0 | +| tstModuleES.mts:2:12:2:24 | [MethodCallExpr] Math.random() | tstModuleES.mts:2:12:2:22 | [DotExpr] Math.random | semmle.order | 0 | +| tstModuleES.mts:2:12:2:30 | [BinaryExpr] Math.random() > 0.5 | tstModuleES.mts:2:12:2:24 | [MethodCallExpr] Math.random() | semmle.label | 1 | +| tstModuleES.mts:2:12:2:30 | [BinaryExpr] Math.random() > 0.5 | tstModuleES.mts:2:12:2:24 | [MethodCallExpr] Math.random() | semmle.order | 1 | +| tstModuleES.mts:2:12:2:30 | [BinaryExpr] Math.random() > 0.5 | tstModuleES.mts:2:28:2:30 | [Literal] 0.5 | semmle.label | 2 | +| tstModuleES.mts:2:12:2:30 | [BinaryExpr] Math.random() > 0.5 | tstModuleES.mts:2:28:2:30 | [Literal] 0.5 | semmle.order | 2 | +| tstModuleES.mts:2:12:2:42 | [ConditionalExpr] Math.ra ... ' : 'b' | tstModuleES.mts:2:12:2:30 | [BinaryExpr] Math.random() > 0.5 | semmle.label | 1 | +| tstModuleES.mts:2:12:2:42 | [ConditionalExpr] Math.ra ... ' : 'b' | tstModuleES.mts:2:12:2:30 | [BinaryExpr] Math.random() > 0.5 | semmle.order | 1 | +| tstModuleES.mts:2:12:2:42 | [ConditionalExpr] Math.ra ... ' : 'b' | tstModuleES.mts:2:34:2:36 | [Literal] 'a' | semmle.label | 2 | +| tstModuleES.mts:2:12:2:42 | [ConditionalExpr] Math.ra ... ' : 'b' | tstModuleES.mts:2:34:2:36 | [Literal] 'a' | semmle.order | 2 | +| tstModuleES.mts:2:12:2:42 | [ConditionalExpr] Math.ra ... ' : 'b' | tstModuleES.mts:2:40:2:42 | [Literal] 'b' | semmle.label | 3 | +| tstModuleES.mts:2:12:2:42 | [ConditionalExpr] Math.ra ... ' : 'b' | tstModuleES.mts:2:40:2:42 | [Literal] 'b' | semmle.order | 3 | +| tstSuffixA.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | tstSuffixA.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | semmle.label | 1 | +| tstSuffixA.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | tstSuffixA.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | semmle.order | 1 | +| tstSuffixA.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | tstSuffixA.ts:1:17:1:28 | [VarDecl] resolvedFile | semmle.label | 0 | +| tstSuffixA.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | tstSuffixA.ts:1:17:1:28 | [VarDecl] resolvedFile | semmle.order | 0 | +| tstSuffixA.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | tstSuffixA.ts:1:33:1:47 | [LiteralTypeExpr] 'tstSuffixA.ts' | semmle.label | 4 | +| tstSuffixA.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | tstSuffixA.ts:1:33:1:47 | [LiteralTypeExpr] 'tstSuffixA.ts' | semmle.order | 4 | +| tstSuffixA.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | tstSuffixA.ts:1:49:3:1 | [BlockStmt] { r ... .ts'; } | semmle.label | 5 | +| tstSuffixA.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | tstSuffixA.ts:1:49:3:1 | [BlockStmt] { r ... .ts'; } | semmle.order | 5 | +| tstSuffixA.ts:1:49:3:1 | [BlockStmt] { r ... .ts'; } | tstSuffixA.ts:2:5:2:27 | [ReturnStmt] return ... xA.ts'; | semmle.label | 1 | +| tstSuffixA.ts:1:49:3:1 | [BlockStmt] { r ... .ts'; } | tstSuffixA.ts:2:5:2:27 | [ReturnStmt] return ... xA.ts'; | semmle.order | 1 | +| tstSuffixA.ts:2:5:2:27 | [ReturnStmt] return ... xA.ts'; | tstSuffixA.ts:2:12:2:26 | [Literal] 'tstSuffixA.ts' | semmle.label | 1 | +| tstSuffixA.ts:2:5:2:27 | [ReturnStmt] return ... xA.ts'; | tstSuffixA.ts:2:12:2:26 | [Literal] 'tstSuffixA.ts' | semmle.order | 1 | +| tstSuffixB.ios.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | tstSuffixB.ios.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | semmle.label | 1 | +| tstSuffixB.ios.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | tstSuffixB.ios.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | semmle.order | 1 | +| tstSuffixB.ios.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | tstSuffixB.ios.ts:1:17:1:28 | [VarDecl] resolvedFile | semmle.label | 0 | +| tstSuffixB.ios.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | tstSuffixB.ios.ts:1:17:1:28 | [VarDecl] resolvedFile | semmle.order | 0 | +| tstSuffixB.ios.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | tstSuffixB.ios.ts:1:33:1:51 | [LiteralTypeExpr] 'tstSuffixB.ios.ts' | semmle.label | 4 | +| tstSuffixB.ios.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | tstSuffixB.ios.ts:1:33:1:51 | [LiteralTypeExpr] 'tstSuffixB.ios.ts' | semmle.order | 4 | +| tstSuffixB.ios.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | tstSuffixB.ios.ts:1:53:3:1 | [BlockStmt] { r ... .ts'; } | semmle.label | 5 | +| tstSuffixB.ios.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | tstSuffixB.ios.ts:1:53:3:1 | [BlockStmt] { r ... .ts'; } | semmle.order | 5 | +| tstSuffixB.ios.ts:1:53:3:1 | [BlockStmt] { r ... .ts'; } | tstSuffixB.ios.ts:2:5:2:31 | [ReturnStmt] return ... os.ts'; | semmle.label | 1 | +| tstSuffixB.ios.ts:1:53:3:1 | [BlockStmt] { r ... .ts'; } | tstSuffixB.ios.ts:2:5:2:31 | [ReturnStmt] return ... os.ts'; | semmle.order | 1 | +| tstSuffixB.ios.ts:2:5:2:31 | [ReturnStmt] return ... os.ts'; | tstSuffixB.ios.ts:2:12:2:30 | [Literal] 'tstSuffixB.ios.ts' | semmle.label | 1 | +| tstSuffixB.ios.ts:2:5:2:31 | [ReturnStmt] return ... os.ts'; | tstSuffixB.ios.ts:2:12:2:30 | [Literal] 'tstSuffixB.ios.ts' | semmle.order | 1 | +| tstSuffixB.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | tstSuffixB.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | semmle.label | 1 | +| tstSuffixB.ts:1:1:3:1 | [ExportDeclaration] export ... .ts'; } | tstSuffixB.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | semmle.order | 1 | +| tstSuffixB.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | tstSuffixB.ts:1:17:1:28 | [VarDecl] resolvedFile | semmle.label | 0 | +| tstSuffixB.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | tstSuffixB.ts:1:17:1:28 | [VarDecl] resolvedFile | semmle.order | 0 | +| tstSuffixB.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | tstSuffixB.ts:1:33:1:47 | [LiteralTypeExpr] 'tstSuffixB.ts' | semmle.label | 4 | +| tstSuffixB.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | tstSuffixB.ts:1:33:1:47 | [LiteralTypeExpr] 'tstSuffixB.ts' | semmle.order | 4 | +| tstSuffixB.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | tstSuffixB.ts:1:49:3:1 | [BlockStmt] { r ... .ts'; } | semmle.label | 5 | +| tstSuffixB.ts:1:8:3:1 | [FunctionDeclStmt] functio ... .ts'; } | tstSuffixB.ts:1:49:3:1 | [BlockStmt] { r ... .ts'; } | semmle.order | 5 | +| tstSuffixB.ts:1:49:3:1 | [BlockStmt] { r ... .ts'; } | tstSuffixB.ts:2:5:2:27 | [ReturnStmt] return ... xB.ts'; | semmle.label | 1 | +| tstSuffixB.ts:1:49:3:1 | [BlockStmt] { r ... .ts'; } | tstSuffixB.ts:2:5:2:27 | [ReturnStmt] return ... xB.ts'; | semmle.order | 1 | +| tstSuffixB.ts:2:5:2:27 | [ReturnStmt] return ... xB.ts'; | tstSuffixB.ts:2:12:2:26 | [Literal] 'tstSuffixB.ts' | semmle.label | 1 | +| tstSuffixB.ts:2:5:2:27 | [ReturnStmt] return ... xB.ts'; | tstSuffixB.ts:2:12:2:26 | [Literal] 'tstSuffixB.ts' | semmle.order | 1 | | type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | type_alias.ts:1:6:1:6 | [Identifier] B | semmle.label | 1 | | type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | type_alias.ts:1:6:1:6 | [Identifier] B | semmle.order | 1 | | type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | type_alias.ts:1:10:1:16 | [KeywordTypeExpr] boolean | semmle.label | 2 | diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tests.expected b/javascript/ql/test/library-tests/TypeScript/Types/tests.expected index 9a0585b9436..ce517db8d6d 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/tests.expected +++ b/javascript/ql/test/library-tests/TypeScript/Types/tests.expected @@ -465,23 +465,43 @@ getExprType | tst.ts:352:18:352:26 | state.get | () => number | | tst.ts:352:18:352:28 | state.get() | number | | tst.ts:352:24:352:26 | get | () => number | -| tst.ts:377:8:377:18 | tstModuleES | () => "a" \| "b" | -| tst.ts:377:25:377:43 | './tstModuleES.mjs' | any | -| tst.ts:379:1:379:7 | console | Console | -| tst.ts:379:1:379:11 | console.log | (...data: any[]) => void | -| tst.ts:379:1:379:26 | console ... leES()) | void | -| tst.ts:379:9:379:11 | log | (...data: any[]) => void | -| tst.ts:379:13:379:23 | tstModuleES | () => "a" \| "b" | -| tst.ts:379:13:379:25 | tstModuleES() | "a" \| "b" | -| tst.ts:381:10:381:21 | tstModuleCJS | () => "a" \| "b" | -| tst.ts:381:10:381:21 | tstModuleCJS | () => "a" \| "b" | -| tst.ts:381:30:381:49 | './tstModuleCJS.cjs' | any | -| tst.ts:383:1:383:7 | console | Console | -| tst.ts:383:1:383:11 | console.log | (...data: any[]) => void | -| tst.ts:383:1:383:27 | console ... eCJS()) | void | -| tst.ts:383:9:383:11 | log | (...data: any[]) => void | -| tst.ts:383:13:383:24 | tstModuleCJS | () => "a" \| "b" | -| tst.ts:383:13:383:26 | tstModuleCJS() | "a" \| "b" | +| tst.ts:356:8:356:18 | tstModuleES | () => "a" \| "b" | +| tst.ts:356:25:356:43 | './tstModuleES.mjs' | any | +| tst.ts:358:1:358:7 | console | Console | +| tst.ts:358:1:358:11 | console.log | (...data: any[]) => void | +| tst.ts:358:1:358:26 | console ... leES()) | void | +| tst.ts:358:9:358:11 | log | (...data: any[]) => void | +| tst.ts:358:13:358:23 | tstModuleES | () => "a" \| "b" | +| tst.ts:358:13:358:25 | tstModuleES() | "a" \| "b" | +| tst.ts:360:10:360:21 | tstModuleCJS | () => "a" \| "b" | +| tst.ts:360:10:360:21 | tstModuleCJS | () => "a" \| "b" | +| tst.ts:360:30:360:49 | './tstModuleCJS.cjs' | any | +| tst.ts:362:1:362:7 | console | Console | +| tst.ts:362:1:362:11 | console.log | (...data: any[]) => void | +| tst.ts:362:1:362:27 | console ... eCJS()) | void | +| tst.ts:362:9:362:11 | log | (...data: any[]) => void | +| tst.ts:362:13:362:24 | tstModuleCJS | () => "a" \| "b" | +| tst.ts:362:13:362:26 | tstModuleCJS() | "a" \| "b" | +| tst.ts:368:13:368:13 | A | typeof library-tests/TypeScript/Types/tstSuffixA.ts | +| tst.ts:368:20:368:33 | './tstSuffixA' | any | +| tst.ts:370:1:370:7 | console | Console | +| tst.ts:370:1:370:11 | console.log | (...data: any[]) => void | +| tst.ts:370:1:370:29 | console ... File()) | void | +| tst.ts:370:9:370:11 | log | (...data: any[]) => void | +| tst.ts:370:13:370:13 | A | typeof library-tests/TypeScript/Types/tstSuffixA.ts | +| tst.ts:370:13:370:26 | A.resolvedFile | () => "tstSuffixA.ts" | +| tst.ts:370:13:370:28 | A.resolvedFile() | "tstSuffixA.ts" | +| tst.ts:370:15:370:26 | resolvedFile | () => "tstSuffixA.ts" | +| tst.ts:372:13:372:13 | B | typeof library-tests/TypeScript/Types/tstSuffixB.ios.ts | +| tst.ts:372:20:372:33 | './tstSuffixB' | any | +| tst.ts:374:1:374:7 | console | Console | +| tst.ts:374:1:374:11 | console.log | (...data: any[]) => void | +| tst.ts:374:1:374:29 | console ... File()) | void | +| tst.ts:374:9:374:11 | log | (...data: any[]) => void | +| tst.ts:374:13:374:13 | B | typeof library-tests/TypeScript/Types/tstSuffixB.ios.ts | +| tst.ts:374:13:374:26 | B.resolvedFile | () => "tstSuffixB.ios.ts" | +| tst.ts:374:13:374:28 | B.resolvedFile() | "tstSuffixB.ios.ts" | +| tst.ts:374:15:374:26 | resolvedFile | () => "tstSuffixB.ios.ts" | | 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 | @@ -502,6 +522,12 @@ getExprType | tstModuleES.mts:2:28:2:30 | 0.5 | 0.5 | | tstModuleES.mts:2:34:2:36 | 'a' | "a" | | tstModuleES.mts:2:40:2:42 | 'b' | "b" | +| tstSuffixA.ts:1:17:1:28 | resolvedFile | () => "tstSuffixA.ts" | +| tstSuffixA.ts:2:12:2:26 | 'tstSuffixA.ts' | "tstSuffixA.ts" | +| tstSuffixB.ios.ts:1:17:1:28 | resolvedFile | () => "tstSuffixB.ios.ts" | +| tstSuffixB.ios.ts:2:12:2:30 | 'tstSuffixB.ios.ts' | "tstSuffixB.ios.ts" | +| tstSuffixB.ts:1:17:1:28 | resolvedFile | () => "tstSuffixB.ts" | +| tstSuffixB.ts:2:12:2:26 | 'tstSuffixB.ts' | "tstSuffixB.ts" | | type_alias.ts:3:5:3:5 | b | boolean | | type_alias.ts:7:5:7:5 | c | ValueOrArray | | type_alias.ts:14:9:14:32 | [proper ... ]: Json | any | @@ -861,6 +887,9 @@ getTypeExprType | tstModuleES.mts:1:40:1:42 | 'a' | "a" | | tstModuleES.mts:1:40:1:48 | 'a' \| 'b' | "a" \| "b" | | tstModuleES.mts:1:46:1:48 | 'b' | "b" | +| tstSuffixA.ts:1:33:1:47 | 'tstSuffixA.ts' | "tstSuffixA.ts" | +| tstSuffixB.ios.ts:1:33:1:51 | 'tstSuffixB.ios.ts' | "tstSuffixB.ios.ts" | +| tstSuffixB.ts:1:33:1:47 | 'tstSuffixB.ts' | "tstSuffixB.ts" | | type_alias.ts:1:6:1:6 | B | boolean | | type_alias.ts:1:10:1:16 | boolean | boolean | | type_alias.ts:3:8:3:8 | B | boolean | From d199173923ad428e24a926f7ed6f4e424d40e98c Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 25 May 2022 16:10:13 +0000 Subject: [PATCH 090/125] add a getAPrimaryQlClass predicate to ExpressionWithTypeArguments --- .../ql/lib/semmle/javascript/TypeScript.qll | 2 ++ .../TypeScript/Types/printAst.expected | 18 +++++++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/TypeScript.qll b/javascript/ql/lib/semmle/javascript/TypeScript.qll index 70a1ed98830..2a0ce9464fb 100644 --- a/javascript/ql/lib/semmle/javascript/TypeScript.qll +++ b/javascript/ql/lib/semmle/javascript/TypeScript.qll @@ -1286,6 +1286,8 @@ class ExpressionWithTypeArguments extends @expression_with_type_arguments, Expr override ControlFlowNode getFirstControlFlowNode() { result = this.getExpression().getFirstControlFlowNode() } + + override string getAPrimaryQlClass() { result = "ExpressionWithTypeArguments" } } /** diff --git a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected index bbe3148efb3..9f4f6c6aba5 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected +++ b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected @@ -1239,7 +1239,7 @@ nodes | tst.ts:325:7:325:14 | [VarDecl] ErrorMap | semmle.label | [VarDecl] ErrorMap | | tst.ts:325:7:325:35 | [VariableDeclarator] ErrorMa ... Error> | semmle.label | [VariableDeclarator] ErrorMa ... Error> | | tst.ts:325:18:325:20 | [VarRef] Map | semmle.label | [VarRef] Map | -| tst.ts:325:18:325:35 | [???] Map | semmle.label | [???] Map | +| tst.ts:325:18:325:35 | [ExpressionWithTypeArguments] Map | semmle.label | [ExpressionWithTypeArguments] Map | | tst.ts:325:22:325:27 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:325:30:325:34 | [LocalTypeAccess] Error | semmle.label | [LocalTypeAccess] Error | | tst.ts:327:1:327:32 | [DeclStmt] const errorMap = ... | semmle.label | [DeclStmt] const errorMap = ... | @@ -3852,14 +3852,14 @@ edges | tst.ts:325:1:325:36 | [DeclStmt] const ErrorMap = ... | tst.ts:325:7:325:35 | [VariableDeclarator] ErrorMa ... Error> | semmle.order | 1 | | tst.ts:325:7:325:35 | [VariableDeclarator] ErrorMa ... Error> | tst.ts:325:7:325:14 | [VarDecl] ErrorMap | semmle.label | 1 | | tst.ts:325:7:325:35 | [VariableDeclarator] ErrorMa ... Error> | tst.ts:325:7:325:14 | [VarDecl] ErrorMap | semmle.order | 1 | -| tst.ts:325:7:325:35 | [VariableDeclarator] ErrorMa ... Error> | tst.ts:325:18:325:35 | [???] Map | semmle.label | 2 | -| tst.ts:325:7:325:35 | [VariableDeclarator] ErrorMa ... Error> | tst.ts:325:18:325:35 | [???] Map | semmle.order | 2 | -| tst.ts:325:18:325:35 | [???] Map | tst.ts:325:18:325:20 | [VarRef] Map | semmle.label | 1 | -| tst.ts:325:18:325:35 | [???] Map | tst.ts:325:18:325:20 | [VarRef] Map | semmle.order | 1 | -| tst.ts:325:18:325:35 | [???] Map | tst.ts:325:22:325:27 | [KeywordTypeExpr] string | semmle.label | 2 | -| tst.ts:325:18:325:35 | [???] Map | tst.ts:325:22:325:27 | [KeywordTypeExpr] string | semmle.order | 2 | -| tst.ts:325:18:325:35 | [???] Map | tst.ts:325:30:325:34 | [LocalTypeAccess] Error | semmle.label | 3 | -| tst.ts:325:18:325:35 | [???] Map | tst.ts:325:30:325:34 | [LocalTypeAccess] Error | semmle.order | 3 | +| tst.ts:325:7:325:35 | [VariableDeclarator] ErrorMa ... Error> | tst.ts:325:18:325:35 | [ExpressionWithTypeArguments] Map | semmle.label | 2 | +| tst.ts:325:7:325:35 | [VariableDeclarator] ErrorMa ... Error> | tst.ts:325:18:325:35 | [ExpressionWithTypeArguments] Map | semmle.order | 2 | +| tst.ts:325:18:325:35 | [ExpressionWithTypeArguments] Map | tst.ts:325:18:325:20 | [VarRef] Map | semmle.label | 1 | +| tst.ts:325:18:325:35 | [ExpressionWithTypeArguments] Map | tst.ts:325:18:325:20 | [VarRef] Map | semmle.order | 1 | +| tst.ts:325:18:325:35 | [ExpressionWithTypeArguments] Map | tst.ts:325:22:325:27 | [KeywordTypeExpr] string | semmle.label | 2 | +| tst.ts:325:18:325:35 | [ExpressionWithTypeArguments] Map | tst.ts:325:22:325:27 | [KeywordTypeExpr] string | semmle.order | 2 | +| tst.ts:325:18:325:35 | [ExpressionWithTypeArguments] Map | tst.ts:325:30:325:34 | [LocalTypeAccess] Error | semmle.label | 3 | +| tst.ts:325:18:325:35 | [ExpressionWithTypeArguments] Map | tst.ts:325:30:325:34 | [LocalTypeAccess] Error | semmle.order | 3 | | tst.ts:327:1:327:32 | [DeclStmt] const errorMap = ... | tst.ts:327:7:327:31 | [VariableDeclarator] errorMa ... orMap() | semmle.label | 1 | | tst.ts:327:1:327:32 | [DeclStmt] const errorMap = ... | tst.ts:327:7:327:31 | [VariableDeclarator] errorMa ... orMap() | semmle.order | 1 | | tst.ts:327:7:327:31 | [VariableDeclarator] errorMa ... orMap() | tst.ts:327:7:327:14 | [VarDecl] errorMap | semmle.label | 1 | From d326b3a91c67b386c61d7c06943b275d3418b12b Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 25 May 2022 18:25:58 +0000 Subject: [PATCH 091/125] Swift: global dataflow WIP --- swift/ql/lib/codeql/swift/dataflow/Ssa.qll | 6 +++ .../dataflow/internal/DataFlowDispatch.qll | 49 +++++++++---------- .../dataflow/internal/DataFlowPrivate.qll | 23 ++++++++- .../dataflow/dataflow/DataFlow.ql | 28 +++++++++++ 4 files changed, 77 insertions(+), 29 deletions(-) create mode 100644 swift/ql/test/library-tests/dataflow/dataflow/DataFlow.ql diff --git a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll index 9053428d4b0..fe5478014be 100644 --- a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll +++ b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll @@ -64,6 +64,12 @@ module Ssa { a = bb.getNode(i).getNode().asAstNode() and value.getNode().asAstNode() = a.getSource() ) + or + exists(VarDecl var, BasicBlock bb, int blockIndex, PatternBindingDecl pbd | + this.definesAt(var, bb, blockIndex) and + pbd.getAPattern() = bb.getNode(blockIndex).getNode() and + value.getNode() = var.getParentInitializer() + ) } } } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll index 0fe0acad457..5b47f41c46c 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll @@ -1,5 +1,6 @@ private import swift private import DataFlowPrivate +private import DataFlowPublic newtype TReturnKind = TNormalReturnKind() @@ -42,47 +43,35 @@ class DataFlowCallable extends TDataFlowCallable { * A call. This includes calls from source code, as well as call(back)s * inside library callables with a flow summary. */ -class DataFlowCall extends TDataFlowCall { +class DataFlowCall extends ExprNode { + DataFlowCall() { + this.asExpr() instanceof CallExpr + } + /** Gets the enclosing callable. */ DataFlowCallable getEnclosingCallable() { none() } - - /** Gets a textual representation of this call. */ - string toString() { none() } - - /** Gets the location of this call. */ - Location getLocation() { none() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } } cached private module Cached { cached - newtype TDataFlowCallable = TODO_TDataFlowCallable() - - cached - newtype TDataFlowCall = TODO_TDataFlowCall() + newtype TDataFlowCallable = TDataFlowFunc(FuncDecl func) /** Gets a viable run-time target for the call `call`. */ cached - DataFlowCallable viableCallable(DataFlowCall call) { none() } + DataFlowCallable viableCallable(DataFlowCall call) { + result = TDataFlowFunc(call.asExpr().(CallExpr).getStaticTarget()) + } cached - newtype TArgumentPosition = TODO_TArgumentPosition() + newtype TArgumentPosition = + TThisArgument() or + TPositionalArgument(int n) { n in [0 .. 100] } // we rely on default exprs generated in the caller for ordering. TODO: compute range properly. TODO: varargs? cached - newtype TParameterPosition = TODO_TParameterPosition() + newtype TParameterPosition = + TThisParameter() or + TPositionalParameter(int n) { n in [0 .. 100] } // TODO: compute range properly } import Cached @@ -111,6 +100,12 @@ class ArgumentPosition extends TArgumentPosition { string toString() { none() } } +class PositionalArgumentPosition extends ArgumentPosition, TPositionalArgument { + int getIndex() { + this = TPositionalArgument(result) + } +} + /** Holds if arguments at position `apos` match parameters at position `ppos`. */ pragma[inline] predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { none() } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index fc3321d2263..d60a7bc471a 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -31,12 +31,18 @@ private class ExprNodeImpl extends ExprNode, NodeImpl { override Location getLocationImpl() { result = expr.getLocation() } override string toStringImpl() { result = expr.toString() } + + override DataFlowCallable getEnclosingCallable() { result = TDataFlowFunc(expr.getScope()) } } private class SsaDefinitionNodeImpl extends SsaDefinitionNode, NodeImpl { override Location getLocationImpl() { result = def.getLocation() } override string toStringImpl() { result = def.toString() } + + override DataFlowCallable getEnclosingCallable() { + result = TDataFlowFunc(def.getBasicBlock().getScope()) + } } /** A collection of cached types and predicates to be evaluated in the same stage. */ @@ -103,7 +109,11 @@ private module ParameterNodes { override string toStringImpl() { result = param.toString() } override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { - none() // TODO + exists(FuncDecl f, int index | + c = TDataFlowFunc(f) and + f.getParam(index) = param and + pos = TPositionalParameter(index) + ) } } } @@ -119,7 +129,16 @@ abstract class ArgumentNode extends Node { final DataFlowCall getCall() { this.argumentOf(result, _) } } -private module ArgumentNodes { } +private module ArgumentNodes { + class NormalArgumentNode extends ExprNode, ArgumentNode { + NormalArgumentNode() { exists(CallExpr call | call.getAnArgument().getExpr() = this.asExpr()) } + + override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { + call.asExpr().(CallExpr).getArgument(pos.(PositionalArgumentPosition).getIndex()).getExpr() = + this.asExpr() + } + } +} import ArgumentNodes diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.ql b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.ql new file mode 100644 index 00000000000..d2291e1636f --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.ql @@ -0,0 +1,28 @@ +import swift +import codeql.swift.dataflow.DataFlow +import DataFlow::PathGraph + +class TestConfiguration extends DataFlow::Configuration { + TestConfiguration() { this = "TestConfiguration" } + + override predicate isSource(DataFlow::Node src) { + src.asExpr().(CallExpr).getStaticTarget().getName() = "source" + } + + override predicate isSink(DataFlow::Node sink) { + exists(CallExpr sinkCall | + sinkCall.getStaticTarget().getName() = "sink" and + sinkCall.getAnArgument() = sink.asExpr() + ) + } + + override int explorationLimit() { result = 100 } +} + +from DataFlow::PartialPathNode src, DataFlow::PartialPathNode sink, TestConfiguration test +where + //test.isSource(src) and + // test.isSink(sink) and + //DataFlow::localFlow(src, sink) + test.hasPartialFlow(src, sink, _) +select src, sink From 9f64622f3121d2212c3f60767718154ea895fe80 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 25 May 2022 19:18:43 +0000 Subject: [PATCH 092/125] Swift: data flow configurations working --- swift/ql/lib/codeql/swift/dataflow/Ssa.qll | 4 ++-- .../swift/dataflow/internal/DataFlowPrivate.qll | 4 +++- .../swift/dataflow/internal/DataFlowPublic.qll | 2 +- .../dataflow/dataflow/DataFlow.expected | 14 ++++++++++++++ .../library-tests/dataflow/dataflow/DataFlow.ql | 9 +++------ .../dataflow/dataflow/LocalFlow.expected | 1 + 6 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected diff --git a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll index fe5478014be..b7a84caca28 100644 --- a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll +++ b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll @@ -67,8 +67,8 @@ module Ssa { or exists(VarDecl var, BasicBlock bb, int blockIndex, PatternBindingDecl pbd | this.definesAt(var, bb, blockIndex) and - pbd.getAPattern() = bb.getNode(blockIndex).getNode() and - value.getNode() = var.getParentInitializer() + pbd.getAPattern() = bb.getNode(blockIndex).getNode().asAstNode() and + value.getNode().asAstNode() = var.getParentInitializer() ) } } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index d60a7bc471a..1902400bff3 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -188,7 +188,9 @@ class DataFlowType extends TDataFlowType { } /** Gets the type of `n` used for type pruning. */ -DataFlowType getNodeType(NodeImpl n) { none() } +DataFlowType getNodeType(NodeImpl n) { + any() // return the singleton DataFlowType until we support type pruning for Swift +} /** Gets a string representation of a `DataFlowType`. */ string ppReprType(DataFlowType t) { result = t.toString() } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll index 70afd2651e1..815b967e094 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll @@ -98,7 +98,7 @@ class PostUpdateNode extends Node instanceof PostUpdateNodeImpl { } /** Gets a node corresponding to expression `e`. */ -ExprNode exprNode(DataFlowExpr e) { none() } +ExprNode exprNode(DataFlowExpr e) { result.asExpr() = e } /** * Gets the node corresponding to the value of parameter `p` at function entry. diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected new file mode 100644 index 00000000000..e531105365d --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected @@ -0,0 +1,14 @@ +edges +| test.swift:6:19:6:26 | CallExpr : | test.swift:7:15:7:15 | DeclRefExpr | +| test.swift:6:19:6:26 | CallExpr : | test.swift:9:15:9:15 | DeclRefExpr | +| test.swift:6:19:6:26 | CallExpr : | test.swift:10:15:10:15 | DeclRefExpr | +nodes +| test.swift:6:19:6:26 | CallExpr : | semmle.label | CallExpr : | +| test.swift:7:15:7:15 | DeclRefExpr | semmle.label | DeclRefExpr | +| test.swift:9:15:9:15 | DeclRefExpr | semmle.label | DeclRefExpr | +| test.swift:10:15:10:15 | DeclRefExpr | semmle.label | DeclRefExpr | +subpaths +#select +| test.swift:6:19:6:26 | CallExpr : | test.swift:7:15:7:15 | DeclRefExpr | +| test.swift:6:19:6:26 | CallExpr : | test.swift:9:15:9:15 | DeclRefExpr | +| test.swift:6:19:6:26 | CallExpr : | test.swift:10:15:10:15 | DeclRefExpr | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.ql b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.ql index d2291e1636f..3144222a652 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.ql +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.ql @@ -12,17 +12,14 @@ class TestConfiguration extends DataFlow::Configuration { override predicate isSink(DataFlow::Node sink) { exists(CallExpr sinkCall | sinkCall.getStaticTarget().getName() = "sink" and - sinkCall.getAnArgument() = sink.asExpr() + sinkCall.getAnArgument().getExpr() = sink.asExpr() ) } override int explorationLimit() { result = 100 } } -from DataFlow::PartialPathNode src, DataFlow::PartialPathNode sink, TestConfiguration test +from DataFlow::PathNode src, DataFlow::PathNode sink, TestConfiguration test where - //test.isSource(src) and - // test.isSink(sink) and - //DataFlow::localFlow(src, sink) - test.hasPartialFlow(src, sink, _) + test.hasFlowPath(src, sink) select src, sink diff --git a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected index 949f9ce1dc8..0169f82ab99 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected @@ -1,6 +1,7 @@ | file://:0:0:0:0 | Phi | test.swift:15:15:15:15 | DeclRefExpr | | file://:0:0:0:0 | Phi | test.swift:21:15:21:15 | DeclRefExpr | | test.swift:6:9:6:13 | WriteDef | test.swift:7:15:7:15 | DeclRefExpr | +| test.swift:6:19:6:26 | CallExpr | test.swift:6:9:6:13 | WriteDef | | test.swift:7:15:7:15 | DeclRefExpr | test.swift:8:10:8:10 | DeclRefExpr | | test.swift:8:5:8:10 | WriteDef | test.swift:10:15:10:15 | DeclRefExpr | | test.swift:8:10:8:10 | DeclRefExpr | test.swift:8:5:8:10 | WriteDef | From aa77ea6bef7f6730b4d49e9213f970963b1a2532 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 25 May 2022 19:24:15 +0000 Subject: [PATCH 093/125] Swift: minimal tests for interprocedural flow --- .../library-tests/dataflow/dataflow/test.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/swift/ql/test/library-tests/dataflow/dataflow/test.swift b/swift/ql/test/library-tests/dataflow/dataflow/test.swift index d20fecb54d7..ae1135fb5d4 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/test.swift +++ b/swift/ql/test/library-tests/dataflow/dataflow/test.swift @@ -20,3 +20,19 @@ func intraprocedural_with_local_flow() -> Void { } sink(arg: t1) } + +func caller_source() -> Void { + callee_sink(x: source()) +} + +func callee_sink(x: Int) -> Void { + sink(arg: x) +} + +func callee_source() -> Int { + return source() +} + +func caller_sink() -> Void { + sink(arg: callee_source()) +} From 25c8b8141c33c997387216982c4a94a86eec9a15 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 26 May 2022 16:48:24 +0000 Subject: [PATCH 094/125] Swift: add params to CFG --- .../internal/ControlFlowElements.qll | 11 + .../internal/ControlFlowGraphImpl.qll | 25 +- .../controlflow/graph/Cfg.expected | 417 ++++++++++++++++-- 3 files changed, 402 insertions(+), 51 deletions(-) diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowElements.qll b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowElements.qll index 2a12d688ac8..6536e438cc2 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowElements.qll +++ b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowElements.qll @@ -3,6 +3,7 @@ private import swift cached newtype TControlFlowElement = TAstElement(AstNode n) or + TFuncDeclElement(AbstractFunctionDecl func) { func.hasBody() } or TPropertyGetterElement(Decl accessor, Expr ref) { isPropertyGetterElement(accessor, ref) } or TPropertySetterElement(AccessorDecl accessor, AssignExpr assign) { isPropertySetterElement(accessor, assign) @@ -161,3 +162,13 @@ class PropertyObserverElement extends ControlFlowElement, TPropertyObserverEleme AssignExpr getAssignExpr() { result = assign } } + + +class FuncDeclElement extends ControlFlowElement, TFuncDeclElement { + AbstractFunctionDecl func; + FuncDeclElement() { this = TFuncDeclElement(func) } + + override string toString() { result = func.toString() } + + override Location getLocation() { result = func.getLocation() } +} \ No newline at end of file diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll index cc89ab2f836..44a7ea0c748 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll +++ b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll @@ -48,15 +48,15 @@ module CfgScope { private class BodyStmtCallableScope extends Range_ instanceof AbstractFunctionDecl { final override predicate entry(ControlFlowElement first) { - exists(Stmts::BraceStmtTree tree | - tree.getAst() = super.getBody() and - tree.firstInner(first) + exists(Decls::FuncDeclTree tree | + tree.getAst() = this and + first = tree ) } final override predicate exit(ControlFlowElement last, Completion c) { - exists(Stmts::BraceStmtTree tree | - tree.getAst() = super.getBody() and + exists(Decls::FuncDeclTree tree | + tree.getAst() = this and tree.last(last, c) ) } @@ -884,6 +884,21 @@ module Decls { ) } } + + class FuncDeclTree extends StandardPreOrderTree, TFuncDeclElement { + AbstractFunctionDecl ast; + + FuncDeclTree() { this = TFuncDeclElement(ast) } + + AbstractFunctionDecl getAst() { result = ast } + + final override ControlFlowElement getChildElement(int i) { + result.asAstNode() = ast.getParam(i) + or + result.asAstNode() = ast.getBody() and + i = ast.getNumberOfParams() + } + } } module Exprs { diff --git a/swift/ql/test/library-tests/controlflow/graph/Cfg.expected b/swift/ql/test/library-tests/controlflow/graph/Cfg.expected index 181ae94bf32..9df24871348 100644 --- a/swift/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/swift/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -1,7 +1,10 @@ cfg.swift: -# 5| enter ConcreteFuncDecl +# 5| ConcreteFuncDecl #-----| -> IntegerLiteralExpr +# 5| enter ConcreteFuncDecl +#-----| -> ConcreteFuncDecl + # 5| exit ConcreteFuncDecl # 5| exit ConcreteFuncDecl (normal) @@ -13,14 +16,20 @@ cfg.swift: # 5| IntegerLiteralExpr #-----| -> ReturnStmt +# 15| ConcreteFuncDecl +#-----| -> ParamDecl + # 15| enter ConcreteFuncDecl -#-----| -> DeclRefExpr +#-----| -> ConcreteFuncDecl # 15| exit ConcreteFuncDecl # 15| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 15| ParamDecl +#-----| -> DeclRefExpr + # 15| ReturnStmt #-----| return -> exit ConcreteFuncDecl (normal) @@ -43,14 +52,20 @@ cfg.swift: # 15| IntegerLiteralExpr #-----| -> BinaryExpr +# 17| ConcreteFuncDecl +#-----| -> ParamDecl + # 17| enter ConcreteFuncDecl -#-----| -> GuardStmt +#-----| -> ConcreteFuncDecl # 17| exit ConcreteFuncDecl # 17| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 17| ParamDecl +#-----| -> GuardStmt + # 18| GuardStmt #-----| -> DeclRefExpr @@ -155,14 +170,20 @@ cfg.swift: # 22| IntegerLiteralExpr #-----| -> BinaryExpr +# 26| ConcreteFuncDecl +#-----| -> ParamDecl + # 26| enter ConcreteFuncDecl -#-----| -> DoCatchStmt +#-----| -> ConcreteFuncDecl # 26| exit ConcreteFuncDecl # 26| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 26| ParamDecl +#-----| -> DoCatchStmt + # 27| DoCatchStmt #-----| -> DeclRefExpr @@ -343,28 +364,48 @@ cfg.swift: # 42| IntegerLiteralExpr #-----| -> ReturnStmt +# 45| ConcreteFuncDecl +#-----| -> ParamDecl + # 45| enter ConcreteFuncDecl -#-----| -> ClosureExpr +#-----| -> ConcreteFuncDecl # 45| exit ConcreteFuncDecl # 45| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 45| ParamDecl +#-----| -> ClosureExpr + # 46| ReturnStmt #-----| return -> exit ConcreteFuncDecl (normal) # 46| ClosureExpr #-----| -> ReturnStmt +# 51| ConcreteFuncDecl +#-----| -> ParamDecl + +# 51| enter ConcreteFuncDecl +#-----| -> ConcreteFuncDecl + +# 51| ParamDecl + +# 52| ConcreteFuncDecl +#-----| -> ParamDecl + # 52| enter ConcreteFuncDecl -#-----| -> DeclRefExpr +#-----| -> ConcreteFuncDecl # 52| exit ConcreteFuncDecl # 52| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 52| ParamDecl +#-----| -> DeclRefExpr + # 53| ReturnStmt #-----| return -> exit ConcreteFuncDecl (normal) @@ -387,23 +428,32 @@ cfg.swift: # 53| DeclRefExpr #-----| -> BinaryExpr +# 58| ConcreteFuncDecl +#-----| -> ParamDecl + # 58| enter ConcreteFuncDecl -#-----| -> ClosureExpr +#-----| -> ConcreteFuncDecl # 58| exit ConcreteFuncDecl # 58| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 58| ParamDecl +#-----| -> ClosureExpr + # 59| ReturnStmt #-----| return -> exit ConcreteFuncDecl (normal) # 59| ClosureExpr #-----| -> ReturnStmt -# 64| enter ConcreteFuncDecl +# 64| ConcreteFuncDecl #-----| -> NamedPattern +# 64| enter ConcreteFuncDecl +#-----| -> ConcreteFuncDecl + # 64| exit ConcreteFuncDecl # 64| exit ConcreteFuncDecl (normal) @@ -481,14 +531,20 @@ cfg.swift: # 67| IntegerLiteralExpr #-----| -> CallExpr +# 70| ConcreteFuncDecl +#-----| -> ParamDecl + # 70| enter ConcreteFuncDecl -#-----| -> NamedPattern +#-----| -> ConcreteFuncDecl # 70| exit ConcreteFuncDecl # 70| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 70| ParamDecl +#-----| -> NamedPattern + # 71| PatternBindingDecl #-----| -> ConcreteVarDecl @@ -525,9 +581,12 @@ cfg.swift: # 72| LoadExpr #-----| -> ReturnStmt -# 75| enter ConcreteFuncDecl +# 75| ConcreteFuncDecl #-----| -> NamedPattern +# 75| enter ConcreteFuncDecl +#-----| -> ConcreteFuncDecl + # 75| exit ConcreteFuncDecl # 75| exit ConcreteFuncDecl (normal) @@ -608,9 +667,12 @@ cfg.swift: # 78| ForceValueExpr #-----| -> BinaryExpr -# 81| enter ConcreteFuncDecl +# 81| ConcreteFuncDecl #-----| -> NamedPattern +# 81| enter ConcreteFuncDecl +#-----| -> ConcreteFuncDecl + # 82| PatternBindingDecl #-----| -> ConcreteVarDecl @@ -622,14 +684,20 @@ cfg.swift: # 82| IntegerLiteralExpr #-----| -> PatternBindingDecl +# 84| ConcreteFuncDecl +#-----| -> ParamDecl + # 84| enter ConcreteFuncDecl -#-----| -> DeclRefExpr +#-----| -> ConcreteFuncDecl # 84| exit ConcreteFuncDecl # 84| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 84| ParamDecl +#-----| -> DeclRefExpr + # 85| DeclRefExpr #-----| -> DeclRefExpr @@ -658,14 +726,20 @@ cfg.swift: # 85| IntegerLiteralExpr #-----| -> BinaryExpr +# 88| ConcreteFuncDecl +#-----| -> ParamDecl + # 88| enter ConcreteFuncDecl -#-----| -> DeclRefExpr +#-----| -> ConcreteFuncDecl # 88| exit ConcreteFuncDecl # 88| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 88| ParamDecl +#-----| -> DeclRefExpr + # 89| DeclRefExpr #-----| -> NilLiteralExpr @@ -675,21 +749,44 @@ cfg.swift: # 89| NilLiteralExpr #-----| -> AssignExpr +# 98| BraceStmt +#-----| -> exit DestructorDecl (normal) + +# 98| DestructorDecl +#-----| -> BraceStmt + +# 98| enter DestructorDecl +#-----| -> DestructorDecl + +# 98| exit DestructorDecl + +# 98| exit DestructorDecl (normal) +#-----| -> exit DestructorDecl + +# 99| AccessorDecl + # 99| enter AccessorDecl +#-----| -> AccessorDecl # 99| exit AccessorDecl # 99| exit AccessorDecl (normal) #-----| -> exit AccessorDecl +# 100| ConstructorDecl +#-----| -> ParamDecl + # 100| enter ConstructorDecl -#-----| -> DeclRefExpr +#-----| -> ConstructorDecl # 100| exit ConstructorDecl # 100| exit ConstructorDecl (normal) #-----| -> exit ConstructorDecl +# 100| ParamDecl +#-----| -> DeclRefExpr + # 101| DeclRefExpr #-----| -> MemberRefExpr @@ -705,9 +802,12 @@ cfg.swift: # 102| ReturnStmt #-----| return -> exit ConstructorDecl (normal) -# 104| enter ConcreteFuncDecl +# 104| ConcreteFuncDecl #-----| -> DeclRefExpr +# 104| enter ConcreteFuncDecl +#-----| -> ConcreteFuncDecl + # 104| exit ConcreteFuncDecl # 104| exit ConcreteFuncDecl (normal) @@ -722,14 +822,26 @@ cfg.swift: # 105| getter for MemberRefExpr #-----| -> ReturnStmt +# 109| ConcreteFuncDecl +#-----| -> ParamDecl + # 109| enter ConcreteFuncDecl -#-----| -> NamedPattern +#-----| -> ConcreteFuncDecl # 109| exit ConcreteFuncDecl # 109| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 109| ParamDecl +#-----| -> ParamDecl + +# 109| ParamDecl +#-----| -> ParamDecl + +# 109| ParamDecl +#-----| -> NamedPattern + # 110| PatternBindingDecl #-----| -> ConcreteVarDecl @@ -1172,14 +1284,20 @@ cfg.swift: # 134| DeclRefExpr #-----| -> TBD (DotSelfExpr) +# 137| ConcreteFuncDecl +#-----| -> ParamDecl + # 137| enter ConcreteFuncDecl -#-----| -> DeclRefExpr +#-----| -> ConcreteFuncDecl # 137| exit ConcreteFuncDecl # 137| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 137| ParamDecl +#-----| -> DeclRefExpr + # 138| ForEachStmt #-----| non-empty -> AnyPattern #-----| empty -> SwitchStmt @@ -1275,14 +1393,20 @@ cfg.swift: # 147| BooleanLiteralExpr #-----| -> ReturnStmt +# 163| ConcreteFuncDecl +#-----| -> ParamDecl + # 163| enter ConcreteFuncDecl -#-----| -> DeferStmt +#-----| -> ConcreteFuncDecl # 163| exit ConcreteFuncDecl # 163| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 163| ParamDecl +#-----| -> DeferStmt + # 165| DeferStmt #-----| -> DeferStmt @@ -1391,14 +1515,20 @@ cfg.swift: # 176| VarargExpansionExpr #-----| -> DefaultArgumentExpr +# 181| ConcreteFuncDecl +#-----| -> ParamDecl + # 181| enter ConcreteFuncDecl -#-----| -> IfStmt +#-----| -> ConcreteFuncDecl # 181| exit ConcreteFuncDecl # 181| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 181| ParamDecl +#-----| -> IfStmt + # 182| IfStmt #-----| -> DeclRefExpr @@ -1590,14 +1720,20 @@ cfg.swift: # 189| VarargExpansionExpr #-----| -> DefaultArgumentExpr +# 193| ConcreteFuncDecl +#-----| -> ParamDecl + # 193| enter ConcreteFuncDecl -#-----| -> IfStmt +#-----| -> ConcreteFuncDecl # 193| exit ConcreteFuncDecl # 193| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 193| ParamDecl +#-----| -> IfStmt + # 194| IfStmt #-----| -> DeclRefExpr @@ -1620,14 +1756,20 @@ cfg.swift: # 197| IntegerLiteralExpr #-----| -> ReturnStmt +# 200| ConcreteFuncDecl +#-----| -> ParamDecl + # 200| enter ConcreteFuncDecl -#-----| -> IfStmt +#-----| -> ConcreteFuncDecl # 200| exit ConcreteFuncDecl # 200| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 200| ParamDecl +#-----| -> IfStmt + # 201| IfStmt #-----| -> DeclRefExpr @@ -1748,14 +1890,26 @@ cfg.swift: # 207| LoadExpr #-----| -> ReturnStmt +# 210| ConcreteFuncDecl +#-----| -> ParamDecl + # 210| enter ConcreteFuncDecl -#-----| -> DeclRefExpr +#-----| -> ConcreteFuncDecl # 210| exit ConcreteFuncDecl # 210| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 210| ParamDecl +#-----| -> ParamDecl + +# 210| ParamDecl +#-----| -> ParamDecl + +# 210| ParamDecl +#-----| -> DeclRefExpr + # 211| ReturnStmt #-----| return -> exit ConcreteFuncDecl (normal) @@ -1792,14 +1946,20 @@ cfg.swift: # 211| StringLiteralExpr #-----| -> IfExpr +# 214| ConcreteFuncDecl +#-----| -> ParamDecl + # 214| enter ConcreteFuncDecl -#-----| -> IfStmt +#-----| -> ConcreteFuncDecl # 214| exit ConcreteFuncDecl # 214| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 214| ParamDecl +#-----| -> IfStmt + # 215| IfStmt #-----| -> DeclRefExpr @@ -1838,9 +1998,12 @@ cfg.swift: # 219| StringLiteralExpr #-----| -> ReturnStmt -# 223| enter ConcreteFuncDecl +# 223| ConcreteFuncDecl #-----| -> IfStmt +# 223| enter ConcreteFuncDecl +#-----| -> ConcreteFuncDecl + # 223| exit ConcreteFuncDecl # 223| exit ConcreteFuncDecl (normal) @@ -1883,14 +2046,20 @@ cfg.swift: # 225| VarargExpansionExpr #-----| -> DefaultArgumentExpr +# 229| ConcreteFuncDecl +#-----| -> ParamDecl + # 229| enter ConcreteFuncDecl -#-----| -> IfStmt +#-----| -> ConcreteFuncDecl # 229| exit ConcreteFuncDecl # 229| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 229| ParamDecl +#-----| -> IfStmt + # 230| IfStmt #-----| -> DeclRefExpr @@ -1953,14 +2122,23 @@ cfg.swift: # 234| VarargExpansionExpr #-----| -> DefaultArgumentExpr +# 237| ConcreteFuncDecl +#-----| -> ParamDecl + # 237| enter ConcreteFuncDecl -#-----| -> IfStmt +#-----| -> ConcreteFuncDecl # 237| exit ConcreteFuncDecl # 237| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 237| ParamDecl +#-----| -> ParamDecl + +# 237| ParamDecl +#-----| -> IfStmt + # 238| IfStmt #-----| -> DeclRefExpr @@ -2011,14 +2189,23 @@ cfg.swift: # 239| VarargExpansionExpr #-----| -> DefaultArgumentExpr +# 243| ConcreteFuncDecl +#-----| -> ParamDecl + # 243| enter ConcreteFuncDecl -#-----| -> NamedPattern +#-----| -> ConcreteFuncDecl # 243| exit ConcreteFuncDecl # 243| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 243| ParamDecl +#-----| -> ParamDecl + +# 243| ParamDecl +#-----| -> NamedPattern + # 244| PatternBindingDecl #-----| -> ConcreteVarDecl @@ -2467,23 +2654,35 @@ cfg.swift: # 259| DeclRefExpr #-----| -> BinaryExpr +# 262| ConcreteFuncDecl +#-----| -> ParamDecl + # 262| enter ConcreteFuncDecl -#-----| -> InterpolatedStringLiteralExpr +#-----| -> ConcreteFuncDecl # 262| exit ConcreteFuncDecl # 262| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 262| ParamDecl +#-----| -> ParamDecl + +# 262| ParamDecl +#-----| -> InterpolatedStringLiteralExpr + # 263| ReturnStmt #-----| return -> exit ConcreteFuncDecl (normal) # 263| InterpolatedStringLiteralExpr #-----| -> ReturnStmt -# 266| enter ConcreteFuncDecl +# 266| ConcreteFuncDecl #-----| -> NamedPattern +# 266| enter ConcreteFuncDecl +#-----| -> ConcreteFuncDecl + # 266| exit ConcreteFuncDecl # 266| exit ConcreteFuncDecl (normal) @@ -3678,14 +3877,20 @@ cfg.swift: # 296| IntegerLiteralExpr #-----| -> getter for SubscriptExpr +# 299| ConcreteFuncDecl +#-----| -> ParamDecl + # 299| enter ConcreteFuncDecl -#-----| -> WhileStmt +#-----| -> ConcreteFuncDecl # 299| exit ConcreteFuncDecl # 299| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 299| ParamDecl +#-----| -> WhileStmt + # 300| WhileStmt #-----| -> DeclRefExpr @@ -3765,14 +3970,20 @@ cfg.swift: # 302| IntegerLiteralExpr #-----| -> BinaryExpr +# 306| ConcreteFuncDecl +#-----| -> ParamDecl + # 306| enter ConcreteFuncDecl -#-----| -> WhileStmt +#-----| -> ConcreteFuncDecl # 306| exit ConcreteFuncDecl # 306| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 306| ParamDecl +#-----| -> WhileStmt + # 307| WhileStmt #-----| -> DeclRefExpr @@ -3965,14 +4176,20 @@ cfg.swift: # 318| VarargExpansionExpr #-----| -> DefaultArgumentExpr +# 321| ConcreteFuncDecl +#-----| -> ParamDecl + # 321| enter ConcreteFuncDecl -#-----| -> WhileStmt +#-----| -> ConcreteFuncDecl # 321| exit ConcreteFuncDecl # 321| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 321| ParamDecl +#-----| -> WhileStmt + # 322| WhileStmt #-----| -> DeclRefExpr @@ -4195,14 +4412,20 @@ cfg.swift: # 334| VarargExpansionExpr #-----| -> DefaultArgumentExpr +# 338| ConcreteFuncDecl +#-----| -> ParamDecl + # 338| enter ConcreteFuncDecl -#-----| -> RepeatWhileStmt +#-----| -> ConcreteFuncDecl # 338| exit ConcreteFuncDecl # 338| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 338| ParamDecl +#-----| -> RepeatWhileStmt + # 339| RepeatWhileStmt #-----| -> DeclRefExpr @@ -4279,9 +4502,12 @@ cfg.swift: # 342| IntegerLiteralExpr #-----| -> BinaryExpr -# 345| enter ConcreteFuncDecl +# 345| ConcreteFuncDecl #-----| -> NamedPattern +# 345| enter ConcreteFuncDecl +#-----| -> ConcreteFuncDecl + # 345| exit ConcreteFuncDecl # 345| exit ConcreteFuncDecl (normal) @@ -4353,21 +4579,44 @@ cfg.swift: # 348| IntegerLiteralExpr #-----| -> BinaryExpr +# 352| BraceStmt +#-----| -> exit DestructorDecl (normal) + +# 352| DestructorDecl +#-----| -> BraceStmt + +# 352| enter DestructorDecl +#-----| -> DestructorDecl + +# 352| exit DestructorDecl + +# 352| exit DestructorDecl (normal) +#-----| -> exit DestructorDecl + +# 353| AccessorDecl + # 353| enter AccessorDecl +#-----| -> AccessorDecl # 353| exit AccessorDecl # 353| exit AccessorDecl (normal) #-----| -> exit AccessorDecl +# 354| ConstructorDecl +#-----| -> ParamDecl + # 354| enter ConstructorDecl -#-----| -> DeclRefExpr +#-----| -> ConstructorDecl # 354| exit ConstructorDecl # 354| exit ConstructorDecl (normal) #-----| -> exit ConstructorDecl +# 354| ParamDecl +#-----| -> DeclRefExpr + # 355| DeclRefExpr #-----| -> MemberRefExpr @@ -4383,9 +4632,12 @@ cfg.swift: # 356| ReturnStmt #-----| return -> exit ConstructorDecl (normal) -# 358| enter ConcreteFuncDecl +# 358| ConcreteFuncDecl #-----| -> DeclRefExpr +# 358| enter ConcreteFuncDecl +#-----| -> ConcreteFuncDecl + # 358| exit ConcreteFuncDecl # 358| exit ConcreteFuncDecl (normal) @@ -4400,14 +4652,20 @@ cfg.swift: # 359| getter for MemberRefExpr #-----| -> ReturnStmt +# 363| ConcreteFuncDecl +#-----| -> ParamDecl + # 363| enter ConcreteFuncDecl -#-----| -> DeclRefExpr +#-----| -> ConcreteFuncDecl # 363| exit ConcreteFuncDecl # 363| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 363| ParamDecl +#-----| -> DeclRefExpr + # 364| ReturnStmt #-----| return -> exit ConcreteFuncDecl (normal) @@ -4446,14 +4704,23 @@ cfg.swift: # 364| DeclRefExpr #-----| -> DeclRefExpr +# 367| ConcreteFuncDecl +#-----| -> ParamDecl + # 367| enter ConcreteFuncDecl -#-----| -> NamedPattern +#-----| -> ConcreteFuncDecl # 367| exit ConcreteFuncDecl # 367| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 367| ParamDecl +#-----| -> ParamDecl + +# 367| ParamDecl +#-----| -> NamedPattern + # 368| ReturnStmt #-----| return -> exit ConcreteFuncDecl (normal) @@ -4497,14 +4764,20 @@ cfg.swift: # 368| StringLiteralExpr #-----| -> PatternBindingDecl +# 373| ConcreteFuncDecl +#-----| -> ParamDecl + # 373| enter ConcreteFuncDecl -#-----| -> DeclRefExpr +#-----| -> ConcreteFuncDecl # 373| exit ConcreteFuncDecl # 373| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 373| ParamDecl +#-----| -> DeclRefExpr + # 374| ReturnStmt #-----| return -> exit ConcreteFuncDecl (normal) @@ -4580,12 +4853,18 @@ cfg.swift: # 374| IntegerLiteralExpr #-----| -> TupleExpr +# 377| BraceStmt +#-----| -> exit DestructorDecl (normal) + # 377| CallExpr #-----| exception -> exit ConstructorDecl (normal) # 377| DeclRefExpr #-----| -> StringLiteralExpr +# 377| DestructorDecl +#-----| -> BraceStmt + # 377| MagicIdentifierLiteralExpr #-----| -> MagicIdentifierLiteralExpr @@ -4601,17 +4880,30 @@ cfg.swift: # 377| StringLiteralExpr #-----| -> MagicIdentifierLiteralExpr +# 377| enter DestructorDecl +#-----| -> DestructorDecl + +# 377| exit DestructorDecl + +# 377| exit DestructorDecl (normal) +#-----| -> exit DestructorDecl + +# 377| ConstructorDecl + # 377| enter ConstructorDecl -#-----| -> DeclRefExpr +#-----| -> ConstructorDecl # 377| exit ConstructorDecl # 377| exit ConstructorDecl (normal) #-----| -> exit ConstructorDecl -# 378| enter ConstructorDecl +# 378| ConstructorDecl #-----| -> TBD (OtherConstructorDeclRefExpr) +# 378| enter ConstructorDecl +#-----| -> ConstructorDecl + # 378| exit ConstructorDecl # 378| exit ConstructorDecl (normal) @@ -4638,14 +4930,20 @@ cfg.swift: # 380| ReturnStmt #-----| return -> exit ConstructorDecl (normal) +# 383| ConcreteFuncDecl +#-----| -> ParamDecl + # 383| enter ConcreteFuncDecl -#-----| -> DoStmt +#-----| -> ConcreteFuncDecl # 383| exit ConcreteFuncDecl # 383| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 383| ParamDecl +#-----| -> DoStmt + # 384| DoStmt #-----| -> DeclRefExpr @@ -4686,15 +4984,27 @@ cfg.swift: # 386| VarargExpansionExpr #-----| -> DefaultArgumentExpr +# 394| AccessorDecl + +# 394| AccessorDecl +#-----| -> ParamDecl + +# 394| AccessorDecl +#-----| -> TBD (YieldStmt) + +# 394| ParamDecl + # 394| TBD (YieldStmt) #-----| -> exit AccessorDecl (normal) # 394| enter AccessorDecl +#-----| -> AccessorDecl # 394| enter AccessorDecl +#-----| -> AccessorDecl # 394| enter AccessorDecl -#-----| -> TBD (YieldStmt) +#-----| -> AccessorDecl # 394| exit AccessorDecl @@ -4711,8 +5021,11 @@ cfg.swift: # 394| exit AccessorDecl (normal) #-----| -> exit AccessorDecl +# 395| ConstructorDecl +#-----| -> DeclRefExpr + # 395| enter ConstructorDecl -#-----| -> DeclRefExpr +#-----| -> ConstructorDecl # 395| exit ConstructorDecl @@ -4734,9 +5047,12 @@ cfg.swift: # 397| ReturnStmt #-----| return -> exit ConstructorDecl (normal) -# 399| enter DestructorDecl +# 399| DestructorDecl #-----| -> DeclRefExpr +# 399| enter DestructorDecl +#-----| -> DestructorDecl + # 399| exit DestructorDecl # 399| exit DestructorDecl (normal) @@ -4754,14 +5070,23 @@ cfg.swift: # 400| IntegerLiteralExpr #-----| -> AssignExpr +# 404| ConcreteFuncDecl +#-----| -> ParamDecl + # 404| enter ConcreteFuncDecl -#-----| -> StringLiteralExpr +#-----| -> ConcreteFuncDecl # 404| exit ConcreteFuncDecl # 404| exit ConcreteFuncDecl (normal) #-----| -> exit ConcreteFuncDecl +# 404| ParamDecl +#-----| -> ParamDecl + +# 404| ParamDecl +#-----| -> StringLiteralExpr + # 405| ReturnStmt #-----| return -> exit ConcreteFuncDecl (normal) From ae6d16a40fb324fa635a1b231a61dc99c61f2f2a Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 26 May 2022 16:53:42 +0000 Subject: [PATCH 095/125] Swift: flow into callees via params --- .../swift/dataflow/internal/DataFlowDispatch.qll | 14 +++++++++++++- .../swift/dataflow/internal/DataFlowPrivate.qll | 13 ++++++++++--- .../swift/dataflow/internal/DataFlowPublic.qll | 2 +- .../swift/dataflow/internal/SsaImplSpecific.qll | 4 ++++ .../dataflow/dataflow/DataFlow.expected | 11 +++++++++++ .../dataflow/dataflow/LocalFlow.expected | 2 ++ 6 files changed, 41 insertions(+), 5 deletions(-) diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll index 5b47f41c46c..889f85d7450 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll @@ -94,6 +94,13 @@ class ParameterPosition extends TParameterPosition { string toString() { none() } } +class PositionalParameterPosition extends ParameterPosition, TPositionalParameter { + int getIndex() { + this = TPositionalParameter(result) + } +} + + /** An argument position. */ class ArgumentPosition extends TArgumentPosition { /** Gets a textual representation of this position. */ @@ -108,4 +115,9 @@ class PositionalArgumentPosition extends ArgumentPosition, TPositionalArgument { /** Holds if arguments at position `apos` match parameters at position `ppos`. */ pragma[inline] -predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { none() } +predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { + ppos instanceof TThisParameter and + apos instanceof TThisArgument + or + ppos.(PositionalParameterPosition).getIndex() = apos.(PositionalArgumentPosition).getIndex() +} diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index 1902400bff3..99fe045dfb9 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -3,6 +3,7 @@ private import DataFlowPublic private import DataFlowDispatch private import codeql.swift.controlflow.CfgNodes private import codeql.swift.dataflow.Ssa +private import codeql.swift.controlflow.BasicBlocks /** Gets the callable in which this node occurs. */ DataFlowCallable nodeGetEnclosingCallable(NodeImpl n) { result = n.getEnclosingCallable() } @@ -51,7 +52,6 @@ private module Cached { cached newtype TNode = TExprNode(ExprCfgNode e) or - TNormalParameterNode(ParamDecl p) or TSsaDefinitionNode(Ssa::Definition def) private predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) { @@ -99,10 +99,15 @@ private module ParameterNodes { predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { none() } } - class NormalParameterNode extends ParameterNodeImpl, TNormalParameterNode { + class NormalParameterNode extends ParameterNodeImpl, SsaDefinitionNode { ParamDecl param; - NormalParameterNode() { this = TNormalParameterNode(param) } + NormalParameterNode() { + exists(BasicBlock bb, int i | + super.asDefinition().definesAt(param, bb, i) and + bb.getNode(i).getNode().asAstNode() = param + ) + } override Location getLocationImpl() { result = param.getLocation() } @@ -115,6 +120,8 @@ private module ParameterNodes { pos = TPositionalParameter(index) ) } + + override DataFlowCallable getEnclosingCallable() { isParameterOf(result, _) } } } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll index 815b967e094..67a184193d0 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll @@ -69,7 +69,7 @@ class ExprNode extends Node, TExprNode { * The value of a parameter at function entry, viewed as a node in a data * flow graph. */ -class ParameterNode extends Node, TNormalParameterNode instanceof ParameterNodeImpl { } +class ParameterNode extends Node, SsaDefinitionNode instanceof ParameterNodeImpl { } /** */ diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll b/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll index bd7373935aa..458e0b3a2e6 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll @@ -27,6 +27,10 @@ predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) v.getParentPattern() = pattern and certain = true ) + or + v instanceof ParamDecl and + bb.getNode(i).getNode().asAstNode() = v and + certain = true } private predicate isLValue(DeclRefExpr ref) { any(AssignExpr assign).getDest() = ref } diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected index e531105365d..2c1e6f4f924 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected @@ -2,13 +2,24 @@ edges | test.swift:6:19:6:26 | CallExpr : | test.swift:7:15:7:15 | DeclRefExpr | | test.swift:6:19:6:26 | CallExpr : | test.swift:9:15:9:15 | DeclRefExpr | | test.swift:6:19:6:26 | CallExpr : | test.swift:10:15:10:15 | DeclRefExpr | +| test.swift:25:20:25:27 | CallExpr : | test.swift:28:18:28:21 | ParamDecl : | +| test.swift:25:20:25:27 | CallExpr : | test.swift:28:18:28:21 | WriteDef : | +| test.swift:28:18:28:21 | ParamDecl : | test.swift:29:15:29:15 | DeclRefExpr | +| test.swift:28:18:28:21 | WriteDef : | test.swift:29:15:29:15 | DeclRefExpr | nodes | test.swift:6:19:6:26 | CallExpr : | semmle.label | CallExpr : | | test.swift:7:15:7:15 | DeclRefExpr | semmle.label | DeclRefExpr | | test.swift:9:15:9:15 | DeclRefExpr | semmle.label | DeclRefExpr | | test.swift:10:15:10:15 | DeclRefExpr | semmle.label | DeclRefExpr | +| test.swift:25:20:25:27 | CallExpr : | semmle.label | CallExpr : | +| test.swift:28:18:28:21 | ParamDecl : | semmle.label | ParamDecl : | +| test.swift:28:18:28:21 | ParamDecl : | semmle.label | WriteDef : | +| test.swift:28:18:28:21 | WriteDef : | semmle.label | ParamDecl : | +| test.swift:28:18:28:21 | WriteDef : | semmle.label | WriteDef : | +| test.swift:29:15:29:15 | DeclRefExpr | semmle.label | DeclRefExpr | subpaths #select | test.swift:6:19:6:26 | CallExpr : | test.swift:7:15:7:15 | DeclRefExpr | | test.swift:6:19:6:26 | CallExpr : | test.swift:9:15:9:15 | DeclRefExpr | | test.swift:6:19:6:26 | CallExpr : | test.swift:10:15:10:15 | DeclRefExpr | +| test.swift:25:20:25:27 | CallExpr : | test.swift:29:15:29:15 | DeclRefExpr | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected index 0169f82ab99..611ef065883 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected @@ -13,3 +13,5 @@ | test.swift:17:10:17:10 | IntegerLiteralExpr | test.swift:17:5:17:10 | WriteDef | | test.swift:19:14:19:14 | DeclRefExpr | test.swift:19:9:19:14 | WriteDef | | test.swift:19:14:19:14 | DeclRefExpr | test.swift:19:14:19:14 | DeclRefExpr | +| test.swift:28:18:28:21 | ParamDecl | test.swift:29:15:29:15 | DeclRefExpr | +| test.swift:28:18:28:21 | WriteDef | test.swift:29:15:29:15 | DeclRefExpr | From 81ac6480658066fec183f6e1bf6da6845a4722ef Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 26 May 2022 16:58:53 +0000 Subject: [PATCH 096/125] Swift: flow out of calls via return statements --- .../dataflow/internal/DataFlowPrivate.qll | 22 +++++++++++++++++-- .../dataflow/dataflow/DataFlow.expected | 4 ++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index 99fe045dfb9..e0853d28e28 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -155,7 +155,19 @@ abstract class ReturnNode extends Node { abstract ReturnKind getKind(); } -private module ReturnNodes { } +private module ReturnNodes { + class ReturnReturnNode extends ReturnNode, ExprNode { + ReturnReturnNode() { + exists(ReturnStmt stmt | + stmt.getResult() = this.asExpr() + ) + } + + override ReturnKind getKind() { + result instanceof NormalReturnKind + } + } +} import ReturnNodes @@ -165,7 +177,13 @@ abstract class OutNode extends Node { abstract DataFlowCall getCall(ReturnKind kind); } -private module OutNodes { } +private module OutNodes { + class CallOutNode extends OutNode, DataFlowCall { + override DataFlowCall getCall(ReturnKind kind) { + result = this and kind instanceof NormalReturnKind + } + } +} import OutNodes diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected index 2c1e6f4f924..164674b22a8 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected @@ -6,6 +6,7 @@ edges | test.swift:25:20:25:27 | CallExpr : | test.swift:28:18:28:21 | WriteDef : | | test.swift:28:18:28:21 | ParamDecl : | test.swift:29:15:29:15 | DeclRefExpr | | test.swift:28:18:28:21 | WriteDef : | test.swift:29:15:29:15 | DeclRefExpr | +| test.swift:33:12:33:19 | CallExpr : | test.swift:37:15:37:29 | CallExpr | nodes | test.swift:6:19:6:26 | CallExpr : | semmle.label | CallExpr : | | test.swift:7:15:7:15 | DeclRefExpr | semmle.label | DeclRefExpr | @@ -17,9 +18,12 @@ nodes | test.swift:28:18:28:21 | WriteDef : | semmle.label | ParamDecl : | | test.swift:28:18:28:21 | WriteDef : | semmle.label | WriteDef : | | test.swift:29:15:29:15 | DeclRefExpr | semmle.label | DeclRefExpr | +| test.swift:33:12:33:19 | CallExpr : | semmle.label | CallExpr : | +| test.swift:37:15:37:29 | CallExpr | semmle.label | CallExpr | subpaths #select | test.swift:6:19:6:26 | CallExpr : | test.swift:7:15:7:15 | DeclRefExpr | | test.swift:6:19:6:26 | CallExpr : | test.swift:9:15:9:15 | DeclRefExpr | | test.swift:6:19:6:26 | CallExpr : | test.swift:10:15:10:15 | DeclRefExpr | | test.swift:25:20:25:27 | CallExpr : | test.swift:29:15:29:15 | DeclRefExpr | +| test.swift:33:12:33:19 | CallExpr : | test.swift:37:15:37:29 | CallExpr | From 507fdef028a620a5433e67eabde0ebb98e85a5d5 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 26 May 2022 17:33:12 +0000 Subject: [PATCH 097/125] Swift: add more data flow tests --- .../dataflow/dataflow/DataFlow.expected | 39 ++++++++++++------- .../dataflow/dataflow/LocalFlow.expected | 17 +++++++- .../dataflow/dataflow/test.swift | 28 ++++++++++++- 3 files changed, 66 insertions(+), 18 deletions(-) diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected index 164674b22a8..e3c07f8ce70 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected @@ -2,28 +2,39 @@ edges | test.swift:6:19:6:26 | CallExpr : | test.swift:7:15:7:15 | DeclRefExpr | | test.swift:6:19:6:26 | CallExpr : | test.swift:9:15:9:15 | DeclRefExpr | | test.swift:6:19:6:26 | CallExpr : | test.swift:10:15:10:15 | DeclRefExpr | -| test.swift:25:20:25:27 | CallExpr : | test.swift:28:18:28:21 | ParamDecl : | -| test.swift:25:20:25:27 | CallExpr : | test.swift:28:18:28:21 | WriteDef : | -| test.swift:28:18:28:21 | ParamDecl : | test.swift:29:15:29:15 | DeclRefExpr | -| test.swift:28:18:28:21 | WriteDef : | test.swift:29:15:29:15 | DeclRefExpr | -| test.swift:33:12:33:19 | CallExpr : | test.swift:37:15:37:29 | CallExpr | +| test.swift:25:20:25:27 | CallExpr : | test.swift:29:18:29:21 | ParamDecl : | +| test.swift:25:20:25:27 | CallExpr : | test.swift:29:18:29:21 | WriteDef : | +| test.swift:26:26:26:33 | CallExpr : | test.swift:29:26:29:29 | ParamDecl : | +| test.swift:26:26:26:33 | CallExpr : | test.swift:29:26:29:29 | WriteDef : | +| test.swift:29:18:29:21 | ParamDecl : | test.swift:30:15:30:15 | DeclRefExpr | +| test.swift:29:18:29:21 | WriteDef : | test.swift:30:15:30:15 | DeclRefExpr | +| test.swift:29:26:29:29 | ParamDecl : | test.swift:31:15:31:15 | DeclRefExpr | +| test.swift:29:26:29:29 | WriteDef : | test.swift:31:15:31:15 | DeclRefExpr | +| test.swift:35:12:35:19 | CallExpr : | test.swift:39:15:39:29 | CallExpr | nodes | test.swift:6:19:6:26 | CallExpr : | semmle.label | CallExpr : | | test.swift:7:15:7:15 | DeclRefExpr | semmle.label | DeclRefExpr | | test.swift:9:15:9:15 | DeclRefExpr | semmle.label | DeclRefExpr | | test.swift:10:15:10:15 | DeclRefExpr | semmle.label | DeclRefExpr | | test.swift:25:20:25:27 | CallExpr : | semmle.label | CallExpr : | -| test.swift:28:18:28:21 | ParamDecl : | semmle.label | ParamDecl : | -| test.swift:28:18:28:21 | ParamDecl : | semmle.label | WriteDef : | -| test.swift:28:18:28:21 | WriteDef : | semmle.label | ParamDecl : | -| test.swift:28:18:28:21 | WriteDef : | semmle.label | WriteDef : | -| test.swift:29:15:29:15 | DeclRefExpr | semmle.label | DeclRefExpr | -| test.swift:33:12:33:19 | CallExpr : | semmle.label | CallExpr : | -| test.swift:37:15:37:29 | CallExpr | semmle.label | CallExpr | +| test.swift:26:26:26:33 | CallExpr : | semmle.label | CallExpr : | +| test.swift:29:18:29:21 | ParamDecl : | semmle.label | ParamDecl : | +| test.swift:29:18:29:21 | ParamDecl : | semmle.label | WriteDef : | +| test.swift:29:18:29:21 | WriteDef : | semmle.label | ParamDecl : | +| test.swift:29:18:29:21 | WriteDef : | semmle.label | WriteDef : | +| test.swift:29:26:29:29 | ParamDecl : | semmle.label | ParamDecl : | +| test.swift:29:26:29:29 | ParamDecl : | semmle.label | WriteDef : | +| test.swift:29:26:29:29 | WriteDef : | semmle.label | ParamDecl : | +| test.swift:29:26:29:29 | WriteDef : | semmle.label | WriteDef : | +| test.swift:30:15:30:15 | DeclRefExpr | semmle.label | DeclRefExpr | +| test.swift:31:15:31:15 | DeclRefExpr | semmle.label | DeclRefExpr | +| test.swift:35:12:35:19 | CallExpr : | semmle.label | CallExpr : | +| test.swift:39:15:39:29 | CallExpr | semmle.label | CallExpr | subpaths #select | test.swift:6:19:6:26 | CallExpr : | test.swift:7:15:7:15 | DeclRefExpr | | test.swift:6:19:6:26 | CallExpr : | test.swift:9:15:9:15 | DeclRefExpr | | test.swift:6:19:6:26 | CallExpr : | test.swift:10:15:10:15 | DeclRefExpr | -| test.swift:25:20:25:27 | CallExpr : | test.swift:29:15:29:15 | DeclRefExpr | -| test.swift:33:12:33:19 | CallExpr : | test.swift:37:15:37:29 | CallExpr | +| test.swift:25:20:25:27 | CallExpr : | test.swift:30:15:30:15 | DeclRefExpr | +| test.swift:26:26:26:33 | CallExpr : | test.swift:31:15:31:15 | DeclRefExpr | +| test.swift:35:12:35:19 | CallExpr : | test.swift:39:15:39:29 | CallExpr | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected index 611ef065883..479031df4c5 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected @@ -1,5 +1,6 @@ | file://:0:0:0:0 | Phi | test.swift:15:15:15:15 | DeclRefExpr | | file://:0:0:0:0 | Phi | test.swift:21:15:21:15 | DeclRefExpr | +| file://:0:0:0:0 | Phi | test.swift:50:15:50:15 | DeclRefExpr | | test.swift:6:9:6:13 | WriteDef | test.swift:7:15:7:15 | DeclRefExpr | | test.swift:6:19:6:26 | CallExpr | test.swift:6:9:6:13 | WriteDef | | test.swift:7:15:7:15 | DeclRefExpr | test.swift:8:10:8:10 | DeclRefExpr | @@ -13,5 +14,17 @@ | test.swift:17:10:17:10 | IntegerLiteralExpr | test.swift:17:5:17:10 | WriteDef | | test.swift:19:14:19:14 | DeclRefExpr | test.swift:19:9:19:14 | WriteDef | | test.swift:19:14:19:14 | DeclRefExpr | test.swift:19:14:19:14 | DeclRefExpr | -| test.swift:28:18:28:21 | ParamDecl | test.swift:29:15:29:15 | DeclRefExpr | -| test.swift:28:18:28:21 | WriteDef | test.swift:29:15:29:15 | DeclRefExpr | +| test.swift:29:18:29:21 | ParamDecl | test.swift:30:15:30:15 | DeclRefExpr | +| test.swift:29:18:29:21 | WriteDef | test.swift:30:15:30:15 | DeclRefExpr | +| test.swift:29:26:29:29 | ParamDecl | test.swift:31:15:31:15 | DeclRefExpr | +| test.swift:29:26:29:29 | WriteDef | test.swift:31:15:31:15 | DeclRefExpr | +| test.swift:42:16:42:19 | ParamDecl | test.swift:45:8:45:8 | DeclRefExpr | +| test.swift:42:16:42:19 | WriteDef | test.swift:45:8:45:8 | DeclRefExpr | +| test.swift:43:9:43:13 | WriteDef | test.swift:46:13:46:13 | DeclRefExpr | +| test.swift:43:19:43:26 | CallExpr | test.swift:43:9:43:13 | WriteDef | +| test.swift:46:13:46:13 | DeclRefExpr | test.swift:46:9:46:13 | WriteDef | +| test.swift:48:13:48:13 | IntegerLiteralExpr | test.swift:48:9:48:13 | WriteDef | +| test.swift:58:9:58:12 | WriteDef | test.swift:59:15:59:15 | DeclRefExpr | +| test.swift:58:18:58:18 | IntegerLiteralExpr | test.swift:58:9:58:12 | WriteDef | +| test.swift:59:15:59:15 | DeclRefExpr | test.swift:60:23:60:23 | DeclRefExpr | +| test.swift:60:23:60:23 | DeclRefExpr | test.swift:61:15:61:15 | DeclRefExpr | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/test.swift b/swift/ql/test/library-tests/dataflow/dataflow/test.swift index ae1135fb5d4..113db2dfea7 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/test.swift +++ b/swift/ql/test/library-tests/dataflow/dataflow/test.swift @@ -22,11 +22,13 @@ func intraprocedural_with_local_flow() -> Void { } func caller_source() -> Void { - callee_sink(x: source()) + callee_sink(x: source(), y: 1) + callee_sink(x: 1, y: source()) } -func callee_sink(x: Int) -> Void { +func callee_sink(x: Int, y: Int) -> Void { sink(arg: x) + sink(arg: y) } func callee_source() -> Int { @@ -36,3 +38,25 @@ func callee_source() -> Int { func caller_sink() -> Void { sink(arg: callee_source()) } + +func branching(b: Bool) -> Void { + var t1: Int = source() + var t: Int = 0 + if(b) { + t = t1; + } else { + t = 1; + } + sink(arg: t) +} + +func inoutSource(arg: inout Int) -> Void { + arg = source() +} + +func inoutUser() { + var x: Int = 0 + sink(arg: x) + inoutSource(arg: &x) + sink(arg: x) +} \ No newline at end of file From 32e4c496f6d5e84a0690a912b017762002403b3e Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 26 May 2022 17:55:25 +0000 Subject: [PATCH 098/125] Swift: Dataflow through Phi nodes --- swift/ql/lib/codeql/swift/dataflow/Ssa.qll | 18 ++++++++++++++++++ .../dataflow/internal/DataFlowPrivate.qll | 11 +++++++++++ .../dataflow/dataflow/DataFlow.expected | 4 ++++ .../dataflow/dataflow/LocalFlow.expected | 11 ++++++++--- 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll index b7a84caca28..b56e33d869f 100644 --- a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll +++ b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll @@ -72,4 +72,22 @@ module Ssa { ) } } + + cached class PhiDefinition extends Definition, SsaImplCommon::PhiNode { + cached + override Location getLocation() { + exists(BasicBlock bb, int i | + this.definesAt(_, bb, i) and + result = bb.getLocation() + ) + } + + cached Definition getPhiInput(BasicBlock bb) { + SsaImplCommon::phiHasInputFromBlock(this, result, bb) + } + + cached Definition getAPhiInput() { + result = this.getPhiInput(_) + } + } } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index e0853d28e28..1fc8bb36812 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -4,6 +4,7 @@ private import DataFlowDispatch private import codeql.swift.controlflow.CfgNodes private import codeql.swift.dataflow.Ssa private import codeql.swift.controlflow.BasicBlocks +private import codeql.swift.dataflow.internal.SsaImplCommon as SsaImpl /** Gets the callable in which this node occurs. */ DataFlowCallable nodeGetEnclosingCallable(NodeImpl n) { result = n.getEnclosingCallable() } @@ -46,6 +47,13 @@ private class SsaDefinitionNodeImpl extends SsaDefinitionNode, NodeImpl { } } +private predicate localFlowSsaInput(Node nodeFrom, Ssa::Definition def, Ssa::Definition next) { + exists(BasicBlock bb, int i | SsaImpl::lastRefRedef(def, bb, i, next) | + def.definesAt(_, bb, i) and + def = nodeFrom.asDefinition() + ) +} + /** A collection of cached types and predicates to be evaluated in the same stage. */ cached private module Cached { @@ -66,6 +74,9 @@ private module Cached { or // use-use flow def.adjacentReadPair(nodeFrom.getCfgNode(), nodeTo.getCfgNode()) + // step from previous read to Phi node + or + localFlowSsaInput(nodeFrom, def, nodeTo.asDefinition()) ) } diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected index e3c07f8ce70..cd380247433 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected @@ -11,6 +11,7 @@ edges | test.swift:29:26:29:29 | ParamDecl : | test.swift:31:15:31:15 | DeclRefExpr | | test.swift:29:26:29:29 | WriteDef : | test.swift:31:15:31:15 | DeclRefExpr | | test.swift:35:12:35:19 | CallExpr : | test.swift:39:15:39:29 | CallExpr | +| test.swift:43:19:43:26 | CallExpr : | test.swift:50:15:50:15 | DeclRefExpr | nodes | test.swift:6:19:6:26 | CallExpr : | semmle.label | CallExpr : | | test.swift:7:15:7:15 | DeclRefExpr | semmle.label | DeclRefExpr | @@ -30,6 +31,8 @@ nodes | test.swift:31:15:31:15 | DeclRefExpr | semmle.label | DeclRefExpr | | test.swift:35:12:35:19 | CallExpr : | semmle.label | CallExpr : | | test.swift:39:15:39:29 | CallExpr | semmle.label | CallExpr | +| test.swift:43:19:43:26 | CallExpr : | semmle.label | CallExpr : | +| test.swift:50:15:50:15 | DeclRefExpr | semmle.label | DeclRefExpr | subpaths #select | test.swift:6:19:6:26 | CallExpr : | test.swift:7:15:7:15 | DeclRefExpr | @@ -38,3 +41,4 @@ subpaths | test.swift:25:20:25:27 | CallExpr : | test.swift:30:15:30:15 | DeclRefExpr | | test.swift:26:26:26:33 | CallExpr : | test.swift:31:15:31:15 | DeclRefExpr | | test.swift:35:12:35:19 | CallExpr : | test.swift:39:15:39:29 | CallExpr | +| test.swift:43:19:43:26 | CallExpr : | test.swift:50:15:50:15 | DeclRefExpr | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected index 479031df4c5..7fb2a6ce9e9 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected @@ -1,6 +1,3 @@ -| file://:0:0:0:0 | Phi | test.swift:15:15:15:15 | DeclRefExpr | -| file://:0:0:0:0 | Phi | test.swift:21:15:21:15 | DeclRefExpr | -| file://:0:0:0:0 | Phi | test.swift:50:15:50:15 | DeclRefExpr | | test.swift:6:9:6:13 | WriteDef | test.swift:7:15:7:15 | DeclRefExpr | | test.swift:6:19:6:26 | CallExpr | test.swift:6:9:6:13 | WriteDef | | test.swift:7:15:7:15 | DeclRefExpr | test.swift:8:10:8:10 | DeclRefExpr | @@ -10,8 +7,13 @@ | test.swift:9:15:9:15 | DeclRefExpr | test.swift:11:8:11:8 | DeclRefExpr | | test.swift:12:9:12:14 | WriteDef | test.swift:13:19:13:19 | DeclRefExpr | | test.swift:12:14:12:14 | IntegerLiteralExpr | test.swift:12:9:12:14 | WriteDef | +| test.swift:15:5:15:5 | Phi | test.swift:15:15:15:15 | DeclRefExpr | | test.swift:15:15:15:15 | DeclRefExpr | test.swift:19:14:19:14 | DeclRefExpr | +| test.swift:17:5:17:10 | WriteDef | test.swift:18:11:18:11 | Phi | | test.swift:17:10:17:10 | IntegerLiteralExpr | test.swift:17:5:17:10 | WriteDef | +| test.swift:18:11:18:11 | Phi | test.swift:19:9:19:14 | WriteDef | +| test.swift:18:11:18:11 | Phi | test.swift:21:15:21:15 | DeclRefExpr | +| test.swift:19:9:19:14 | WriteDef | test.swift:18:11:18:11 | Phi | | test.swift:19:14:19:14 | DeclRefExpr | test.swift:19:9:19:14 | WriteDef | | test.swift:19:14:19:14 | DeclRefExpr | test.swift:19:14:19:14 | DeclRefExpr | | test.swift:29:18:29:21 | ParamDecl | test.swift:30:15:30:15 | DeclRefExpr | @@ -22,8 +24,11 @@ | test.swift:42:16:42:19 | WriteDef | test.swift:45:8:45:8 | DeclRefExpr | | test.swift:43:9:43:13 | WriteDef | test.swift:46:13:46:13 | DeclRefExpr | | test.swift:43:19:43:26 | CallExpr | test.swift:43:9:43:13 | WriteDef | +| test.swift:46:9:46:13 | WriteDef | test.swift:50:5:50:5 | Phi | | test.swift:46:13:46:13 | DeclRefExpr | test.swift:46:9:46:13 | WriteDef | +| test.swift:48:9:48:13 | WriteDef | test.swift:50:5:50:5 | Phi | | test.swift:48:13:48:13 | IntegerLiteralExpr | test.swift:48:9:48:13 | WriteDef | +| test.swift:50:5:50:5 | Phi | test.swift:50:15:50:15 | DeclRefExpr | | test.swift:58:9:58:12 | WriteDef | test.swift:59:15:59:15 | DeclRefExpr | | test.swift:58:18:58:18 | IntegerLiteralExpr | test.swift:58:9:58:12 | WriteDef | | test.swift:59:15:59:15 | DeclRefExpr | test.swift:60:23:60:23 | DeclRefExpr | From fef87db73976e95e65f6c43694d0a71952ca32b3 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 27 May 2022 10:08:30 +0200 Subject: [PATCH 099/125] use syntactically correct JS in poly-redos example --- javascript/ql/src/Performance/PolynomialReDoS.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/Performance/PolynomialReDoS.qhelp b/javascript/ql/src/Performance/PolynomialReDoS.qhelp index 78746df38ad..210a4a8a84b 100644 --- a/javascript/ql/src/Performance/PolynomialReDoS.qhelp +++ b/javascript/ql/src/Performance/PolynomialReDoS.qhelp @@ -71,7 +71,7 @@

    - ^0\.\d+E?\d+$ // BAD + /^0\.\d+E?\d+$/.test(str) // BAD

    From a0d5d414b46d6ae57f68b1c451377f3ebe3b9515 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Thu, 26 May 2022 20:36:33 +0100 Subject: [PATCH 100/125] Kotlin: extract methods defined on collections types with their Java signatures Collection, List and Map all define various methods which are either made more generic in Kotlin (e.g. `remove(Object) -> remove(E)`, `containsAll(Collection) -> containsAll(Collection)`), or are made invariant (e.g. `addAll(Collection) -> addAll(Collection)`). This substitutes the types back to their Java signatures, thereby avoiding differing trap labels and duplicated methods for these types and their descendents. --- .../src/main/kotlin/KotlinFileExtractor.kt | 19 +- .../src/main/kotlin/KotlinUsesExtractor.kt | 92 +++- .../Test.java | 28 ++ .../test.expected | 412 ++++++++++++++++++ .../test.kt | 29 ++ .../test.ql | 28 ++ 6 files changed, 593 insertions(+), 15 deletions(-) create mode 100644 java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/Test.java create mode 100644 java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.expected create mode 100644 java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.kt create mode 100644 java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.ql diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index ddce450a887..de591a642a4 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -491,11 +491,17 @@ open class KotlinFileExtractor( private fun extractValueParameter(vp: IrValueParameter, parent: Label, idx: Int, typeSubstitution: TypeSubstitution?, parentSourceDeclaration: Label, classTypeArgsIncludingOuterClasses: List?, extractTypeAccess: Boolean, locOverride: Label? = null): TypeResults { with("value parameter", vp) { val location = locOverride ?: getLocation(vp, classTypeArgsIncludingOuterClasses) + val maybeErasedType = (vp.parent as? IrFunction)?.let { + if (overridesCollectionsMethodWithAlteredParameterTypes(it)) + eraseCollectionsMethodParameterType(vp.type, it.name.asString(), idx) + else + null + } ?: vp.type val id = useValueParameter(vp, parent) if (extractTypeAccess) { - extractTypeAccessRecursive(typeSubstitution?.let { it(vp.type, TypeContext.OTHER, pluginContext) } ?: vp.type, location, id, -1) + extractTypeAccessRecursive(typeSubstitution?.let { it(maybeErasedType, TypeContext.OTHER, pluginContext) } ?: maybeErasedType, location, id, -1) } - return extractValueParameter(id, vp.type, vp.name.asString(), location, parent, idx, typeSubstitution, useValueParameter(vp, parentSourceDeclaration), vp.isVararg) + return extractValueParameter(id, maybeErasedType, vp.name.asString(), location, parent, idx, typeSubstitution, useValueParameter(vp, parentSourceDeclaration), vp.isVararg) } } @@ -524,7 +530,8 @@ open class KotlinFileExtractor( pluginContext.irBuiltIns.unitType, extensionReceiverParameter = null, functionTypeParameters = listOf(), - classTypeArgsIncludingOuterClasses = listOf() + classTypeArgsIncludingOuterClasses = listOf(), + overridesCollectionsMethod = false ) val clinitId = tw.getLabelFor(clinitLabel) val returnType = useType(pluginContext.irBuiltIns.unitType, TypeContext.RETURN) @@ -1391,12 +1398,6 @@ open class KotlinFileExtractor( result } - val javaLangObject by lazy { - val result = pluginContext.referenceClass(FqName("java.lang.Object"))?.owner - result?.let { extractExternalClassLater(it) } - result - } - val objectCloneMethod by lazy { val result = javaLangObject?.declarations?.find { it is IrFunction && it.name.asString() == "clone" diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt index 150e355e44d..5b9ac5e9a28 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt @@ -5,16 +5,20 @@ import com.github.codeql.utils.versions.isRawType import com.semmle.extractor.java.OdasaOutput import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.backend.common.ir.allOverridden +import org.jetbrains.kotlin.backend.common.ir.isOverridableOrOverrides import org.jetbrains.kotlin.backend.common.lower.parentsWithSelf import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.interpreter.getLastOverridden import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.impl.* import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name @@ -31,6 +35,17 @@ open class KotlinUsesExtractor( val pluginContext: IrPluginContext, val globalExtensionState: KotlinExtractorGlobalState ) { + + val javaLangObject by lazy { + val result = pluginContext.referenceClass(FqName("java.lang.Object"))?.owner + result?.let { extractExternalClassLater(it) } + result + } + + val javaLangObjectType by lazy { + javaLangObject?.typeWith() + } + fun usePackage(pkg: String): Label { return extractPackage(pkg) } @@ -789,6 +804,52 @@ open class KotlinUsesExtractor( else -> null } + val javaUtilCollection by lazy { + val result = pluginContext.referenceClass(FqName("java.util.Collection"))?.owner + result?.let { extractExternalClassLater(it) } + result + } + + val wildcardCollectionType by lazy { + javaUtilCollection?.let { + it.symbol.typeWithArguments(listOf(IrStarProjectionImpl)) + } + } + + private fun makeCovariant(t: IrTypeArgument) = + t.typeOrNull?.let { makeTypeProjection(it, Variance.OUT_VARIANCE) } ?: t + + private fun makeArgumentsCovariant(t: IrType) = (t as? IrSimpleType)?.let { + t.toBuilder().also { b -> b.arguments = b.arguments.map(this::makeCovariant) }.buildSimpleType() + } ?: t + + fun eraseCollectionsMethodParameterType(t: IrType, collectionsMethodName: String, paramIdx: Int) = + when(collectionsMethodName) { + "contains", "remove", "containsKey", "containsValue", "get", "indexOf", "lastIndexOf" -> javaLangObjectType + "getOrDefault" -> if (paramIdx == 0) javaLangObjectType else null + "containsAll", "removeAll", "retainAll" -> wildcardCollectionType + // Kotlin defines these like addAll(Collection); Java uses addAll(Collection) + "putAll", "addAll" -> makeArgumentsCovariant(t) + else -> null + } ?: t + + private fun overridesFunctionDefinedOn(f: IrFunction, packageName: String, className: String) = + (f as? IrSimpleFunction)?.let { + it.overriddenSymbols.any { overridden -> + overridden.owner.parentClassOrNull?.let { defnClass -> + defnClass.name.asString() == className && + defnClass.packageFqName?.asString() == packageName + } ?: false + } + } ?: false + + @OptIn(ObsoleteDescriptorBasedAPI::class) + fun overridesCollectionsMethodWithAlteredParameterTypes(f: IrFunction) = + BuiltinMethodsWithSpecialGenericSignature.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(f.descriptor) != null || + (f.name.asString() == "putAll" && overridesFunctionDefinedOn(f, "kotlin.collections", "MutableMap")) || + (f.name.asString() == "addAll" && overridesFunctionDefinedOn(f, "kotlin.collections", "MutableCollection")) || + (f.name.asString() == "addAll" && overridesFunctionDefinedOn(f, "kotlin.collections", "MutableList")) + /* * This is the normal getFunctionLabel function to use. If you want * to refer to the function in its source class then @@ -810,8 +871,19 @@ open class KotlinUsesExtractor( * `java.lang.Throwable`, which isn't what we want. So we have to * allow it to be passed in. */ + @OptIn(ObsoleteDescriptorBasedAPI::class) fun getFunctionLabel(f: IrFunction, maybeParentId: Label?, classTypeArgsIncludingOuterClasses: List?) = - getFunctionLabel(f.parent, maybeParentId, getFunctionShortName(f).nameInDB, f.valueParameters, f.returnType, f.extensionReceiverParameter, getFunctionTypeParameters(f), classTypeArgsIncludingOuterClasses) + getFunctionLabel( + f.parent, + maybeParentId, + getFunctionShortName(f).nameInDB, + f.valueParameters, + f.returnType, + f.extensionReceiverParameter, + getFunctionTypeParameters(f), + classTypeArgsIncludingOuterClasses, + overridesCollectionsMethodWithAlteredParameterTypes(f) + ) /* * This function actually generates the label for a function. @@ -837,6 +909,9 @@ open class KotlinUsesExtractor( functionTypeParameters: List, // The type arguments of enclosing classes of the function. classTypeArgsIncludingOuterClasses: List?, + // If true, this method implements a Java Collections interface (Collection, Map or List) and may need + // parameter erasure to match the way this class will appear to an external consumer of the .class file. + overridesCollectionsMethod: Boolean, // The prefix used in the label. "callable", unless a property label is created, then it's "property". prefix: String = "callable" ): String { @@ -855,14 +930,19 @@ open class KotlinUsesExtractor( enclosingClass?.let { notNullClass -> makeTypeGenericSubstitutionMap(notNullClass, notNullArgs) } } } - val getIdForFunctionLabel = { it: IrValueParameter -> - // Mimic the Java extractor's behaviour: functions with type parameters are named for their erased types; + val getIdForFunctionLabel = { it: IndexedValue -> + // Kotlin rewrites certain Java collections types adding additional generic constraints-- for example, + // Collection.remove(Object) because Collection.remove(Collection::E) in the Kotlin universe. + // If this has happened, erase the type again to get the correct Java signature. + val maybeAmendedForCollections = if (overridesCollectionsMethod) eraseCollectionsMethodParameterType(it.value.type, name, it.index) else it.value.type + // Now substitute any class type parameters in: + val maybeSubbed = maybeAmendedForCollections.substituteTypeAndArguments(substitutionMap, TypeContext.OTHER, pluginContext) + // Finally, mimic the Java extractor's behaviour by naming functions with type parameters for their erased types; // those without type parameters are named for the generic type. - val maybeSubbed = it.type.substituteTypeAndArguments(substitutionMap, TypeContext.OTHER, pluginContext) val maybeErased = if (functionTypeParameters.isEmpty()) maybeSubbed else erase(maybeSubbed) "{${useType(maybeErased).javaResult.id}}" } - val paramTypeIds = allParams.joinToString(separator = ",", transform = getIdForFunctionLabel) + val paramTypeIds = allParams.withIndex().joinToString(separator = ",", transform = getIdForFunctionLabel) val labelReturnType = if (name == "") pluginContext.irBuiltIns.unitType @@ -1299,7 +1379,7 @@ open class KotlinUsesExtractor( val returnType = getter?.returnType ?: setter?.valueParameters?.singleOrNull()?.type ?: pluginContext.irBuiltIns.unitType val typeParams = getFunctionTypeParameters(func) - getFunctionLabel(p.parent, parentId, p.name.asString(), listOf(), returnType, ext, typeParams, classTypeArgsIncludingOuterClasses, "property") + getFunctionLabel(p.parent, parentId, p.name.asString(), listOf(), returnType, ext, typeParams, classTypeArgsIncludingOuterClasses, overridesCollectionsMethod = false, prefix = "property") } } diff --git a/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/Test.java b/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/Test.java new file mode 100644 index 00000000000..12a77514c3d --- /dev/null +++ b/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/Test.java @@ -0,0 +1,28 @@ +import java.util.Map; +import java.util.AbstractMap; +import java.util.Collection; +import java.util.AbstractCollection; +import java.util.List; +import java.util.AbstractList; + +public class Test { + + public static void test( + Map p1, + AbstractMap p2, + Collection p3, + AbstractCollection p4, + List p5, + AbstractList p6) { + + // Use a method of each to ensure method prototypes are extracted: + p1.remove("Hello"); + p2.remove("Hello"); + p3.remove("Hello"); + p4.remove("Hello"); + p5.remove("Hello"); + p6.remove("Hello"); + + } + +} diff --git a/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.expected b/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.expected new file mode 100644 index 00000000000..8b681d0f785 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.expected @@ -0,0 +1,412 @@ +methodWithDuplicate +#select +| AbstractCollection | add | E | +| AbstractCollection | addAll | Collection | +| AbstractCollection | contains | Object | +| AbstractCollection | containsAll | Collection | +| AbstractCollection | remove | Object | +| AbstractCollection | removeAll | Collection | +| AbstractCollection | retainAll | Collection | +| AbstractCollection | toArray | T[] | +| AbstractCollection | add | E | +| AbstractCollection | addAll | Collection | +| AbstractCollection | contains | Object | +| AbstractCollection | containsAll | Collection | +| AbstractCollection | remove | Object | +| AbstractCollection | removeAll | Collection | +| AbstractCollection | retainAll | Collection | +| AbstractCollection | toArray | T[] | +| AbstractCollection | add | String | +| AbstractCollection | addAll | Collection | +| AbstractCollection | contains | Object | +| AbstractCollection | containsAll | Collection | +| AbstractCollection | remove | Object | +| AbstractCollection | removeAll | Collection | +| AbstractCollection | retainAll | Collection | +| AbstractCollection | toArray | T[] | +| AbstractList | add | E | +| AbstractList | add | int | +| AbstractList | addAll | Collection | +| AbstractList | addAll | int | +| AbstractList | equals | Object | +| AbstractList | get | int | +| AbstractList | indexOf | Object | +| AbstractList | lastIndexOf | Object | +| AbstractList | listIterator | int | +| AbstractList | removeAt | int | +| AbstractList | removeRange | int | +| AbstractList | set | E | +| AbstractList | set | int | +| AbstractList | subList | int | +| AbstractList | subListRangeCheck | int | +| AbstractList | add | E | +| AbstractList | add | int | +| AbstractList | addAll | Collection | +| AbstractList | addAll | int | +| AbstractList | equals | Object | +| AbstractList | get | int | +| AbstractList | indexOf | Object | +| AbstractList | lastIndexOf | Object | +| AbstractList | listIterator | int | +| AbstractList | removeAt | int | +| AbstractList | removeRange | int | +| AbstractList | set | E | +| AbstractList | set | int | +| AbstractList | subList | int | +| AbstractList | subListRangeCheck | int | +| AbstractMap | containsEntry | Entry | +| AbstractMap | containsKey | Object | +| AbstractMap | containsValue | Object | +| AbstractMap | equals | Object | +| AbstractMap | get | Object | +| AbstractMap | put | K | +| AbstractMap | put | V | +| AbstractMap | putAll | Map | +| AbstractMap | remove | Object | +| AbstractMap> | containsKey | Object | +| AbstractMap> | containsValue | Object | +| AbstractMap> | equals | Object | +| AbstractMap> | get | Object | +| AbstractMap> | put | Entry | +| AbstractMap> | put | Identity | +| AbstractMap> | putAll | Map> | +| AbstractMap> | remove | Object | +| AbstractMap | containsKey | Object | +| AbstractMap | containsValue | Object | +| AbstractMap | equals | Object | +| AbstractMap | get | Object | +| AbstractMap | put | K | +| AbstractMap | put | V | +| AbstractMap | putAll | Map | +| AbstractMap | remove | Object | +| AbstractMap | containsEntry | Entry | +| AbstractMap | containsKey | Object | +| AbstractMap | containsValue | Object | +| AbstractMap | equals | Object | +| AbstractMap | get | Object | +| AbstractMap | put | String | +| AbstractMap | putAll | Map | +| AbstractMap | remove | Object | +| AbstractMutableCollection | add | E | +| AbstractMutableList | add | E | +| AbstractMutableList | add | int | +| AbstractMutableList | removeAt | int | +| AbstractMutableList | set | E | +| AbstractMutableList | set | int | +| AbstractMutableMap | put | K | +| AbstractMutableMap | put | V | +| Collection | add | E | +| Collection | addAll | Collection | +| Collection | contains | Object | +| Collection | containsAll | Collection | +| Collection | equals | Object | +| Collection | remove | Object | +| Collection | removeAll | Collection | +| Collection | removeIf | Predicate | +| Collection | retainAll | Collection | +| Collection | toArray | IntFunction | +| Collection | toArray | T[] | +| Collection | add | E | +| Collection | addAll | Collection | +| Collection | contains | Object | +| Collection | containsAll | Collection | +| Collection | remove | Object | +| Collection | removeAll | Collection | +| Collection | removeIf | Predicate | +| Collection | retainAll | Collection | +| Collection | toArray | IntFunction | +| Collection | toArray | T[] | +| Collection> | add | Entry | +| Collection> | addAll | Collection> | +| Collection> | contains | Object | +| Collection> | containsAll | Collection | +| Collection> | remove | Object | +| Collection> | removeAll | Collection | +| Collection> | removeIf | Predicate> | +| Collection> | retainAll | Collection | +| Collection> | toArray | IntFunction | +| Collection> | toArray | T[] | +| Collection | add | K | +| Collection | addAll | Collection | +| Collection | contains | Object | +| Collection | containsAll | Collection | +| Collection | remove | Object | +| Collection | removeAll | Collection | +| Collection | removeIf | Predicate | +| Collection | retainAll | Collection | +| Collection | toArray | IntFunction | +| Collection | toArray | T[] | +| Collection | add | String | +| Collection | addAll | Collection | +| Collection | contains | Object | +| Collection | containsAll | Collection | +| Collection | equals | Object | +| Collection | remove | Object | +| Collection | removeAll | Collection | +| Collection | removeIf | Predicate | +| Collection | retainAll | Collection | +| Collection | toArray | IntFunction | +| Collection | toArray | T[] | +| Collection | add | V | +| Collection | addAll | Collection | +| Collection | contains | Object | +| Collection | containsAll | Collection | +| Collection | remove | Object | +| Collection | removeAll | Collection | +| Collection | removeIf | Predicate | +| Collection | retainAll | Collection | +| Collection | toArray | IntFunction | +| Collection | toArray | T[] | +| List | add | E | +| List | add | int | +| List | addAll | Collection | +| List | addAll | int | +| List | contains | Object | +| List | containsAll | Collection | +| List | copyOf | Collection | +| List | equals | Object | +| List | get | int | +| List | indexOf | Object | +| List | lastIndexOf | Object | +| List | listIterator | int | +| List | of | E | +| List | of | E[] | +| List | remove | Object | +| List | remove | int | +| List | removeAll | Collection | +| List | replaceAll | UnaryOperator | +| List | retainAll | Collection | +| List | set | E | +| List | set | int | +| List | sort | Comparator | +| List | subList | int | +| List | toArray | T[] | +| List | add | E | +| List | add | int | +| List | addAll | Collection | +| List | addAll | int | +| List | contains | Object | +| List | containsAll | Collection | +| List | copyOf | Collection | +| List | get | int | +| List | indexOf | Object | +| List | lastIndexOf | Object | +| List | listIterator | int | +| List | of | E | +| List | of | E[] | +| List | remove | Object | +| List | remove | int | +| List | removeAll | Collection | +| List | replaceAll | UnaryOperator | +| List | retainAll | Collection | +| List | set | E | +| List | set | int | +| List | sort | Comparator | +| List | subList | int | +| List | toArray | T[] | +| List | add | String | +| List | add | int | +| List | addAll | Collection | +| List | addAll | int | +| List | contains | Object | +| List | containsAll | Collection | +| List | copyOf | Collection | +| List | equals | Object | +| List | get | int | +| List | indexOf | Object | +| List | lastIndexOf | Object | +| List | listIterator | int | +| List | of | E | +| List | of | E[] | +| List | remove | Object | +| List | remove | int | +| List | removeAll | Collection | +| List | replaceAll | UnaryOperator | +| List | retainAll | Collection | +| List | set | String | +| List | set | int | +| List | sort | Comparator | +| List | subList | int | +| List | toArray | T[] | +| Map | compute | BiFunction | +| Map | compute | K | +| Map | computeIfAbsent | Function | +| Map | computeIfAbsent | K | +| Map | computeIfPresent | BiFunction | +| Map | computeIfPresent | K | +| Map | containsKey | Object | +| Map | containsValue | Object | +| Map | copyOf | Map | +| Map | entry | K | +| Map | entry | V | +| Map | equals | Object | +| Map | forEach | BiConsumer | +| Map | get | Object | +| Map | getOrDefault | Object | +| Map | getOrDefault | V | +| Map | merge | BiFunction | +| Map | merge | K | +| Map | merge | V | +| Map | of | K | +| Map | of | V | +| Map | ofEntries | Entry[] | +| Map | put | K | +| Map | put | V | +| Map | putAll | Map | +| Map | putIfAbsent | K | +| Map | putIfAbsent | V | +| Map | remove | Object | +| Map | replace | K | +| Map | replace | V | +| Map | replaceAll | BiFunction | +| Map> | compute | BiFunction,? extends Entry> | +| Map> | compute | Identity | +| Map> | computeIfAbsent | Function> | +| Map> | computeIfAbsent | Identity | +| Map> | computeIfPresent | BiFunction,? extends Entry> | +| Map> | computeIfPresent | Identity | +| Map> | containsKey | Object | +| Map> | containsValue | Object | +| Map> | copyOf | Map | +| Map> | entry | K | +| Map> | entry | V | +| Map> | forEach | BiConsumer> | +| Map> | get | Object | +| Map> | getOrDefault | Entry | +| Map> | getOrDefault | Object | +| Map> | merge | BiFunction,? super Entry,? extends Entry> | +| Map> | merge | Entry | +| Map> | merge | Identity | +| Map> | of | K | +| Map> | of | V | +| Map> | ofEntries | Entry[] | +| Map> | put | Entry | +| Map> | put | Identity | +| Map> | putAll | Map> | +| Map> | putIfAbsent | Entry | +| Map> | putIfAbsent | Identity | +| Map> | remove | Object | +| Map> | replace | Entry | +| Map> | replace | Identity | +| Map> | replaceAll | BiFunction,? extends Entry> | +| Map | compute | BiFunction | +| Map | compute | K | +| Map | computeIfAbsent | Function | +| Map | computeIfAbsent | K | +| Map | computeIfPresent | BiFunction | +| Map | computeIfPresent | K | +| Map | containsKey | Object | +| Map | containsValue | Object | +| Map | copyOf | Map | +| Map | entry | K | +| Map | entry | V | +| Map | forEach | BiConsumer | +| Map | get | Object | +| Map | getOrDefault | Object | +| Map | getOrDefault | V | +| Map | merge | BiFunction | +| Map | merge | K | +| Map | merge | V | +| Map | of | K | +| Map | of | V | +| Map | ofEntries | Entry[] | +| Map | put | K | +| Map | put | V | +| Map | putAll | Map | +| Map | putIfAbsent | K | +| Map | putIfAbsent | V | +| Map | remove | Object | +| Map | replace | K | +| Map | replace | V | +| Map | replaceAll | BiFunction | +| Map | compute | BiFunction | +| Map | compute | Object | +| Map | computeIfAbsent | Function | +| Map | computeIfAbsent | Object | +| Map | computeIfPresent | BiFunction | +| Map | computeIfPresent | Object | +| Map | containsKey | Object | +| Map | containsValue | Object | +| Map | copyOf | Map | +| Map | entry | K | +| Map | entry | V | +| Map | forEach | BiConsumer | +| Map | get | Object | +| Map | getOrDefault | Object | +| Map | merge | BiFunction | +| Map | merge | Object | +| Map | of | K | +| Map | of | V | +| Map | ofEntries | Entry[] | +| Map | put | Object | +| Map | putAll | Map | +| Map | putIfAbsent | Object | +| Map | remove | Object | +| Map | replace | Object | +| Map | replaceAll | BiFunction | +| Map | compute | BiFunction | +| Map | compute | String | +| Map | computeIfAbsent | Function | +| Map | computeIfAbsent | String | +| Map | computeIfPresent | BiFunction | +| Map | computeIfPresent | String | +| Map | containsKey | Object | +| Map | containsValue | Object | +| Map | copyOf | Map | +| Map | entry | K | +| Map | entry | V | +| Map | equals | Object | +| Map | forEach | BiConsumer | +| Map | get | Object | +| Map | getOrDefault | Object | +| Map | getOrDefault | String | +| Map | merge | BiFunction | +| Map | merge | String | +| Map | of | K | +| Map | of | V | +| Map | ofEntries | Entry[] | +| Map | put | String | +| Map | putAll | Map | +| Map | putIfAbsent | String | +| Map | remove | Object | +| Map | replace | String | +| Map | replaceAll | BiFunction | +| MutableCollection | add | E | +| MutableCollection | addAll | Collection | +| MutableCollection | remove | Object | +| MutableCollection | removeAll | Collection | +| MutableCollection | removeIf | Predicate | +| MutableCollection | retainAll | Collection | +| MutableList | add | E | +| MutableList | add | int | +| MutableList | addAll | Collection | +| MutableList | addAll | Collection | +| MutableList | addAll | int | +| MutableList | listIterator | int | +| MutableList | remove | Object | +| MutableList | removeAll | Collection | +| MutableList | removeAt | int | +| MutableList | replaceAll | UnaryOperator | +| MutableList | retainAll | Collection | +| MutableList | set | E | +| MutableList | set | int | +| MutableList | sort | Comparator | +| MutableList | subList | int | +| MutableMap | compute | BiFunction | +| MutableMap | compute | K | +| MutableMap | computeIfAbsent | Function | +| MutableMap | computeIfAbsent | K | +| MutableMap | computeIfPresent | BiFunction | +| MutableMap | computeIfPresent | K | +| MutableMap | merge | BiFunction | +| MutableMap | merge | K | +| MutableMap | merge | V | +| MutableMap | put | K | +| MutableMap | put | V | +| MutableMap | putAll | Map | +| MutableMap | putIfAbsent | K | +| MutableMap | putIfAbsent | V | +| MutableMap | remove | Object | +| MutableMap | replace | K | +| MutableMap | replace | V | +| MutableMap | replaceAll | BiFunction | diff --git a/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.kt b/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.kt new file mode 100644 index 00000000000..a597bbb8490 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.kt @@ -0,0 +1,29 @@ +fun test( + p1: Map, + p2: AbstractMap, + p3: Collection, + p4: AbstractCollection, + p5: List, + p6: AbstractList, + p7: MutableMap, + p8: AbstractMutableMap, + p9: MutableCollection, + p10: AbstractMutableCollection, + p11: MutableList, + p12: AbstractMutableList) { + + // Use a method of each to ensure method prototypes are extracted: + p1.get("Hello"); + p2.get("Hello"); + p3.contains("Hello"); + p4.contains("Hello"); + p5.contains("Hello"); + p6.contains("Hello"); + p7.remove("Hello"); + p8.remove("Hello"); + p9.remove("Hello"); + p10.remove("Hello"); + p11.remove("Hello"); + p12.remove("Hello"); + +} diff --git a/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.ql b/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.ql new file mode 100644 index 00000000000..f43ca177689 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.ql @@ -0,0 +1,28 @@ +import java + +RefType getARelevantCollectionType() { + result.hasQualifiedName(["java.util", "kotlin.collections"], ["Abstract", ""] + ["Mutable", ""] + ["Collection", "List", "Map"]) +} + +class RelevantMethod extends Method { + + RelevantMethod() { this.getDeclaringType().getSourceDeclaration() = getARelevantCollectionType() } + +} + +// Check for methods with suspicious twins -- probably another extraction of the same method outline which was given a different trap key. +// It so happens the collections methods of interest to this test don't use overloads with the same parameter count. +query predicate methodWithDuplicate(string methodName, string typeName) { + exists(RelevantMethod m, RelevantMethod dup | + dup.getName() = m.getName() and + not dup.getName() = ["of", "remove", "toArray"] and // These really do have overloads with the same parameter count, so it isn't trivial to tell if they are intentional overloads or inappropriate duplicates. + dup.getNumberOfParameters() = m.getNumberOfParameters() and + dup.getDeclaringType() = m.getDeclaringType() and + dup != m and + methodName = m.getName() and + typeName = m.getDeclaringType().getName() + ) +} + +from RelevantMethod m +select m.getDeclaringType().getName(), m.getName(), m.getAParamType().getName() From 8c12a7289ffd876357f3f718a67911c7258d46c3 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 27 May 2022 13:19:06 +0200 Subject: [PATCH 101/125] collapse a few small stages --- javascript/ql/lib/semmle/javascript/NodeJS.qll | 2 ++ .../ql/lib/semmle/javascript/internal/CachedStages.qll | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/javascript/ql/lib/semmle/javascript/NodeJS.qll b/javascript/ql/lib/semmle/javascript/NodeJS.qll index 038ca7b2186..c937a730f2d 100644 --- a/javascript/ql/lib/semmle/javascript/NodeJS.qll +++ b/javascript/ql/lib/semmle/javascript/NodeJS.qll @@ -3,6 +3,7 @@ import javascript private import NodeModuleResolutionImpl private import semmle.javascript.DynamicPropertyAccess as DynamicPropertyAccess +private import semmle.javascript.internal.CachedStages /** * A Node.js module. @@ -113,6 +114,7 @@ class NodeModule extends Module { } override DataFlow::Node getABulkExportedNode() { + Stages::Imports::ref() and exists(DataFlow::PropWrite write | write.getBase().asExpr() = this.getModuleVariable().getAnAccess() and write.getPropertyName() = "exports" and diff --git a/javascript/ql/lib/semmle/javascript/internal/CachedStages.qll b/javascript/ql/lib/semmle/javascript/internal/CachedStages.qll index a53710a9e43..97193953c86 100644 --- a/javascript/ql/lib/semmle/javascript/internal/CachedStages.qll +++ b/javascript/ql/lib/semmle/javascript/internal/CachedStages.qll @@ -176,6 +176,8 @@ module Stages { exists(DataFlow::moduleImport(_)) or exists(any(ReExportDeclaration d).getReExportedModule()) + or + exists(any(Module m).getABulkExportedNode()) } } @@ -276,6 +278,9 @@ module Stages { .getInstance() .getReceiver() .getPromisedError() + .getADecoratedClass() + .getADecoratedMember() + .getADecoratedParameter() ) } } From 9c62b349ece760828e667d4be449d8f05995a058 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Fri, 27 May 2022 13:36:55 +0100 Subject: [PATCH 102/125] Autoformat --- .../java-kotlin-collection-type-generic-methods/test.ql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.ql b/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.ql index f43ca177689..22a78af1dea 100644 --- a/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.ql +++ b/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.ql @@ -1,13 +1,13 @@ import java RefType getARelevantCollectionType() { - result.hasQualifiedName(["java.util", "kotlin.collections"], ["Abstract", ""] + ["Mutable", ""] + ["Collection", "List", "Map"]) + result + .hasQualifiedName(["java.util", "kotlin.collections"], + ["Abstract", ""] + ["Mutable", ""] + ["Collection", "List", "Map"]) } class RelevantMethod extends Method { - RelevantMethod() { this.getDeclaringType().getSourceDeclaration() = getARelevantCollectionType() } - } // Check for methods with suspicious twins -- probably another extraction of the same method outline which was given a different trap key. @@ -18,7 +18,7 @@ query predicate methodWithDuplicate(string methodName, string typeName) { not dup.getName() = ["of", "remove", "toArray"] and // These really do have overloads with the same parameter count, so it isn't trivial to tell if they are intentional overloads or inappropriate duplicates. dup.getNumberOfParameters() = m.getNumberOfParameters() and dup.getDeclaringType() = m.getDeclaringType() and - dup != m and + dup != m and methodName = m.getName() and typeName = m.getDeclaringType().getName() ) From 468a4df2159f3d951f26142909fe995310e1cfe7 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 27 May 2022 15:55:25 +0200 Subject: [PATCH 103/125] Update javascript/ql/lib/semmle/javascript/security/dataflow/TypeConfusionThroughParameterTamperingQuery.qll Co-authored-by: Erik Krogh Kristensen --- .../TypeConfusionThroughParameterTamperingQuery.qll | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/TypeConfusionThroughParameterTamperingQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/TypeConfusionThroughParameterTamperingQuery.qll index 1b6cc31f653..8e24b3e36d8 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/TypeConfusionThroughParameterTamperingQuery.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/TypeConfusionThroughParameterTamperingQuery.qll @@ -36,13 +36,11 @@ class Configuration extends DataFlow::Configuration { private class TypeOfTestBarrier extends DataFlow::BarrierGuardNode, DataFlow::ValueNode { override EqualityTest astNode; - private Expr operand; - TypeOfTestBarrier() { astNode.getAnOperand().(TypeofExpr).getOperand() = operand } + TypeOfTestBarrier() { TaintTracking::isTypeofGuard(astNode, _, _) } override predicate blocks(boolean outcome, Expr e) { - e = operand and - if astNode.getAnOperand().getStringValue() = ["string", "object"] + if TaintTracking::isTypeofGuard(astNode, e, ["string", "object"]) then outcome = [true, false] // separation between string/array removes type confusion in both branches else outcome = astNode.getPolarity() // block flow to branch where value is neither string nor array } From 62fd3fd90f10fb8b09c09934804440f5e2722fc1 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 27 May 2022 14:15:27 +0000 Subject: [PATCH 104/125] add test that we detect the used type variable in an infer type --- .../Declarations/UnusedVariable/UnusedVariable.expected | 1 + .../query-tests/Declarations/UnusedVariable/interTypes.ts | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 javascript/ql/test/query-tests/Declarations/UnusedVariable/interTypes.ts diff --git a/javascript/ql/test/query-tests/Declarations/UnusedVariable/UnusedVariable.expected b/javascript/ql/test/query-tests/Declarations/UnusedVariable/UnusedVariable.expected index 9db0d903c2d..73be1f62b89 100644 --- a/javascript/ql/test/query-tests/Declarations/UnusedVariable/UnusedVariable.expected +++ b/javascript/ql/test/query-tests/Declarations/UnusedVariable/UnusedVariable.expected @@ -5,6 +5,7 @@ | eval.js:19:9:19:24 | not_used_by_eval | Unused variable not_used_by_eval. | | externs.js:6:5:6:13 | iAmUnused | Unused variable iAmUnused. | | importWithoutPragma.jsx:1:1:1:27 | import ... react'; | Unused import h. | +| interTypes.ts:1:1:1:37 | import ... where"; | Unused import Bar. | | multi-imports.js:1:1:1:29 | import ... om 'x'; | Unused imports a, b, d. | | multi-imports.js:2:1:2:42 | import ... om 'x'; | Unused imports alphabetically, ordered. | | namespaceImportAsType.ts:3:1:3:23 | import ... om "z"; | Unused import Z. | diff --git a/javascript/ql/test/query-tests/Declarations/UnusedVariable/interTypes.ts b/javascript/ql/test/query-tests/Declarations/UnusedVariable/interTypes.ts new file mode 100644 index 00000000000..bdcd767fae8 --- /dev/null +++ b/javascript/ql/test/query-tests/Declarations/UnusedVariable/interTypes.ts @@ -0,0 +1,6 @@ +import { Foo, Bar } from "somewhere"; // OK + +type FooBar = + T extends [infer S extends Foo, ...unknown[]] + ? S + : never; From 9ea139566df2a08b2cec558d75a165fdc2a9d14b Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Fri, 27 May 2022 16:26:51 +0100 Subject: [PATCH 105/125] Disambiguate the names and trap labels of backing fields of extension properties --- .../src/main/kotlin/KotlinFileExtractor.kt | 3 ++- .../src/main/kotlin/KotlinUsesExtractor.kt | 14 +++++++++++++- .../clashing-extension-fields/test.expected | 9 +++++++++ .../clashing-extension-fields/test.kt | 7 +++++++ .../clashing-extension-fields/test.ql | 5 +++++ 5 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 java/ql/test/kotlin/library-tests/clashing-extension-fields/test.expected create mode 100644 java/ql/test/kotlin/library-tests/clashing-extension-fields/test.kt create mode 100644 java/ql/test/kotlin/library-tests/clashing-extension-fields/test.ql diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index d381704e28f..e32e5ef1974 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -759,7 +759,8 @@ open class KotlinFileExtractor( with("field", f) { DeclarationStackAdjuster(f).use { declarationStack.push(f) - return extractField(useField(f), f.name.asString(), f.type, parentId, tw.getLocation(f), f.visibility, f, isExternalDeclaration(f), f.isFinal) + val fNameSuffix = getExtensionReceiverType(f)?.let { it.classFqName?.asString()?.replace(".", "$$") } ?: "" + return extractField(useField(f), "${f.name.asString()}$fNameSuffix", f.type, parentId, tw.getLocation(f), f.visibility, f, isExternalDeclaration(f), f.isFinal) } } } diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt index 8bbcb2ee2dd..14515cb85ed 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt @@ -1269,6 +1269,7 @@ open class KotlinUsesExtractor( fun useValueParameter(vp: IrValueParameter, parent: Label?): Label = tw.getLabelFor(getValueParameterLabel(vp, parent)) + fun isDirectlyExposedCompanionObjectField(f: IrField) = f.hasAnnotation(FqName("kotlin.jvm.JvmField")) || f.correspondingPropertySymbol?.owner?.let { @@ -1283,9 +1284,20 @@ open class KotlinUsesExtractor( null } ?: f.parent + // Gets a field's corresponding property's extension receiver type, if any + fun getExtensionReceiverType(f: IrField) = + f.correspondingPropertySymbol?.owner?.let { + (it.getter ?: it.setter)?.extensionReceiverParameter?.type + } + fun getFieldLabel(f: IrField): String { val parentId = useDeclarationParent(getFieldParent(f), false) - return "@\"field;{$parentId};${f.name.asString()}\"" + // Distinguish backing fields of properties based on their extension receiver type; + // otherwise two extension properties declared in the same enclosing context will get + // clashing trap labels. These are always private, so we can just make up a label without + // worrying about their names as seen from Java. + val extensionPropertyDiscriminator = getExtensionReceiverType(f)?.let { "extension;${useType(it)}" } ?: "" + return "@\"field;{$parentId};${extensionPropertyDiscriminator}${f.name.asString()}\"" } fun useField(f: IrField): Label = diff --git a/java/ql/test/kotlin/library-tests/clashing-extension-fields/test.expected b/java/ql/test/kotlin/library-tests/clashing-extension-fields/test.expected new file mode 100644 index 00000000000..72fe7124394 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/clashing-extension-fields/test.expected @@ -0,0 +1,9 @@ +| | +| | +| A | +| B | +| get | +| getX | +| invoke | +| x$delegatepackagename$$subpackagename$$A | +| x$delegatepackagename$$subpackagename$$B | diff --git a/java/ql/test/kotlin/library-tests/clashing-extension-fields/test.kt b/java/ql/test/kotlin/library-tests/clashing-extension-fields/test.kt new file mode 100644 index 00000000000..9a7c5d51801 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/clashing-extension-fields/test.kt @@ -0,0 +1,7 @@ +package packagename.subpackagename + +public class A { } +public class B { } + +val A.x : String by lazy { "HelloA" } +val B.x : String by lazy { "HelloB" } diff --git a/java/ql/test/kotlin/library-tests/clashing-extension-fields/test.ql b/java/ql/test/kotlin/library-tests/clashing-extension-fields/test.ql new file mode 100644 index 00000000000..4c0a8d53b1f --- /dev/null +++ b/java/ql/test/kotlin/library-tests/clashing-extension-fields/test.ql @@ -0,0 +1,5 @@ +import java + +from Class c +where c.fromSource() +select c.getAMember().toString() From 6ea87cd71841aeb4301268332280d1096afe5329 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Fri, 27 May 2022 22:05:57 +0100 Subject: [PATCH 106/125] Accept test changes --- java/ql/test/kotlin/library-tests/exprs/PrintAst.expected | 6 +++--- .../kotlin/library-tests/exprs/delegatedProperties.expected | 2 +- java/ql/test/kotlin/library-tests/exprs/exprs.expected | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/java/ql/test/kotlin/library-tests/exprs/PrintAst.expected b/java/ql/test/kotlin/library-tests/exprs/PrintAst.expected index 4a93ee5df3b..4fdd6b4d718 100644 --- a/java/ql/test/kotlin/library-tests/exprs/PrintAst.expected +++ b/java/ql/test/kotlin/library-tests/exprs/PrintAst.expected @@ -31,7 +31,7 @@ delegatedProperties.kt: # 87| 0: [MethodAccess] getValue(...) # 87| -2: [TypeAccess] Integer # 87| -1: [TypeAccess] PropertyReferenceDelegatesKt -# 87| 0: [VarAccess] DelegatedPropertiesKt.extDelegated$delegate +# 87| 0: [VarAccess] DelegatedPropertiesKt.extDelegated$delegateMyClass # 87| -1: [TypeAccess] DelegatedPropertiesKt # 1| 1: [ExtensionReceiverAccess] this # 87| 2: [PropertyRefExpr] ...::... @@ -80,7 +80,7 @@ delegatedProperties.kt: # 87| 0: [MethodAccess] setValue(...) # 87| -2: [TypeAccess] Integer # 87| -1: [TypeAccess] PropertyReferenceDelegatesKt -# 87| 0: [VarAccess] DelegatedPropertiesKt.extDelegated$delegate +# 87| 0: [VarAccess] DelegatedPropertiesKt.extDelegated$delegateMyClass # 87| -1: [TypeAccess] DelegatedPropertiesKt # 1| 1: [ExtensionReceiverAccess] this # 87| 2: [PropertyRefExpr] ...::... @@ -118,7 +118,7 @@ delegatedProperties.kt: # 87| 0: [TypeAccess] MyClass # 87| 1: [TypeAccess] Integer # 87| 3: [VarAccess] -# 87| 5: [FieldDeclaration] KMutableProperty0 extDelegated$delegate; +# 87| 5: [FieldDeclaration] KMutableProperty0 extDelegated$delegateMyClass; # 87| -1: [TypeAccess] KMutableProperty0 # 87| 0: [TypeAccess] Integer # 87| 0: [PropertyRefExpr] ...::... diff --git a/java/ql/test/kotlin/library-tests/exprs/delegatedProperties.expected b/java/ql/test/kotlin/library-tests/exprs/delegatedProperties.expected index c60b200a2eb..7141b6d1531 100644 --- a/java/ql/test/kotlin/library-tests/exprs/delegatedProperties.expected +++ b/java/ql/test/kotlin/library-tests/exprs/delegatedProperties.expected @@ -16,7 +16,7 @@ delegatedProperties | delegatedProperties.kt:77:5:77:49 | delegatedToTopLevel | delegatedToTopLevel | non-local | delegatedProperties.kt:77:34:77:49 | delegatedToTopLevel$delegate | delegatedProperties.kt:77:37:77:49 | ...::... | | delegatedProperties.kt:79:5:79:38 | max | max | non-local | delegatedProperties.kt:79:18:79:38 | max$delegate | delegatedProperties.kt:79:21:79:38 | ...::... | | delegatedProperties.kt:82:9:82:54 | delegatedToMember3 | delegatedToMember3 | local | delegatedProperties.kt:82:37:82:54 | KMutableProperty0 delegatedToMember3$delegate | delegatedProperties.kt:82:40:82:54 | ...::... | -| delegatedProperties.kt:87:1:87:46 | extDelegated | extDelegated | non-local | delegatedProperties.kt:87:31:87:46 | extDelegated$delegate | delegatedProperties.kt:87:34:87:46 | ...::... | +| delegatedProperties.kt:87:1:87:46 | extDelegated | extDelegated | non-local | delegatedProperties.kt:87:31:87:46 | extDelegated$delegateMyClass | delegatedProperties.kt:87:34:87:46 | ...::... | delegatedPropertyTypes | delegatedProperties.kt:6:9:9:9 | prop1 | file://:0:0:0:0 | int | file:///Lazy.class:0:0:0:0 | Lazy | | delegatedProperties.kt:19:9:19:51 | varResource1 | file://:0:0:0:0 | int | delegatedProperties.kt:45:1:51:1 | ResourceDelegate | diff --git a/java/ql/test/kotlin/library-tests/exprs/exprs.expected b/java/ql/test/kotlin/library-tests/exprs/exprs.expected index a2a1e3bd941..110d9e47147 100644 --- a/java/ql/test/kotlin/library-tests/exprs/exprs.expected +++ b/java/ql/test/kotlin/library-tests/exprs/exprs.expected @@ -830,9 +830,9 @@ | delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt | delegatedProperties.kt:87:31:87:46 | set | TypeAccess | | delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt | delegatedProperties.kt:87:31:87:46 | set | TypeAccess | | delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt | delegatedProperties.kt:87:31:87:46 | setExtDelegated | TypeAccess | -| delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt.extDelegated$delegate | delegatedProperties.kt:0:0:0:0 | | VarAccess | -| delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt.extDelegated$delegate | delegatedProperties.kt:87:31:87:46 | getExtDelegated | VarAccess | -| delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt.extDelegated$delegate | delegatedProperties.kt:87:31:87:46 | setExtDelegated | VarAccess | +| delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt.extDelegated$delegateMyClass | delegatedProperties.kt:0:0:0:0 | | VarAccess | +| delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt.extDelegated$delegateMyClass | delegatedProperties.kt:87:31:87:46 | getExtDelegated | VarAccess | +| delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt.extDelegated$delegateMyClass | delegatedProperties.kt:87:31:87:46 | setExtDelegated | VarAccess | | delegatedProperties.kt:87:31:87:46 | Integer | delegatedProperties.kt:87:31:87:46 | getExtDelegated | TypeAccess | | delegatedProperties.kt:87:31:87:46 | Integer | delegatedProperties.kt:87:31:87:46 | setExtDelegated | TypeAccess | | delegatedProperties.kt:87:31:87:46 | Integer | file://:0:0:0:0 | | TypeAccess | From 31c91a6faa9ee6814c093a66f0b5cb5ecf2dc85c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 29 May 2022 00:16:56 +0000 Subject: [PATCH 107/125] Add changed framework coverage reports --- .../library-coverage/coverage.csv | 23 ++++++++++++++++--- .../library-coverage/coverage.rst | 6 ++--- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/csharp/documentation/library-coverage/coverage.csv b/csharp/documentation/library-coverage/coverage.csv index 4d9996c1446..2bc6ca863ac 100644 --- a/csharp/documentation/library-coverage/coverage.csv +++ b/csharp/documentation/library-coverage/coverage.csv @@ -1,10 +1,27 @@ package,sink,source,summary,sink:code,sink:html,sink:remote,sink:sql,sink:xss,source:local,summary:taint,summary:value Dapper,55,,,,,,55,,,, +JsonToItemsTaskFactory,,,7,,,,,,,7, Microsoft.ApplicationBlocks.Data,28,,,,,,28,,,, +Microsoft.CSharp,,,24,,,,,,,24, Microsoft.EntityFrameworkCore,6,,,,,,6,,,, -Microsoft.Extensions.Primitives,,,54,,,,,,,54, -Microsoft.VisualBasic,,,4,,,,,,,,4 +Microsoft.Extensions.Caching.Distributed,,,15,,,,,,,15, +Microsoft.Extensions.Caching.Memory,,,46,,,,,,,45,1 +Microsoft.Extensions.Configuration,,,83,,,,,,,80,3 +Microsoft.Extensions.DependencyInjection,,,62,,,,,,,62, +Microsoft.Extensions.DependencyModel,,,12,,,,,,,12, +Microsoft.Extensions.FileProviders,,,15,,,,,,,15, +Microsoft.Extensions.FileSystemGlobbing,,,15,,,,,,,13,2 +Microsoft.Extensions.Hosting,,,17,,,,,,,16,1 +Microsoft.Extensions.Http,,,10,,,,,,,10, +Microsoft.Extensions.Logging,,,37,,,,,,,37, +Microsoft.Extensions.Options,,,8,,,,,,,8, +Microsoft.Extensions.Primitives,,,63,,,,,,,63, +Microsoft.Interop,,,27,,,,,,,27, +Microsoft.NET.Build.Tasks,,,1,,,,,,,1, +Microsoft.NETCore.Platforms.BuildTasks,,,4,,,,,,,4, +Microsoft.VisualBasic,,,9,,,,,,,5,4 +Microsoft.Win32,,,8,,,,,,,8, MySql.Data.MySqlClient,48,,,,,,48,,,, Newtonsoft.Json,,,91,,,,,,,73,18 ServiceStack,194,,7,27,,75,92,,,7, -System,28,3,2336,,4,,23,1,3,611,1725 +System,28,3,12038,,4,,23,1,3,10096,1942 diff --git a/csharp/documentation/library-coverage/coverage.rst b/csharp/documentation/library-coverage/coverage.rst index 9863b503fbf..076d2078d4b 100644 --- a/csharp/documentation/library-coverage/coverage.rst +++ b/csharp/documentation/library-coverage/coverage.rst @@ -8,7 +8,7 @@ C# framework & library support Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE-079` :sub:`Cross-site scripting` `ServiceStack `_,"``ServiceStack.*``, ``ServiceStack``",,7,194, - System,"``System.*``, ``System``",3,2336,28,5 - Others,"``Dapper``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Primitives``, ``Microsoft.VisualBasic``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``",,149,137, - Totals,,3,2492,359,5 + System,"``System.*``, ``System``",3,12038,28,5 + Others,"``Dapper``, ``JsonToItemsTaskFactory``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.CSharp``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.NETCore.Platforms.BuildTasks``, ``Microsoft.VisualBasic``, ``Microsoft.Win32``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``",,554,137, + Totals,,3,12599,359,5 From 815dff338d1949d295d5a5ce7d29bcaae2f9b803 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 30 May 2022 09:44:39 +0200 Subject: [PATCH 108/125] Java: Update capture models meta data. --- java/ql/src/utils/model-generator/CaptureSinkModels.ql | 2 ++ java/ql/src/utils/model-generator/CaptureSourceModels.ql | 4 +++- java/ql/src/utils/model-generator/CaptureSummaryModels.ql | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/java/ql/src/utils/model-generator/CaptureSinkModels.ql b/java/ql/src/utils/model-generator/CaptureSinkModels.ql index 8fd2e4d2a30..fb04c4ede00 100644 --- a/java/ql/src/utils/model-generator/CaptureSinkModels.ql +++ b/java/ql/src/utils/model-generator/CaptureSinkModels.ql @@ -1,7 +1,9 @@ /** * @name Capture sink models. * @description Finds public methods that act as sinks as they flow into a a known sink. + * @kind diagnostic * @id java/utils/model-generator/sink-models + * @tags model-generator */ private import internal.CaptureModels diff --git a/java/ql/src/utils/model-generator/CaptureSourceModels.ql b/java/ql/src/utils/model-generator/CaptureSourceModels.ql index 49e9cb34990..d37cf5e78fb 100644 --- a/java/ql/src/utils/model-generator/CaptureSourceModels.ql +++ b/java/ql/src/utils/model-generator/CaptureSourceModels.ql @@ -1,7 +1,9 @@ /** * @name Capture source models. * @description Finds APIs that act as sources as they expose already known sources. - * @id java/utils/model-generator/sink-models + * @kind diagnostic + * @id java/utils/model-generator/source-models + * @tags model-generator */ private import internal.CaptureModels diff --git a/java/ql/src/utils/model-generator/CaptureSummaryModels.ql b/java/ql/src/utils/model-generator/CaptureSummaryModels.ql index 8f4eab20722..253c3d4ed46 100644 --- a/java/ql/src/utils/model-generator/CaptureSummaryModels.ql +++ b/java/ql/src/utils/model-generator/CaptureSummaryModels.ql @@ -1,7 +1,9 @@ /** * @name Capture summary models. * @description Finds applicable summary models to be used by other queries. + * @kind diagnostic * @id java/utils/model-generator/summary-models + * @tags model-generator */ private import internal.CaptureModels From 49d9d8e7d6a02bd22dcf2256b82970de507d2c8e Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Mon, 30 May 2022 09:59:11 +0100 Subject: [PATCH 109/125] Remove unused imports --- java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt index 5b9ac5e9a28..0813aee1869 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt @@ -5,7 +5,6 @@ import com.github.codeql.utils.versions.isRawType import com.semmle.extractor.java.OdasaOutput import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.backend.common.ir.allOverridden -import org.jetbrains.kotlin.backend.common.ir.isOverridableOrOverrides import org.jetbrains.kotlin.backend.common.lower.parentsWithSelf import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor import org.jetbrains.kotlin.builtins.StandardNames @@ -13,7 +12,6 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.interpreter.getLastOverridden import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.impl.* From e557d8839bae0dbc2dbb5d611da747f4afbeaf69 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 30 May 2022 12:21:42 +0200 Subject: [PATCH 110/125] have the Instance token just be an alias for ReturnValue --- .../python/frameworks/data/internal/ApiGraphModelsSpecific.qll | 2 +- python/ql/test/library-tests/frameworks/data/test.expected | 1 - python/ql/test/library-tests/frameworks/data/test.py | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll index 20d99eb0335..92f7dcbd50b 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -53,7 +53,7 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) { result = node.getMember(token.getAnArgument()) or token.getName() = "Instance" and - result = node.getASubclass*().getReturn() // In Python `Instance` is just an alias for `Subclass.ReturnValue` + result = node.getReturn() // In Python `Instance` is just an alias for `ReturnValue` or token.getName() = "Awaited" and result = node.getAwaited() diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index fcf94bb784e..68de6ecd878 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -68,7 +68,6 @@ isSource | test.py:39:11:39:20 | ControlFlowNode for Await | test-source | | test.py:41:8:41:27 | ControlFlowNode for Attribute() | test-source | | test.py:46:7:46:16 | ControlFlowNode for SubClass() | test-source | -| test.py:51:8:51:18 | ControlFlowNode for Sub2Class() | test-source | | test.py:53:7:53:16 | ControlFlowNode for Attribute() | test-source | | test.py:60:13:60:16 | ControlFlowNode for self | test-source | | test.py:60:24:60:28 | ControlFlowNode for named | test-source | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index 6ff4271dca2..ea1a6e0d4d4 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -48,7 +48,7 @@ sub = SubClass() class Sub2Class (CommonTokens.Class): pass -sub2 = Sub2Class() +sub2 = Sub2Class() # TODO: Currently not recognized as an instance of CommonTokens.Class val = inst.foo() From 2106d487857b8a0e4607e0f4b440a77a0f57cefd Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 30 May 2022 12:51:29 +0100 Subject: [PATCH 111/125] Swift: Add 'Argument.getIndex()' and use it in 'DataFlowDispatch'. --- .../dataflow/internal/DataFlowDispatch.qll | 24 +++++++------------ .../codeql/swift/elements/expr/Argument.qll | 3 +++ 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll index 889f85d7450..d416f16b583 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll @@ -44,9 +44,7 @@ class DataFlowCallable extends TDataFlowCallable { * inside library callables with a flow summary. */ class DataFlowCall extends ExprNode { - DataFlowCall() { - this.asExpr() instanceof CallExpr - } + DataFlowCall() { this.asExpr() instanceof CallExpr } /** Gets the enclosing callable. */ DataFlowCallable getEnclosingCallable() { none() } @@ -59,19 +57,20 @@ private module Cached { /** Gets a viable run-time target for the call `call`. */ cached - DataFlowCallable viableCallable(DataFlowCall call) { + DataFlowCallable viableCallable(DataFlowCall call) { result = TDataFlowFunc(call.asExpr().(CallExpr).getStaticTarget()) } cached newtype TArgumentPosition = TThisArgument() or - TPositionalArgument(int n) { n in [0 .. 100] } // we rely on default exprs generated in the caller for ordering. TODO: compute range properly. TODO: varargs? + // we rely on default exprs generated in the caller for ordering + TPositionalArgument(int n) { n = any(Argument arg).getIndex() } cached - newtype TParameterPosition = - TThisParameter() or - TPositionalParameter(int n) { n in [0 .. 100] } // TODO: compute range properly + newtype TParameterPosition = + TThisParameter() or + TPositionalParameter(int n) { n = any(Argument arg).getIndex() } } import Cached @@ -95,12 +94,9 @@ class ParameterPosition extends TParameterPosition { } class PositionalParameterPosition extends ParameterPosition, TPositionalParameter { - int getIndex() { - this = TPositionalParameter(result) - } + int getIndex() { this = TPositionalParameter(result) } } - /** An argument position. */ class ArgumentPosition extends TArgumentPosition { /** Gets a textual representation of this position. */ @@ -108,9 +104,7 @@ class ArgumentPosition extends TArgumentPosition { } class PositionalArgumentPosition extends ArgumentPosition, TPositionalArgument { - int getIndex() { - this = TPositionalArgument(result) - } + int getIndex() { this = TPositionalArgument(result) } } /** Holds if arguments at position `apos` match parameters at position `ppos`. */ diff --git a/swift/ql/lib/codeql/swift/elements/expr/Argument.qll b/swift/ql/lib/codeql/swift/elements/expr/Argument.qll index c2a25b877a4..88c11b14013 100644 --- a/swift/ql/lib/codeql/swift/elements/expr/Argument.qll +++ b/swift/ql/lib/codeql/swift/elements/expr/Argument.qll @@ -1,5 +1,8 @@ private import codeql.swift.generated.expr.Argument +private import codeql.swift.elements.expr.ApplyExpr class Argument extends ArgumentBase { override string toString() { result = this.getLabel() + ": " + this.getExpr().toString() } + + int getIndex() { any(ApplyExpr apply).getArgument(result) = this } } From 425d66e454309b6f918cd17d06e3c7fc4490281f Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 30 May 2022 12:52:48 +0100 Subject: [PATCH 112/125] Update swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll --- swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index 1fc8bb36812..6eb554f933c 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -74,8 +74,8 @@ private module Cached { or // use-use flow def.adjacentReadPair(nodeFrom.getCfgNode(), nodeTo.getCfgNode()) - // step from previous read to Phi node or + // step from previous read to Phi node localFlowSsaInput(nodeFrom, def, nodeTo.asDefinition()) ) } From ef31aec29eebe2cb16635a69eb009104322d1337 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 30 May 2022 12:54:36 +0100 Subject: [PATCH 113/125] Swift: Autoformat. --- swift/ql/lib/codeql/swift/dataflow/Ssa.qll | 13 ++++++------- .../swift/dataflow/internal/DataFlowPrivate.qll | 14 ++++---------- .../library-tests/dataflow/dataflow/DataFlow.ql | 3 +-- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll index b56e33d869f..0f67b0faa5a 100644 --- a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll +++ b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll @@ -73,7 +73,8 @@ module Ssa { } } - cached class PhiDefinition extends Definition, SsaImplCommon::PhiNode { + cached + class PhiDefinition extends Definition, SsaImplCommon::PhiNode { cached override Location getLocation() { exists(BasicBlock bb, int i | @@ -82,12 +83,10 @@ module Ssa { ) } - cached Definition getPhiInput(BasicBlock bb) { - SsaImplCommon::phiHasInputFromBlock(this, result, bb) - } + cached + Definition getPhiInput(BasicBlock bb) { SsaImplCommon::phiHasInputFromBlock(this, result, bb) } - cached Definition getAPhiInput() { - result = this.getPhiInput(_) - } + cached + Definition getAPhiInput() { result = this.getPhiInput(_) } } } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index 6eb554f933c..3de5e0dfdf9 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -166,17 +166,11 @@ abstract class ReturnNode extends Node { abstract ReturnKind getKind(); } -private module ReturnNodes { +private module ReturnNodes { class ReturnReturnNode extends ReturnNode, ExprNode { - ReturnReturnNode() { - exists(ReturnStmt stmt | - stmt.getResult() = this.asExpr() - ) - } + ReturnReturnNode() { exists(ReturnStmt stmt | stmt.getResult() = this.asExpr()) } - override ReturnKind getKind() { - result instanceof NormalReturnKind - } + override ReturnKind getKind() { result instanceof NormalReturnKind } } } @@ -188,7 +182,7 @@ abstract class OutNode extends Node { abstract DataFlowCall getCall(ReturnKind kind); } -private module OutNodes { +private module OutNodes { class CallOutNode extends OutNode, DataFlowCall { override DataFlowCall getCall(ReturnKind kind) { result = this and kind instanceof NormalReturnKind diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.ql b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.ql index 3144222a652..be4f604a4f7 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.ql +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.ql @@ -20,6 +20,5 @@ class TestConfiguration extends DataFlow::Configuration { } from DataFlow::PathNode src, DataFlow::PathNode sink, TestConfiguration test -where - test.hasFlowPath(src, sink) +where test.hasFlowPath(src, sink) select src, sink From a5dc4f430c85d80e763997f3578ccc240dc6a83d Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 30 May 2022 13:52:32 +0200 Subject: [PATCH 114/125] Python: Expand test-filter tests With no virtual environment enabled, none of the third-party library test case are found. --- .../filters/tests/Filter.expected | 2 ++ .../filters/tests/unittest_test.py | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/python/ql/test/library-tests/filters/tests/Filter.expected b/python/ql/test/library-tests/filters/tests/Filter.expected index a4f643aac45..f1db1818179 100644 --- a/python/ql/test/library-tests/filters/tests/Filter.expected +++ b/python/ql/test/library-tests/filters/tests/Filter.expected @@ -4,3 +4,5 @@ | test_foo.py:3:1:3:15 | Function test_foo | | unittest_test.py:3:1:3:33 | Class FooTest | | unittest_test.py:4:5:4:25 | Function test_valid | +| unittest_test.py:7:1:7:49 | Class FunctionFooTest | +| unittest_test.py:8:5:8:25 | Function test_valid | diff --git a/python/ql/test/library-tests/filters/tests/unittest_test.py b/python/ql/test/library-tests/filters/tests/unittest_test.py index 6d28a61438a..d8425f0f1fa 100644 --- a/python/ql/test/library-tests/filters/tests/unittest_test.py +++ b/python/ql/test/library-tests/filters/tests/unittest_test.py @@ -3,3 +3,29 @@ import unittest class FooTest(unittest.TestCase): def test_valid(self): pass + +class FunctionFooTest(unittest.FunctionTestCase): + def test_valid(self): + pass + +class AsyncTest(unittest.IsolatedAsyncioTestCase): + async def test_valid(self): + pass + +# django -- see https://docs.djangoproject.com/en/4.0/topics/testing/overview/ +import django.test +class MyDjangoUnitTest(django.test.TestCase): + def test_valid(self): + pass + +# flask -- see https://pythonhosted.org/Flask-Testing/ +import flask_testing +class MyFlaskUnitTest(flask_testing.LiveServerTestCase): + def test_valid(self): + pass + +# tornado -- see https://www.tornadoweb.org/en/stable/testing.html#tornado.testing.AsyncHTTPTestCase +import tornado.testing +class MyTornadoUnitTest(tornado.testing.AsyncHTTPTestCase): + def test_valid(self): + pass From a8b4b6a374f2528400111f5e7529957d5c068fd1 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 30 May 2022 14:13:06 +0200 Subject: [PATCH 115/125] Python: Move test-modeling to API-graphs Notice that although we loose the contrived examples in `test.py`, we do gain support for real-world test-case construction, which seems worth the tradeoff. --- python/ql/lib/semmle/python/filters/Tests.qll | 13 +++++++------ .../library-tests/filters/tests/Filter.expected | 11 ++++++++--- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/python/ql/lib/semmle/python/filters/Tests.qll b/python/ql/lib/semmle/python/filters/Tests.qll index 53c4e1ba176..44ea2edebe4 100644 --- a/python/ql/lib/semmle/python/filters/Tests.qll +++ b/python/ql/lib/semmle/python/filters/Tests.qll @@ -1,14 +1,15 @@ import python +private import semmle.python.ApiGraphs abstract class TestScope extends Scope { } -// don't extend Class directly to avoid ambiguous method warnings -class UnitTestClass extends TestScope { +class UnitTestClass extends TestScope, Class { UnitTestClass() { - exists(ClassValue cls | this = cls.getScope() | - cls.getABaseType+() = Module::named("unittest").attr(_) - or - cls.getABaseType+().getName().toLowerCase() = "testcase" + exists(API::Node testCaseClass, string testCaseString | + testCaseString.matches("%TestCase") and + testCaseClass = any(API::Node mod).getMember(testCaseString) + | + this.getParent() = testCaseClass.getASubclass*().getAnImmediateUse().asExpr() ) } } diff --git a/python/ql/test/library-tests/filters/tests/Filter.expected b/python/ql/test/library-tests/filters/tests/Filter.expected index f1db1818179..9aeda5bd3f4 100644 --- a/python/ql/test/library-tests/filters/tests/Filter.expected +++ b/python/ql/test/library-tests/filters/tests/Filter.expected @@ -1,8 +1,13 @@ -| test.py:4:1:4:23 | Class MyTest | -| test.py:6:5:6:21 | Function test_1 | -| test.py:9:5:9:21 | Function test_2 | | test_foo.py:3:1:3:15 | Function test_foo | | unittest_test.py:3:1:3:33 | Class FooTest | | unittest_test.py:4:5:4:25 | Function test_valid | | unittest_test.py:7:1:7:49 | Class FunctionFooTest | | unittest_test.py:8:5:8:25 | Function test_valid | +| unittest_test.py:11:1:11:50 | Class AsyncTest | +| unittest_test.py:12:11:12:31 | Function test_valid | +| unittest_test.py:17:1:17:45 | Class MyDjangoUnitTest | +| unittest_test.py:18:5:18:25 | Function test_valid | +| unittest_test.py:23:1:23:56 | Class MyFlaskUnitTest | +| unittest_test.py:24:5:24:25 | Function test_valid | +| unittest_test.py:29:1:29:59 | Class MyTornadoUnitTest | +| unittest_test.py:30:5:30:25 | Function test_valid | From 08e64ea1b4cbfd2c6cc3b9023c8ef8ab4074374a Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 30 May 2022 14:45:34 +0200 Subject: [PATCH 116/125] Python: Remove contrived test-case example --- python/ql/test/library-tests/filters/tests/test.py | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 python/ql/test/library-tests/filters/tests/test.py diff --git a/python/ql/test/library-tests/filters/tests/test.py b/python/ql/test/library-tests/filters/tests/test.py deleted file mode 100644 index 261d2fc9f36..00000000000 --- a/python/ql/test/library-tests/filters/tests/test.py +++ /dev/null @@ -1,10 +0,0 @@ -class TestCase: - pass - -class MyTest(TestCase): - - def test_1(self): - pass - - def test_2(self): - pass From 420dea0792dc9f0436bf54b67dc9faaf6c143cca Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 30 May 2022 14:48:06 +0200 Subject: [PATCH 117/125] Python: Fix example TestCase --- python/ql/test/query-tests/Statements/asserts/assert.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/test/query-tests/Statements/asserts/assert.py b/python/ql/test/query-tests/Statements/asserts/assert.py index 036f9b20a4e..e4ca2bfe2bd 100644 --- a/python/ql/test/query-tests/Statements/asserts/assert.py +++ b/python/ql/test/query-tests/Statements/asserts/assert.py @@ -61,8 +61,8 @@ def ok_assert_false(x): if x: assert 0==1, "Ok" -class TestCase: - pass +from unittest import TestCase + class MyTest(TestCase): def test_ok_assert_in_test(self, x): From 21527f66e1e481fa8b05cac4989df730bf754921 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 30 May 2022 15:46:18 +0100 Subject: [PATCH 118/125] Swift: Extract KeyPath applications and KeyPathDot expressions. --- swift/codegen/schema.yml | 2 ++ swift/extractor/visitors/ExprVisitor.h | 16 ++++++++++++++++ .../generated/expr/KeyPathApplicationExpr.qll | 14 ++++++++++++++ swift/ql/lib/swift.dbscheme | 4 +++- 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/swift/codegen/schema.yml b/swift/codegen/schema.yml index ebdece63965..dbb220bfd43 100644 --- a/swift/codegen/schema.yml +++ b/swift/codegen/schema.yml @@ -390,6 +390,8 @@ InOutExpr: KeyPathApplicationExpr: _extends: Expr + base: Expr + key_path: Expr KeyPathDotExpr: _extends: Expr diff --git a/swift/extractor/visitors/ExprVisitor.h b/swift/extractor/visitors/ExprVisitor.h index d65e824becc..412802df877 100644 --- a/swift/extractor/visitors/ExprVisitor.h +++ b/swift/extractor/visitors/ExprVisitor.h @@ -509,6 +509,22 @@ class ExprVisitor : public AstVisitorBase { dispatcher_.emit(IfExprsTrap{label, condLabel, thenLabel, elseLabel}); } + void visitKeyPathDotExpr(swift::KeyPathDotExpr* expr) { + auto label = dispatcher_.assignNewLabel(expr); + dispatcher_.emit(KeyPathDotExprsTrap{label}); + } + + void visitKeyPathApplicationExpr(swift::KeyPathApplicationExpr* expr) { + auto label = dispatcher_.assignNewLabel(expr); + assert(expr->getBase() && "KeyPathApplicationExpr has getBase()"); + assert(expr->getKeyPath() && "KeyPathApplicationExpr has getKeyPath()"); + + auto baseLabel = dispatcher_.fetchLabel(expr->getBase()); + auto keyPathLabel = dispatcher_.fetchLabel(expr->getKeyPath()); + + dispatcher_.emit(KeyPathApplicationExprsTrap{label, baseLabel, keyPathLabel}); + } + private: TrapLabel emitArgument(const swift::Argument& arg) { auto argLabel = dispatcher_.createLabel(); diff --git a/swift/ql/lib/codeql/swift/generated/expr/KeyPathApplicationExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/KeyPathApplicationExpr.qll index e746c44943b..900145c26bd 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/KeyPathApplicationExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/KeyPathApplicationExpr.qll @@ -3,4 +3,18 @@ import codeql.swift.elements.expr.Expr class KeyPathApplicationExprBase extends @key_path_application_expr, Expr { override string getAPrimaryQlClass() { result = "KeyPathApplicationExpr" } + + Expr getBase() { + exists(Expr x | + key_path_application_exprs(this, x, _) and + result = x.resolve() + ) + } + + Expr getKeyPath() { + exists(Expr x | + key_path_application_exprs(this, _, x) and + result = x.resolve() + ) + } } diff --git a/swift/ql/lib/swift.dbscheme b/swift/ql/lib/swift.dbscheme index 47edbb550e8..5305c94a75e 100644 --- a/swift/ql/lib/swift.dbscheme +++ b/swift/ql/lib/swift.dbscheme @@ -888,7 +888,9 @@ in_out_exprs( ); key_path_application_exprs( - unique int id: @key_path_application_expr + unique int id: @key_path_application_expr, + int base: @expr ref, + int key_path: @expr ref ); key_path_dot_exprs( From 52f0b0d8d80e7e9b36faf2b0410c2397a83484b5 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 30 May 2022 15:46:54 +0100 Subject: [PATCH 119/125] Swift: Fix extraction of roots in 'KeyPathExpr'. --- swift/extractor/visitors/ExprVisitor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/extractor/visitors/ExprVisitor.h b/swift/extractor/visitors/ExprVisitor.h index 412802df877..6977a91d1a4 100644 --- a/swift/extractor/visitors/ExprVisitor.h +++ b/swift/extractor/visitors/ExprVisitor.h @@ -468,7 +468,7 @@ class ExprVisitor : public AstVisitorBase { auto pathLabel = dispatcher_.fetchLabel(path); dispatcher_.emit(KeyPathExprParsedPathsTrap{label, pathLabel}); } - if (auto root = expr->getParsedPath()) { + if (auto root = expr->getParsedRoot()) { auto rootLabel = dispatcher_.fetchLabel(root); dispatcher_.emit(KeyPathExprParsedRootsTrap{label, rootLabel}); } From 9175354bbd705039ad5eab706a128d9f8cfec098 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 30 May 2022 15:51:49 +0100 Subject: [PATCH 120/125] Swift: Add test and accept changes. --- .../extractor-tests/expressions/all.expected | 13 +++++++++++++ .../expressions/expressions.swift | 16 ++++++++++++++++ .../expressions/semantics.expected | 6 ++++++ 3 files changed, 35 insertions(+) diff --git a/swift/ql/test/extractor-tests/expressions/all.expected b/swift/ql/test/extractor-tests/expressions/all.expected index d8094e7f54f..5ec593d4a09 100644 --- a/swift/ql/test/extractor-tests/expressions/all.expected +++ b/swift/ql/test/extractor-tests/expressions/all.expected @@ -125,6 +125,7 @@ | expressions.swift:54:1:54:1 | _ | | expressions.swift:54:1:54:8 | ... = ... | | expressions.swift:54:5:54:8 | #keyPath(...) | +| expressions.swift:54:6:54:8 | TBD (UnresolvedDotExpr) | | expressions.swift:58:16:58:16 | 1234 | | expressions.swift:59:1:59:1 | unsafeFunction | | expressions.swift:59:1:59:34 | call to unsafeFunction | @@ -232,3 +233,15 @@ | expressions.swift:138:10:138:17 | ...[...] | | expressions.swift:138:13:138:13 | 3 | | expressions.swift:138:16:138:16 | 4 | +| expressions.swift:152:26:152:26 | a | +| expressions.swift:152:26:152:47 | \\...[...] | +| expressions.swift:152:37:152:37 | keyPathInt | +| expressions.swift:153:24:153:24 | a | +| expressions.swift:153:24:153:43 | \\...[...] | +| expressions.swift:153:35:153:35 | keyPathB | +| expressions.swift:154:22:154:22 | a | +| expressions.swift:154:22:154:41 | \\...[...] | +| expressions.swift:154:22:154:56 | \\...[...] | +| expressions.swift:154:33:154:33 | keyPathB | +| expressions.swift:154:52:154:55 | #keyPath(...) | +| expressions.swift:154:53:154:55 | TBD (UnresolvedDotExpr) | diff --git a/swift/ql/test/extractor-tests/expressions/expressions.swift b/swift/ql/test/extractor-tests/expressions/expressions.swift index f294a5a285c..bc720abe70f 100644 --- a/swift/ql/test/extractor-tests/expressions/expressions.swift +++ b/swift/ql/test/extractor-tests/expressions/expressions.swift @@ -136,4 +136,20 @@ func testProperties(hp : inout HasProperty) -> Int { var w = hp.normalField hp[1] = 2 return hp[3, 4] +} + +struct B { + var x : Int +} + +struct A { + var b : B + var bs : [B] + var mayB : B? +} + +func test(a : A, keyPathInt : WritableKeyPath, keyPathB : WritableKeyPath) { + var apply_keyPathInt = a[keyPath: keyPathInt] + var apply_keyPathB = a[keyPath: keyPathB] + var nested_apply = a[keyPath: keyPathB][keyPath: \B.x] } \ No newline at end of file diff --git a/swift/ql/test/extractor-tests/expressions/semantics.expected b/swift/ql/test/extractor-tests/expressions/semantics.expected index 5f07b18715f..ea49c8e7ead 100644 --- a/swift/ql/test/extractor-tests/expressions/semantics.expected +++ b/swift/ql/test/extractor-tests/expressions/semantics.expected @@ -68,3 +68,9 @@ | expressions.swift:137:3:137:7 | ...[...] | OrdinarySemantics | | expressions.swift:138:10:138:10 | hp | OrdinarySemantics | | expressions.swift:138:10:138:17 | ...[...] | OrdinarySemantics | +| expressions.swift:152:26:152:26 | a | OrdinarySemantics | +| expressions.swift:152:37:152:37 | keyPathInt | OrdinarySemantics | +| expressions.swift:153:24:153:24 | a | OrdinarySemantics | +| expressions.swift:153:35:153:35 | keyPathB | OrdinarySemantics | +| expressions.swift:154:22:154:22 | a | OrdinarySemantics | +| expressions.swift:154:33:154:33 | keyPathB | OrdinarySemantics | From c5dd8aa703ada9a14d5a5c31f869f2603b9b0d46 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Tue, 24 May 2022 16:52:24 -0700 Subject: [PATCH 121/125] Convert .codeqlmanifest.json to codeql-workspace.yml The semantics are the same, except one is json, the other is yaml. --- .codeqlmanifest.json | 30 ------------------------------ codeql-workspace.yml | 27 +++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 30 deletions(-) delete mode 100644 .codeqlmanifest.json create mode 100644 codeql-workspace.yml diff --git a/.codeqlmanifest.json b/.codeqlmanifest.json deleted file mode 100644 index 2c39a11f9ae..00000000000 --- a/.codeqlmanifest.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "provide": [ - "*/ql/src/qlpack.yml", - "*/ql/lib/qlpack.yml", - "*/ql/test/qlpack.yml", - "*/ql/examples/qlpack.yml", - "*/ql/consistency-queries/qlpack.yml", - "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml", - "go/ql/config/legacy-support/qlpack.yml", - "go/build/codeql-extractor-go/codeql-extractor.yml", - "javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml", - "javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml", - "javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml", - "csharp/ql/campaigns/Solorigate/lib/qlpack.yml", - "csharp/ql/campaigns/Solorigate/src/qlpack.yml", - "csharp/ql/campaigns/Solorigate/test/qlpack.yml", - "misc/legacy-support/*/qlpack.yml", - "misc/suite-helpers/qlpack.yml", - "ruby/extractor-pack/codeql-extractor.yml", - "swift/extractor-pack/codeql-extractor.yml", - "ql/extractor-pack/codeql-extractor.yml" - ], - "versionPolicies": { - "default": { - "requireChangeNotes": true, - "committedPrereleaseSuffix": "dev", - "committedVersion": "nextPatchRelease" - } - } -} diff --git a/codeql-workspace.yml b/codeql-workspace.yml new file mode 100644 index 00000000000..5ca1481036d --- /dev/null +++ b/codeql-workspace.yml @@ -0,0 +1,27 @@ +provide: + - "*/ql/src/qlpack.yml" + - "*/ql/lib/qlpack.yml" + - "*/ql/test/qlpack.yml" + - "*/ql/examples/qlpack.yml" + - "*/ql/consistency-queries/qlpack.yml" + - "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml" + - "go/ql/config/legacy-support/qlpack.yml" + - "go/build/codeql-extractor-go/codeql-extractor.yml" + - "javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml" + - "javascript/ql/experimental/adaptivethreatmodeling/model/qlpack.yml" + - "javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml" + - "javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml" + - "csharp/ql/campaigns/Solorigate/lib/qlpack.yml" + - "csharp/ql/campaigns/Solorigate/src/qlpack.yml" + - "csharp/ql/campaigns/Solorigate/test/qlpack.yml" + - "misc/legacy-support/*/qlpack.yml" + - "misc/suite-helpers/qlpack.yml" + - "ruby/extractor-pack/codeql-extractor.yml" + - "swift/extractor-pack/codeql-extractor.yml" + - "ql/extractor-pack/codeql-extractor.ym" + +versionPolicies: + default: + requireChangeNotes: true + committedPrereleaseSuffix: dev + committedVersion: nextPatchRelease From 18c5474431e3f1d1cc3bb6144917af89e7bc28d0 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Thu, 26 May 2022 10:06:24 -0700 Subject: [PATCH 122/125] Add comment to codeql-workspace.yml --- codeql-workspace.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/codeql-workspace.yml b/codeql-workspace.yml index 5ca1481036d..c892d5e50c6 100644 --- a/codeql-workspace.yml +++ b/codeql-workspace.yml @@ -8,7 +8,9 @@ provide: - "go/ql/config/legacy-support/qlpack.yml" - "go/build/codeql-extractor-go/codeql-extractor.yml" - "javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml" - - "javascript/ql/experimental/adaptivethreatmodeling/model/qlpack.yml" + # This pack is explicitly excluded from the workspace since we + # require consumers to use a version of this pack from the package cache. + # - "javascript/ql/experimental/adaptivethreatmodeling/model/qlpack.yml" - "javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml" - "javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml" - "csharp/ql/campaigns/Solorigate/lib/qlpack.yml" From e544a9b94ba62d987081f8f53854eae54b6e13cc Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Thu, 26 May 2022 10:21:55 -0700 Subject: [PATCH 123/125] Update codeql-workspace.yml Co-authored-by: Henry Mercer --- codeql-workspace.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/codeql-workspace.yml b/codeql-workspace.yml index c892d5e50c6..1bf0510af50 100644 --- a/codeql-workspace.yml +++ b/codeql-workspace.yml @@ -8,8 +8,11 @@ provide: - "go/ql/config/legacy-support/qlpack.yml" - "go/build/codeql-extractor-go/codeql-extractor.yml" - "javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml" - # This pack is explicitly excluded from the workspace since we - # require consumers to use a version of this pack from the package cache. + # This pack is explicitly excluded from the workspace since most users + # will want to use a version of this pack from the package cache. Internal + # users can uncomment the following line and place a custom ML model + # in the corresponding pack to test a custom ML model within their local + # checkout. # - "javascript/ql/experimental/adaptivethreatmodeling/model/qlpack.yml" - "javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml" - "javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml" From ca764576beb1fec8e2b22f1033a498173f204e0a Mon Sep 17 00:00:00 2001 From: Henry Mercer Date: Fri, 27 May 2022 15:27:45 +0100 Subject: [PATCH 124/125] Swift: Update mention of manifest file in docs --- swift/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/README.md b/swift/README.md index 5ec70fb4fbb..67ca199842c 100644 --- a/swift/README.md +++ b/swift/README.md @@ -13,7 +13,7 @@ bazel run //swift:create-extractor-pack which will install `swift/extractor-pack`. Using `--search-path=swift/extractor-pack` will then pick up the Swift extractor. You can also use -`--search-path=.`, as the extractor pack is mentioned in the root `.codeqlmanifest.json`. +`--search-path=.`, as the extractor pack is mentioned in the root `codeql-workspace.yml`. Notice you can run `bazel run :create-extractor-pack` if you already are in the `swift` directory. From 99e6d2a925b2e6cf7de92b4ebe42aba2ddd5bad1 Mon Sep 17 00:00:00 2001 From: Henry Mercer Date: Fri, 27 May 2022 17:46:54 +0100 Subject: [PATCH 125/125] Run relevant tests when `codeql-workspace.yml` is updated --- .github/workflows/go-tests.yml | 1 + .github/workflows/js-ml-tests.yml | 2 ++ .github/workflows/ql-for-ql-tests.yml | 2 ++ .github/workflows/ruby-build.yml | 2 ++ .github/workflows/ruby-qltest.yml | 2 ++ .github/workflows/swift-qltest.yml | 1 + 6 files changed, 10 insertions(+) diff --git a/.github/workflows/go-tests.yml b/.github/workflows/go-tests.yml index 12e162adf29..ca126d1a3ee 100644 --- a/.github/workflows/go-tests.yml +++ b/.github/workflows/go-tests.yml @@ -4,6 +4,7 @@ on: paths: - "go/**" - .github/workflows/go-tests.yml + - codeql-workspace.yml jobs: test-linux: diff --git a/.github/workflows/js-ml-tests.yml b/.github/workflows/js-ml-tests.yml index ba33779455e..65db215d8c3 100644 --- a/.github/workflows/js-ml-tests.yml +++ b/.github/workflows/js-ml-tests.yml @@ -5,6 +5,7 @@ on: paths: - "javascript/ql/experimental/adaptivethreatmodeling/**" - .github/workflows/js-ml-tests.yml + - codeql-workspace.yml branches: - main - "rc/*" @@ -12,6 +13,7 @@ on: paths: - "javascript/ql/experimental/adaptivethreatmodeling/**" - .github/workflows/js-ml-tests.yml + - codeql-workspace.yml workflow_dispatch: defaults: diff --git a/.github/workflows/ql-for-ql-tests.yml b/.github/workflows/ql-for-ql-tests.yml index a6c79237759..3b0a4963b79 100644 --- a/.github/workflows/ql-for-ql-tests.yml +++ b/.github/workflows/ql-for-ql-tests.yml @@ -5,10 +5,12 @@ on: branches: [main] paths: - "ql/**" + - codeql-workspace.yml pull_request: branches: [main] paths: - "ql/**" + - codeql-workspace.yml env: CARGO_TERM_COLOR: always diff --git a/.github/workflows/ruby-build.yml b/.github/workflows/ruby-build.yml index 9e95839789f..c402312db0e 100644 --- a/.github/workflows/ruby-build.yml +++ b/.github/workflows/ruby-build.yml @@ -5,6 +5,7 @@ on: paths: - "ruby/**" - .github/workflows/ruby-build.yml + - codeql-workspace.yml branches: - main - "rc/*" @@ -12,6 +13,7 @@ on: paths: - "ruby/**" - .github/workflows/ruby-build.yml + - codeql-workspace.yml branches: - main - "rc/*" diff --git a/.github/workflows/ruby-qltest.yml b/.github/workflows/ruby-qltest.yml index 463c501c765..0cf8860d8f1 100644 --- a/.github/workflows/ruby-qltest.yml +++ b/.github/workflows/ruby-qltest.yml @@ -5,6 +5,7 @@ on: paths: - "ruby/**" - .github/workflows/ruby-qltest.yml + - codeql-workspace.yml branches: - main - "rc/*" @@ -12,6 +13,7 @@ on: paths: - "ruby/**" - .github/workflows/ruby-qltest.yml + - codeql-workspace.yml branches: - main - "rc/*" diff --git a/.github/workflows/swift-qltest.yml b/.github/workflows/swift-qltest.yml index e0cc5a1cd02..915e1f331a5 100644 --- a/.github/workflows/swift-qltest.yml +++ b/.github/workflows/swift-qltest.yml @@ -5,6 +5,7 @@ on: paths: - "swift/**" - .github/workflows/swift-qltest.yml + - codeql-workspace.yml branches: - main defaults: