From 460eddd7812e2b50592d0679c549ad5f207921d9 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Tue, 16 Aug 2022 08:55:49 +0200 Subject: [PATCH 0001/1420] add ql/override-any --- .../performance/VarUnusedInDisjunctQuery.qll | 27 +++++++++++++ .../performance/VarUnusedInDisjunct.ql | 27 +------------ ql/ql/src/queries/style/OverrideAny.ql | 40 +++++++++++++++++++ 3 files changed, 68 insertions(+), 26 deletions(-) create mode 100644 ql/ql/src/codeql_ql/performance/VarUnusedInDisjunctQuery.qll create mode 100644 ql/ql/src/queries/style/OverrideAny.ql diff --git a/ql/ql/src/codeql_ql/performance/VarUnusedInDisjunctQuery.qll b/ql/ql/src/codeql_ql/performance/VarUnusedInDisjunctQuery.qll new file mode 100644 index 00000000000..503ad6db3ad --- /dev/null +++ b/ql/ql/src/codeql_ql/performance/VarUnusedInDisjunctQuery.qll @@ -0,0 +1,27 @@ +import ql + +/** + * Holds if we assume `t` is a small type, and + * variables of this type are therefore not an issue in cartesian products. + */ +predicate isSmallType(Type t) { + t.getName() = "string" // DataFlow::Configuration and the like + or + exists(NewType newType | newType = t.getDeclaration() | + forex(NewTypeBranch branch | branch = newType.getABranch() | branch.getArity() = 0) + ) + or + t.getName() = "boolean" + or + exists(NewType newType | newType = t.getDeclaration() | + forex(NewTypeBranch branch | branch = newType.getABranch() | + isSmallType(branch.getReturnType()) + ) + ) + or + exists(NewTypeBranch branch | t = branch.getReturnType() | + forall(Type param | param = branch.getParameterType(_) | isSmallType(param)) + ) + or + isSmallType(t.getASuperType()) +} diff --git a/ql/ql/src/queries/performance/VarUnusedInDisjunct.ql b/ql/ql/src/queries/performance/VarUnusedInDisjunct.ql index c26b47554fe..2d85b872153 100644 --- a/ql/ql/src/queries/performance/VarUnusedInDisjunct.ql +++ b/ql/ql/src/queries/performance/VarUnusedInDisjunct.ql @@ -10,6 +10,7 @@ */ import ql +import codeql_ql.performance.VarUnusedInDisjunctQuery /** * Holds if `node` bind `var` in a (transitive) child node. @@ -48,32 +49,6 @@ predicate alwaysBindsVar(VarDef var, AstNode node) { exists(IfFormula ifForm | ifForm = node | alwaysBindsVar(var, ifForm.getCondition())) } -/** - * Holds if we assume `t` is a small type, and - * variables of this type are therefore not an issue in cartesian products. - */ -predicate isSmallType(Type t) { - t.getName() = "string" // DataFlow::Configuration and the like - or - exists(NewType newType | newType = t.getDeclaration() | - forex(NewTypeBranch branch | branch = newType.getABranch() | branch.getArity() = 0) - ) - or - t.getName() = "boolean" - or - exists(NewType newType | newType = t.getDeclaration() | - forex(NewTypeBranch branch | branch = newType.getABranch() | - isSmallType(branch.getReturnType()) - ) - ) - or - exists(NewTypeBranch branch | t = branch.getReturnType() | - forall(Type param | param = branch.getParameterType(_) | isSmallType(param)) - ) - or - isSmallType(t.getASuperType()) -} - /** * Holds if `pred` is inlined. */ diff --git a/ql/ql/src/queries/style/OverrideAny.ql b/ql/ql/src/queries/style/OverrideAny.ql new file mode 100644 index 00000000000..0c7d82fd3c8 --- /dev/null +++ b/ql/ql/src/queries/style/OverrideAny.ql @@ -0,0 +1,40 @@ +/** + * @name Override with unmentioned parameter + * @description A predicate that overrides the default behavior but doesn't mention a parameter is suspicious. + * @kind problem + * @problem.severity warning + * @id ql/override-any + * @precision very-high + */ + +import ql +import codeql_ql.performance.VarUnusedInDisjunctQuery + +AstNode param(Predicate pred, string name, Type t) { + result = pred.getParameter(_) and + result.(VarDecl).getName() = name and + result.(VarDecl).getType() = t + or + result = pred.getReturnTypeExpr() and + name = "result" and + t = pred.getReturnType() +} + +predicate hasAccess(Predicate pred, string name) { + exists(param(pred, name, _).(VarDecl).getAnAccess()) + or + name = "result" and + exists(param(pred, name, _)) and + exists(ResultAccess res | res.getEnclosingPredicate() = pred) +} + +from Predicate pred, AstNode param, string name, Type paramType +where + pred.hasAnnotation("override") and + param = param(pred, name, paramType) and + not hasAccess(pred, name) and + not pred.getBody() instanceof NoneCall and + exists(pred.getBody()) and + not isSmallType(pred.getParent().(Class).getType()) and + not isSmallType(paramType) +select pred, "Override predicate doesn't mention $@.", param, name From 46627a737e281b0432b1297abaa402fafaca51aa Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Fri, 14 Oct 2022 16:00:17 +0200 Subject: [PATCH 0002/1420] add an AdditionalTaintStep class for Ruby --- .../ql/lib/codeql/ruby/dataflow/FlowSteps.qll | 28 +++++++++++++++++++ .../internal/TaintTrackingPrivate.qll | 4 +++ 2 files changed, 32 insertions(+) create mode 100644 ruby/ql/lib/codeql/ruby/dataflow/FlowSteps.qll diff --git a/ruby/ql/lib/codeql/ruby/dataflow/FlowSteps.qll b/ruby/ql/lib/codeql/ruby/dataflow/FlowSteps.qll new file mode 100644 index 00000000000..414d241c9fa --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/FlowSteps.qll @@ -0,0 +1,28 @@ +/** + * Provides classes representing various flow steps for taint tracking. + */ + +private import codeql.ruby.DataFlow +private import internal.DataFlowPrivate as DFPrivate + +private class Unit = DFPrivate::Unit; + +/** + * A module importing the frameworks that implement additional flow steps, + * ensuring that they are visible to the taint tracking library. + */ +private module Frameworks { } + +/** + * A unit class for adding additional taint steps. + * + * Extend this class to add additional taint steps that should apply to all + * taint configurations. + */ +class AdditionalTaintStep extends Unit { + /** + * Holds if the step from `node1` to `node2` should be considered a taint + * step for all configurations. + */ + abstract predicate step(DataFlow::Node node1, DataFlow::Node node2); +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll index 92eef6e3cfb..bf9fd46ffa0 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll @@ -62,6 +62,8 @@ private CfgNodes::ExprNodes::VariableWriteAccessCfgNode variablesInPattern( cached private module Cached { + private import codeql.ruby.dataflow.FlowSteps as FlowSteps + cached predicate forceCachingInSameStage() { any() } @@ -99,6 +101,8 @@ private module Cached { or FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, false) or + any(FlowSteps::AdditionalTaintStep s).step(nodeFrom, nodeTo) + or // Although flow through collections is modeled precisely using stores/reads, we still // allow flow out of a _tainted_ collection. This is needed in order to support taint- // tracking configurations where the source is a collection. From dbf2673a916b66839ed4711d0be0dce9b69ebe4c Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Fri, 14 Oct 2022 16:06:10 +0200 Subject: [PATCH 0003/1420] add returnsFormatted predicate to PrintfStyleCall (similar to JS) --- .../codeql/ruby/security/TaintedFormatStringSpecific.qll | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ruby/ql/lib/codeql/ruby/security/TaintedFormatStringSpecific.qll b/ruby/ql/lib/codeql/ruby/security/TaintedFormatStringSpecific.qll index 2f5f6069594..e2c4549133a 100644 --- a/ruby/ql/lib/codeql/ruby/security/TaintedFormatStringSpecific.qll +++ b/ruby/ql/lib/codeql/ruby/security/TaintedFormatStringSpecific.qll @@ -25,6 +25,9 @@ abstract class PrintfStyleCall extends DataFlow::CallNode { * Gets then `n`th formatted argument of this call. */ DataFlow::Node getFormatArgument(int n) { n >= 0 and result = this.getArgument(n + 1) } + + /** Holds if this call returns the formatted string. */ + predicate returnsFormatted() { any() } } /** @@ -50,6 +53,8 @@ class KernelPrintfCall extends PrintfStyleCall { then result = this.getArgument(0) else result = this.getArgument([0, 1]) } + + override predicate returnsFormatted() { none() } } /** @@ -62,6 +67,8 @@ class KernelSprintfCall extends PrintfStyleCall { this.asExpr().getExpr() instanceof UnknownMethodCall and this.getMethodName() = "sprintf" } + + override predicate returnsFormatted() { any() } } /** @@ -71,4 +78,6 @@ class IOPrintfCall extends PrintfStyleCall { IOPrintfCall() { this.getReceiver() instanceof IO::IOInstance and this.getMethodName() = "printf" } + + override predicate returnsFormatted() { none() } } From a2b924bbdf90926dc47e44b4acb081082bc92155 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Mon, 17 Oct 2022 12:28:29 +0200 Subject: [PATCH 0004/1420] move model of printf style calls to StringFormatters.qll --- ruby/ql/lib/codeql/ruby/Frameworks.qll | 1 + .../ruby/frameworks/StringFormatters.qll | 70 +++++++++++++++++ .../security/TaintedFormatStringSpecific.qll | 78 +------------------ 3 files changed, 73 insertions(+), 76 deletions(-) create mode 100644 ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll diff --git a/ruby/ql/lib/codeql/ruby/Frameworks.qll b/ruby/ql/lib/codeql/ruby/Frameworks.qll index f89040b1d5d..9896cf23c39 100644 --- a/ruby/ql/lib/codeql/ruby/Frameworks.qll +++ b/ruby/ql/lib/codeql/ruby/Frameworks.qll @@ -23,3 +23,4 @@ private import codeql.ruby.frameworks.HttpClients private import codeql.ruby.frameworks.XmlParsing private import codeql.ruby.frameworks.ActionDispatch private import codeql.ruby.frameworks.PosixSpawn +private import codeql.ruby.frameworks.StringFormatters diff --git a/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll b/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll new file mode 100644 index 00000000000..501411c2558 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll @@ -0,0 +1,70 @@ +/** + * Provides classes for modeling string formatting libraries. + */ + +private import codeql.ruby.ast.Call +private import codeql.ruby.DataFlow +private import codeql.ruby.ApiGraphs +private import codeql.ruby.frameworks.core.IO + +/** + * A call to `printf` or `sprintf`. + */ +abstract class PrintfStyleCall extends DataFlow::CallNode { + // We assume that most printf-like calls have the signature f(format_string, args...) + /** + * Gets the format string of this call. + */ + DataFlow::Node getFormatString() { result = this.getArgument(0) } + + /** + * Gets then `n`th formatted argument of this call. + */ + DataFlow::Node getFormatArgument(int n) { n >= 0 and result = this.getArgument(n + 1) } +} + +/** + * A call to `Kernel.printf`. + */ +class KernelPrintfCall extends PrintfStyleCall { + KernelPrintfCall() { + this = API::getTopLevelMember("Kernel").getAMethodCall("printf") + or + this.asExpr().getExpr() instanceof UnknownMethodCall and + this.getMethodName() = "printf" + } + + // Kernel#printf supports two signatures: + // printf(io, string, ...) + // printf(string, ...) + override DataFlow::Node getFormatString() { + // Because `printf` has two different signatures, we can't be sure which + // argument is the format string, so we use a heuristic: + // If the first argument has a string value, then we assume it is the format string. + // Otherwise we treat both the first and second args as the format string. + if this.getArgument(0).getExprNode().getConstantValue().isString(_) + then result = this.getArgument(0) + else result = this.getArgument([0, 1]) + } +} + +/** + * A call to `Kernel.sprintf`. + */ +class KernelSprintfCall extends PrintfStyleCall { + KernelSprintfCall() { + this = API::getTopLevelMember("Kernel").getAMethodCall("sprintf") + or + this.asExpr().getExpr() instanceof UnknownMethodCall and + this.getMethodName() = "sprintf" + } +} + +/** + * A call to `IO#printf`. + */ +class IOPrintfCall extends PrintfStyleCall { + IOPrintfCall() { + this.getReceiver() instanceof IO::IOInstance and this.getMethodName() = "printf" + } +} diff --git a/ruby/ql/lib/codeql/ruby/security/TaintedFormatStringSpecific.qll b/ruby/ql/lib/codeql/ruby/security/TaintedFormatStringSpecific.qll index e2c4549133a..43f9dff4a25 100644 --- a/ruby/ql/lib/codeql/ruby/security/TaintedFormatStringSpecific.qll +++ b/ruby/ql/lib/codeql/ruby/security/TaintedFormatStringSpecific.qll @@ -2,82 +2,8 @@ * Provides Ruby-specific imports and classes needed for `TaintedFormatStringQuery` and `TaintedFormatStringCustomizations`. */ -import codeql.ruby.AST +import codeql.ruby.frameworks.StringFormatters import codeql.ruby.DataFlow import codeql.ruby.dataflow.RemoteFlowSources -import codeql.ruby.ApiGraphs import codeql.ruby.TaintTracking -private import codeql.ruby.frameworks.Files -private import codeql.ruby.frameworks.core.IO -private import codeql.ruby.controlflow.CfgNodes - -/** - * A call to `printf` or `sprintf`. - */ -abstract class PrintfStyleCall extends DataFlow::CallNode { - // We assume that most printf-like calls have the signature f(format_string, args...) - /** - * Gets the format string of this call. - */ - DataFlow::Node getFormatString() { result = this.getArgument(0) } - - /** - * Gets then `n`th formatted argument of this call. - */ - DataFlow::Node getFormatArgument(int n) { n >= 0 and result = this.getArgument(n + 1) } - - /** Holds if this call returns the formatted string. */ - predicate returnsFormatted() { any() } -} - -/** - * A call to `Kernel.printf`. - */ -class KernelPrintfCall extends PrintfStyleCall { - KernelPrintfCall() { - this = API::getTopLevelMember("Kernel").getAMethodCall("printf") - or - this.asExpr().getExpr() instanceof UnknownMethodCall and - this.getMethodName() = "printf" - } - - // Kernel#printf supports two signatures: - // printf(io, string, ...) - // printf(string, ...) - override DataFlow::Node getFormatString() { - // Because `printf` has two different signatures, we can't be sure which - // argument is the format string, so we use a heuristic: - // If the first argument has a string value, then we assume it is the format string. - // Otherwise we treat both the first and second args as the format string. - if this.getArgument(0).getExprNode().getConstantValue().isString(_) - then result = this.getArgument(0) - else result = this.getArgument([0, 1]) - } - - override predicate returnsFormatted() { none() } -} - -/** - * A call to `Kernel.sprintf`. - */ -class KernelSprintfCall extends PrintfStyleCall { - KernelSprintfCall() { - this = API::getTopLevelMember("Kernel").getAMethodCall("sprintf") - or - this.asExpr().getExpr() instanceof UnknownMethodCall and - this.getMethodName() = "sprintf" - } - - override predicate returnsFormatted() { any() } -} - -/** - * A call to `IO#printf`. - */ -class IOPrintfCall extends PrintfStyleCall { - IOPrintfCall() { - this.getReceiver() instanceof IO::IOInstance and this.getMethodName() = "printf" - } - - override predicate returnsFormatted() { none() } -} +import codeql.ruby.DataFlow From 6de1abcb0ef47ea2b2ef29bfc748275e8f03bfea Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Mon, 17 Oct 2022 12:29:07 +0200 Subject: [PATCH 0005/1420] add a returnsFormatted predicate to the printf model, similar to the JS implementation --- ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll b/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll index 501411c2558..1f58329629f 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll @@ -21,6 +21,9 @@ abstract class PrintfStyleCall extends DataFlow::CallNode { * Gets then `n`th formatted argument of this call. */ DataFlow::Node getFormatArgument(int n) { n >= 0 and result = this.getArgument(n + 1) } + + /** Holds if this call returns the formatted string. */ + predicate returnsFormatted() { any() } } /** @@ -46,6 +49,8 @@ class KernelPrintfCall extends PrintfStyleCall { then result = this.getArgument(0) else result = this.getArgument([0, 1]) } + + override predicate returnsFormatted() { none() } } /** @@ -58,6 +63,8 @@ class KernelSprintfCall extends PrintfStyleCall { this.asExpr().getExpr() instanceof UnknownMethodCall and this.getMethodName() = "sprintf" } + + override predicate returnsFormatted() { any() } } /** @@ -67,4 +74,6 @@ class IOPrintfCall extends PrintfStyleCall { IOPrintfCall() { this.getReceiver() instanceof IO::IOInstance and this.getMethodName() = "printf" } + + override predicate returnsFormatted() { none() } } From f222cc1f3ec1e6bd3de4977bcaa47ef9b9f88ac9 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Mon, 17 Oct 2022 12:35:46 +0200 Subject: [PATCH 0006/1420] refactor the existing taint-step for string interpolation into StringFormatters.qll --- ruby/ql/lib/codeql/ruby/dataflow/FlowSteps.qll | 4 +++- .../dataflow/internal/TaintTrackingPrivate.qll | 4 ---- .../codeql/ruby/frameworks/StringFormatters.qll | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/FlowSteps.qll b/ruby/ql/lib/codeql/ruby/dataflow/FlowSteps.qll index 414d241c9fa..881441fc765 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/FlowSteps.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/FlowSteps.qll @@ -11,7 +11,9 @@ private class Unit = DFPrivate::Unit; * A module importing the frameworks that implement additional flow steps, * ensuring that they are visible to the taint tracking library. */ -private module Frameworks { } +private module Frameworks { + import codeql.ruby.frameworks.StringFormatters +} /** * A unit class for adding additional taint steps. diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll index bf9fd46ffa0..92484e12596 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll @@ -95,10 +95,6 @@ private module Cached { ) ) or - // string interpolation of `nodeFrom` into `nodeTo` - nodeFrom.asExpr() = - nodeTo.asExpr().(CfgNodes::ExprNodes::StringlikeLiteralCfgNode).getAComponent() - or FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, false) or any(FlowSteps::AdditionalTaintStep s).step(nodeFrom, nodeTo) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll b/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll index 1f58329629f..fa284d8f18c 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll @@ -77,3 +77,19 @@ class IOPrintfCall extends PrintfStyleCall { override predicate returnsFormatted() { none() } } + +private import codeql.ruby.dataflow.FlowSteps +private import codeql.ruby.CFG + +/** + * A step for string interpolation of `pred` into `succ`. + * E.g. + * ```rb + * succ = "foo #{pred} bar" + * ``` + */ +private class StringLiteralFormatStep extends AdditionalTaintStep { + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + pred.asExpr() = succ.asExpr().(CfgNodes::ExprNodes::StringlikeLiteralCfgNode).getAComponent() + } +} From d4919d04ba934f5cb95da3656c29a44694ee8e4f Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Mon, 17 Oct 2022 12:43:28 +0200 Subject: [PATCH 0007/1420] add a taint-step for format-calls --- .../codeql/ruby/frameworks/StringFormatters.qll | 16 ++++++++++++++++ .../security/cwe-079/StoredXSS.expected | 4 ++++ .../cwe-079/app/views/foo/stores/show.html.erb | 5 +++++ 3 files changed, 25 insertions(+) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll b/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll index fa284d8f18c..b7c3fb0d21c 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll @@ -93,3 +93,19 @@ private class StringLiteralFormatStep extends AdditionalTaintStep { pred.asExpr() = succ.asExpr().(CfgNodes::ExprNodes::StringlikeLiteralCfgNode).getAComponent() } } + +/** + * A taint propagating data flow edge arising from string formatting. + */ +private class StringFormattingTaintStep extends AdditionalTaintStep { + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + exists(PrintfStyleCall call | + call.returnsFormatted() and + succ = call + | + pred = call.getFormatString() + or + pred = call.getFormatArgument(_) + ) + } +} diff --git a/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected b/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected index a637a8e4d0d..98156ed74c5 100644 --- a/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected +++ b/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected @@ -11,6 +11,7 @@ edges | app/views/foo/stores/show.html.erb:41:64:41:87 | ... + ... : | app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | | app/views/foo/stores/show.html.erb:41:64:41:87 | ... + ... : | app/views/foo/bars/_widget.html.erb:8:9:8:36 | ...[...] | | app/views/foo/stores/show.html.erb:41:76:41:87 | call to display_text : | app/views/foo/stores/show.html.erb:41:64:41:87 | ... + ... : | +| app/views/foo/stores/show.html.erb:87:17:87:28 | call to handle : | app/views/foo/stores/show.html.erb:87:3:87:29 | call to sprintf | nodes | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read : | semmle.label | call to read : | | app/controllers/foo/stores_controller.rb:9:22:9:23 | dt : | semmle.label | dt : | @@ -31,6 +32,8 @@ nodes | app/views/foo/stores/show.html.erb:70:3:70:20 | call to raw_name | semmle.label | call to raw_name | | app/views/foo/stores/show.html.erb:80:5:80:22 | call to display_name | semmle.label | call to display_name | | app/views/foo/stores/show.html.erb:83:5:83:24 | @other_user_raw_name | semmle.label | @other_user_raw_name | +| app/views/foo/stores/show.html.erb:87:3:87:29 | call to sprintf | semmle.label | call to sprintf | +| app/views/foo/stores/show.html.erb:87:17:87:28 | call to handle : | semmle.label | call to handle : | subpaths #select | app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read : | app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | Stored cross-site scripting vulnerability due to $@. | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | stored value | @@ -46,3 +49,4 @@ subpaths | app/views/foo/stores/show.html.erb:70:3:70:20 | call to raw_name | app/views/foo/stores/show.html.erb:70:3:70:20 | call to raw_name | app/views/foo/stores/show.html.erb:70:3:70:20 | call to raw_name | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:70:3:70:20 | call to raw_name | stored value | | app/views/foo/stores/show.html.erb:80:5:80:22 | call to display_name | app/views/foo/stores/show.html.erb:80:5:80:22 | call to display_name | app/views/foo/stores/show.html.erb:80:5:80:22 | call to display_name | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:80:5:80:22 | call to display_name | stored value | | app/views/foo/stores/show.html.erb:83:5:83:24 | @other_user_raw_name | app/controllers/foo/stores_controller.rb:12:28:12:48 | call to raw_name : | app/views/foo/stores/show.html.erb:83:5:83:24 | @other_user_raw_name | Stored cross-site scripting vulnerability due to $@. | app/controllers/foo/stores_controller.rb:12:28:12:48 | call to raw_name | stored value | +| app/views/foo/stores/show.html.erb:87:3:87:29 | call to sprintf | app/views/foo/stores/show.html.erb:87:17:87:28 | call to handle : | app/views/foo/stores/show.html.erb:87:3:87:29 | call to sprintf | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:87:17:87:28 | call to handle | stored value | diff --git a/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb index 90b70199767..837e5bccc3d 100644 --- a/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb +++ b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb @@ -81,3 +81,8 @@ <%# BAD: Indirect to a database value without escaping %> <%= @other_user_raw_name.html_safe %> + +<%# BAD: Kernel.sprintf is a taint-step %> +<%= + sprintf("%s", @user.handle).html_safe +%> From f09e3bd3ac593ccd21654d2271374b3728f3ab77 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Mon, 17 Oct 2022 13:51:36 +0200 Subject: [PATCH 0008/1420] add String#% as a printf like call --- .../ruby/frameworks/StringFormatters.qll | 28 +++++++++++++++++-- .../cwe-134/TaintedFormatString.expected | 12 ++++++++ .../security/cwe-134/tainted_format_string.rb | 6 ++++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll b/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll index b7c3fb0d21c..b11fba8301f 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll @@ -2,7 +2,7 @@ * Provides classes for modeling string formatting libraries. */ -private import codeql.ruby.ast.Call +private import codeql.ruby.AST as Ast private import codeql.ruby.DataFlow private import codeql.ruby.ApiGraphs private import codeql.ruby.frameworks.core.IO @@ -33,7 +33,7 @@ class KernelPrintfCall extends PrintfStyleCall { KernelPrintfCall() { this = API::getTopLevelMember("Kernel").getAMethodCall("printf") or - this.asExpr().getExpr() instanceof UnknownMethodCall and + this.asExpr().getExpr() instanceof Ast::UnknownMethodCall and this.getMethodName() = "printf" } @@ -60,7 +60,7 @@ class KernelSprintfCall extends PrintfStyleCall { KernelSprintfCall() { this = API::getTopLevelMember("Kernel").getAMethodCall("sprintf") or - this.asExpr().getExpr() instanceof UnknownMethodCall and + this.asExpr().getExpr() instanceof Ast::UnknownMethodCall and this.getMethodName() = "sprintf" } @@ -78,6 +78,28 @@ class IOPrintfCall extends PrintfStyleCall { override predicate returnsFormatted() { none() } } +/** + * A call to `String#%`. + */ +class StringPercentCall extends PrintfStyleCall { + StringPercentCall() { this.getMethodName() = "%" } + + override DataFlow::Node getFormatString() { result = this.getReceiver() } + + override DataFlow::Node getFormatArgument(int n) { + exists(DataFlow::CallNode arrCall | + arrCall = this.getArgument(0) and arrCall.getMethodName() = "[]" + | + n = -2 and // -2 is indicates that the index does not make sense in this context + result = arrCall.getKeywordArgument(_) + or + result = arrCall.getArgument(n) + ) + } + + override predicate returnsFormatted() { any() } +} + private import codeql.ruby.dataflow.FlowSteps private import codeql.ruby.CFG diff --git a/ruby/ql/test/query-tests/security/cwe-134/TaintedFormatString.expected b/ruby/ql/test/query-tests/security/cwe-134/TaintedFormatString.expected index 4404c74a8d5..09ac79c910c 100644 --- a/ruby/ql/test/query-tests/security/cwe-134/TaintedFormatString.expected +++ b/ruby/ql/test/query-tests/security/cwe-134/TaintedFormatString.expected @@ -12,6 +12,10 @@ edges | tainted_format_string.rb:33:32:33:46 | ...[...] : | tainted_format_string.rb:33:12:33:46 | ... + ... | | tainted_format_string.rb:36:30:36:35 | call to params : | tainted_format_string.rb:36:30:36:44 | ...[...] : | | tainted_format_string.rb:36:30:36:44 | ...[...] : | tainted_format_string.rb:36:12:36:46 | "A log message: #{...}" | +| tainted_format_string.rb:39:22:39:27 | call to params : | tainted_format_string.rb:39:22:39:36 | ...[...] : | +| tainted_format_string.rb:39:22:39:36 | ...[...] : | tainted_format_string.rb:39:5:39:45 | "A log message #{...} %{foo}" | +| tainted_format_string.rb:42:22:42:27 | call to params : | tainted_format_string.rb:42:22:42:36 | ...[...] : | +| tainted_format_string.rb:42:22:42:36 | ...[...] : | tainted_format_string.rb:42:5:42:43 | "A log message #{...} %08x" | nodes | tainted_format_string.rb:4:12:4:17 | call to params : | semmle.label | call to params : | | tainted_format_string.rb:4:12:4:26 | ...[...] | semmle.label | ...[...] | @@ -37,6 +41,12 @@ nodes | tainted_format_string.rb:36:12:36:46 | "A log message: #{...}" | semmle.label | "A log message: #{...}" | | tainted_format_string.rb:36:30:36:35 | call to params : | semmle.label | call to params : | | tainted_format_string.rb:36:30:36:44 | ...[...] : | semmle.label | ...[...] : | +| tainted_format_string.rb:39:5:39:45 | "A log message #{...} %{foo}" | semmle.label | "A log message #{...} %{foo}" | +| tainted_format_string.rb:39:22:39:27 | call to params : | semmle.label | call to params : | +| tainted_format_string.rb:39:22:39:36 | ...[...] : | semmle.label | ...[...] : | +| tainted_format_string.rb:42:5:42:43 | "A log message #{...} %08x" | semmle.label | "A log message #{...} %08x" | +| tainted_format_string.rb:42:22:42:27 | call to params : | semmle.label | call to params : | +| tainted_format_string.rb:42:22:42:36 | ...[...] : | semmle.label | ...[...] : | subpaths #select | tainted_format_string.rb:4:12:4:26 | ...[...] | tainted_format_string.rb:4:12:4:17 | call to params : | tainted_format_string.rb:4:12:4:26 | ...[...] | Format string depends on a $@. | tainted_format_string.rb:4:12:4:17 | call to params | user-provided value | @@ -50,3 +60,5 @@ subpaths | tainted_format_string.rb:28:19:28:33 | ...[...] | tainted_format_string.rb:28:19:28:24 | call to params : | tainted_format_string.rb:28:19:28:33 | ...[...] | Format string depends on a $@. | tainted_format_string.rb:28:19:28:24 | call to params | user-provided value | | tainted_format_string.rb:33:12:33:46 | ... + ... | tainted_format_string.rb:33:32:33:37 | call to params : | tainted_format_string.rb:33:12:33:46 | ... + ... | Format string depends on a $@. | tainted_format_string.rb:33:32:33:37 | call to params | user-provided value | | tainted_format_string.rb:36:12:36:46 | "A log message: #{...}" | tainted_format_string.rb:36:30:36:35 | call to params : | tainted_format_string.rb:36:12:36:46 | "A log message: #{...}" | Format string depends on a $@. | tainted_format_string.rb:36:30:36:35 | call to params | user-provided value | +| tainted_format_string.rb:39:5:39:45 | "A log message #{...} %{foo}" | tainted_format_string.rb:39:22:39:27 | call to params : | tainted_format_string.rb:39:5:39:45 | "A log message #{...} %{foo}" | Format string depends on a $@. | tainted_format_string.rb:39:22:39:27 | call to params | user-provided value | +| tainted_format_string.rb:42:5:42:43 | "A log message #{...} %08x" | tainted_format_string.rb:42:22:42:27 | call to params : | tainted_format_string.rb:42:5:42:43 | "A log message #{...} %08x" | Format string depends on a $@. | tainted_format_string.rb:42:22:42:27 | call to params | user-provided value | diff --git a/ruby/ql/test/query-tests/security/cwe-134/tainted_format_string.rb b/ruby/ql/test/query-tests/security/cwe-134/tainted_format_string.rb index 364f0ea1afb..aa66a9aa470 100644 --- a/ruby/ql/test/query-tests/security/cwe-134/tainted_format_string.rb +++ b/ruby/ql/test/query-tests/security/cwe-134/tainted_format_string.rb @@ -34,5 +34,11 @@ class UsersController < ActionController::Base # Taint via string interpolation printf("A log message: #{params[:format]}", arg) # BAD + + # Using String# + "A log message #{params[:format]} %{foo}" % {foo: "foo"} # BAD + + # String# with an array + "A log message #{params[:format]} %08x" % ["foo"] # BAD end end \ No newline at end of file From bb4bc55c6aef1f026e3a6ca19a9477f23c576432 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Mon, 17 Oct 2022 15:52:21 +0200 Subject: [PATCH 0009/1420] update expected output --- .../test/library-tests/dataflow/string-flow/string-flow.expected | 1 + 1 file changed, 1 insertion(+) diff --git a/ruby/ql/test/library-tests/dataflow/string-flow/string-flow.expected b/ruby/ql/test/library-tests/dataflow/string-flow/string-flow.expected index 6cebf219cd6..122a1d8eb1f 100644 --- a/ruby/ql/test/library-tests/dataflow/string-flow/string-flow.expected +++ b/ruby/ql/test/library-tests/dataflow/string-flow/string-flow.expected @@ -12,6 +12,7 @@ edges | string_flow.rb:10:29:10:29 | b : | string_flow.rb:10:10:10:30 | call to try_convert | | string_flow.rb:14:9:14:18 | call to source : | string_flow.rb:15:10:15:17 | ... % ... | | string_flow.rb:14:9:14:18 | call to source : | string_flow.rb:15:17:15:17 | a : | +| string_flow.rb:14:9:14:18 | call to source : | string_flow.rb:16:10:16:29 | ... % ... | | string_flow.rb:14:9:14:18 | call to source : | string_flow.rb:16:28:16:28 | a : | | string_flow.rb:14:9:14:18 | call to source : | string_flow.rb:17:10:17:10 | a : | | string_flow.rb:14:9:14:18 | call to source : | string_flow.rb:17:10:17:18 | ... % ... | From 0051ba1596e6b745be0214896faf062eb38f9403 Mon Sep 17 00:00:00 2001 From: Taus Date: Mon, 17 Oct 2022 13:10:56 +0000 Subject: [PATCH 0010/1420] Python: Add new module resolution implementation A fairly complicated bit of modelling, mostly due to the quirks of how imports are handled in Python. A few notes: - The handling of `__all__` is not actually needed (and perhaps not desirable, as it only pertains to `import *`, though it does match the current behaviour), but it might become useful at a later date, so I left it in. - Ideally, we would represent `foo as bar` in an `import` as a `DefinitionNode` in the CFG. I opted _not_ to do this, as it would also affect points-to, and I did not want to deal with any fallout arising from that. --- .../new/internal/ImportResolution.qll | 262 ++++++++++++++++++ .../dataflow/new/internal/ImportStar.qll | 2 +- 2 files changed, 263 insertions(+), 1 deletion(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll index 0c346fa2dd4..906460d76c1 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll @@ -1,14 +1,72 @@ +/** + * INTERNAL. DO NOT USE. + * + * Provides predicates for resolving imports. + */ + private import python private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.internal.ImportStar private import semmle.python.dataflow.new.TypeTracker +/** + * Python modules and the way imports are resolved are... complicated. Here's a crash course in how + * it works, as well as some caveats to bear in mind when looking at the implementation in this + * module. + * + * First, let's consider the humble `import` statement: + * ```python + * import foo + * import bar.baz + * import ham.eggs as spam + * ``` + * + * In the AST, all imports are aliased, as in the last import above. That is, `import foo` becomes + * `import foo as foo`, and `import bar.baz` becomes `import bar as bar`. Note that `import` is + * exclusively used to import modules -- if `eggs` is an attribute of the `ham` module (and not a + * submodule of the `ham` package), then the third line above is an error. + * + * Next, we have the `from` statement. This one is a bit more complicated, but still has the same + * aliasing desugaring as above applied to it. Thus, `from foo import bar` becomes + * `from foo import bar as bar`. + * + * In general, `from foo import bar` can mean two different things: + * + * 1. If `foo` is a module, and `bar` is an attribute of `foo`, then `from foo import bar` imports + * the attribute `bar` into the current module (binding it to the name `bar`). + * 2. If `foo` is a package, and `bar` is a submodule of `foo`, then `from foo import bar` first imports + * `foo.bar`, and then attempts to locate the `bar` attribute again. In most cases, that attribute + * will then point to the `bar` submodule. + * + * Now, when in comes to how these imports are represented in the AST, things get a bit complicated. + * First of all, both of the above forms of imports get mapped to the same kind of AST node: + * `Import`. An `Import` node has a sequence of names, each of which is an `Alias` node. This `Alias` + * node represents the `x as y` bit of each imported module. + * + * The same is true for `from` imports. So, how then do we distinguish between the two forms of + * imports? The distinguishing feature is the left hand side of the `as` node. If the left hand side + * is an `ImportExpr`, then it is a plain import. If it is an `ImportMember`, then it is a `from` + * import. (And to confuse matters even more, this `ImportMember` contains another `ImportExpr` for + * the bit between the `from` and `import` keywords.) + * + * Caveats: + * + * - A relative import of the form `from .foo import bar as baz` not only imports `bar` and binds it + * to the name `baz`, but also imports `foo` and binds it to the name `foo`. This only happens with + * relative imports. `from foo import bar as baz` only binds `bar` to `baz`. + * - Modules may also be packages, so e.g. `import foo.bar` may import the `bar` submodule in the `foo` + * package, or the `bar` subpackage of the `foo` package. The practical difference here is the name of + * the module that is imported, as the package `foo.bar` will have the "name" `foo.bar.__init__`, + * corresponding to the fact that the code that is executed is in the `__init__.py` file of the + * `bar` package. + */ module ImportResolution { /** * Holds if the module `m` defines a name `name` by assigning `defn` to it. This is an * overapproximation, as `name` may not in fact be exported (e.g. by defining an `__all__` that does * not include `name`). */ + pragma[nomagic] predicate module_export(Module m, string name, DataFlow::CfgNode defn) { exists(EssaVariable v | v.getName() = name and @@ -18,12 +76,216 @@ module ImportResolution { or defn.getNode() = v.getDefinition().(ArgumentRefinement).getArgument() ) + or + exists(Alias a | + defn.asExpr() = [a.getValue(), a.getValue().(ImportMember).getModule()] and + a.getAsname().(Name).getId() = name and + defn.getScope() = m + ) + } + + /** + * Holds if the module `m` explicitly exports the name `name` by listing it in `__all__`. Only + * handles simple cases where we can statically tell that this is the case. + */ + private predicate all_mentions_name(Module m, string name) { + exists(DefinitionNode def, SequenceNode n | + def.getValue() = n and + def.(NameNode).getId() = "__all__" and + def.getScope() = m and + any(StrConst s | s.getText() = name) = n.getAnElement().getNode() + ) + } + + /** + * Holds if the module `m` either does not set `__all__` (and so implicitly exports anything that + * doesn't start with an underscore), or sets `__all__` in a way that's too complicated for us to + * handle (in which case we _also_ pretend that it just exports all such names). + */ + private predicate no_or_complicated_all(Module m) { + // No mention of `__all__` in the module + not exists(DefinitionNode def | def.getScope() = m and def.(NameNode).getId() = "__all__") + or + // `__all__` is set to a non-sequence value + exists(DefinitionNode def | + def.(NameNode).getId() = "__all__" and + def.getScope() = m and + not def.getValue() instanceof SequenceNode + ) + or + // `__all__` is used in some way that doesn't involve storing a value in it. This usually means + // it is being mutated through `append` or `extend`, which we don't handle. + exists(NameNode n | n.getId() = "__all__" and n.getScope() = m and n.isLoad()) + } + + private predicate potential_module_export(Module m, string name) { + all_mentions_name(m, name) + or + no_or_complicated_all(m) and + ( + exists(NameNode n | n.getId() = name and n.getScope() = m and name.charAt(0) != "_") + or + exists(Alias a | a.getAsname().(Name).getId() = name and a.getValue().getScope() = m) + ) + } + + /** + * Holds if the module `reexporter` exports the module `reexported` under the name + * `reexported_name`. + */ + private predicate module_reexport(Module reexporter, string reexported_name, Module reexported) { + exists(DataFlow::Node ref | + ref = getImmediateModuleReference(reexported) and + module_export(reexporter, reexported_name, ref) and + potential_module_export(reexporter, reexported_name) + ) + } + + /** + * Gets a reference to `sys.modules`. + */ + private DataFlow::Node sys_modules_reference() { + result = + any(DataFlow::AttrRef a | + a.getAttributeName() = "modules" and a.getObject().asExpr().(Name).getId() = "sys" + ) + } + + /** Gets a module that may have been added to `sys.modules`. */ + private Module sys_modules_module_with_name(string name) { + exists(ControlFlowNode n, DataFlow::Node mod | + exists(SubscriptNode sub | + sub.getObject() = sys_modules_reference().asCfgNode() and + sub.getIndex() = n and + n.getNode().(StrConst).getText() = name and + sub.(DefinitionNode).getValue() = mod.asCfgNode() and + mod = getModuleReference(result) + ) + ) } Module getModule(DataFlow::CfgNode node) { exists(ModuleValue mv | node.getNode().pointsTo(mv) and result = mv.getScope() + Module getModuleImportedByImportStar(ImportStar i) { + isPreferredModuleForName(result.getFile(), i.getImportedModuleName()) + } + + /** Gets a data-flow node that may be a reference to a module with the name `module_name`. */ + DataFlow::Node getReferenceToModuleName(string module_name) { + // Regular import statements, e.g. + // import foo # implicitly `import foo as foo` + // import foo as foo_alias + exists(Import i, Alias a | a = i.getAName() | + result.asExpr() = a.getAsname() and + module_name = a.getValue().(ImportExpr).getImportedModuleName() + ) + or + // The module part of a `from ... import ...` statement, e.g. the `..foo.bar` in + // from ..foo.bar import baz # ..foo.bar might point to, say, package.subpackage.foo.bar + exists(ImportMember i | result.asExpr() = i.getModule() | + module_name = i.getModule().(ImportExpr).getImportedModuleName() + ) + or + // Modules (not attributes) imported via `from ... import ... statements`, e.g. + // from foo.bar import baz # imports foo.bar.baz as baz + // from foo.bar import baz as baz_alias # imports foo.bar.baz as baz_alias + exists(Import i, Alias a, ImportMember im | a = i.getAName() and im = a.getValue() | + i.isFromImport() and + result.asExpr() = a.getAsname() and + module_name = im.getModule().(ImportExpr).getImportedModuleName() + "." + im.getName() + ) + or + // For parity with the points-to based solution, the `ImportExpr` and `ImportMember` bits of the + // above cases should _also_ point to the right modules. + result.asExpr() = any(ImportExpr i | i.getImportedModuleName() = module_name) + or + result.asExpr() = + any(ImportMember i | + i.getModule().(ImportExpr).getImportedModuleName() = module_name + or + i.getModule().(ImportExpr).getImportedModuleName() + "." + i.getName() = module_name and + none() + ) + } + + /** Gets a dataflow node that is an immediate reference to the module `m`. */ + DataFlow::Node getImmediateModuleReference(Module m) { + exists(string module_name | result = getReferenceToModuleName(module_name) | + // Depending on whether the referenced module is a package or not, we may need to add a + // trailing `.__init__` to the module name. + isPreferredModuleForName(m.getFile(), module_name + ["", ".__init__"]) + or + // Module defined via `sys.modules` + m = sys_modules_module_with_name(module_name) + ) + or + // Reading an attribute on a module may return a submodule (or subpackage). + exists(DataFlow::AttrRead ar, Module p, string attr_name | + ar.getObject() = getModuleReference(p) and + attr_name = any(Module m0).getFile().getStem() and + ar.getAttributeName() = attr_name and + result = ar + | + isPreferredModuleForName(m.getFile(), p.getPackageName() + "." + attr_name + ["", ".__init__"]) + or + // This is also true for attributes that come from reexports. + module_reexport(p, attr_name, m) + ) + or + // Submodules that are implicitly defined when importing via `from ... import ...` statements. + // In practice, we create a definition for each module in a package, even if it is not imported. + exists(string submodule, Module package | + SsaSource::init_module_submodule_defn(result.asVar().getSourceVariable(), + package.getEntryNode()) and + isPreferredModuleForName(m.getFile(), + package.getPackageName() + "." + submodule + ["", ".__init__"]) ) } + + /** Join-order helper for `getModuleReference`. */ + pragma[nomagic] + private predicate module_name_in_scope(DataFlow::Node node, Scope s, string name, Module m) { + node.getScope() = s and + node.asExpr().(Name).getId() = name and + pragma[only_bind_into](node) = getImmediateModuleReference(pragma[only_bind_into](m)) + } + + /** Join-order helper for `getModuleReference`. */ + pragma[nomagic] + private predicate module_reference_in_scope(DataFlow::Node node, Scope s, string name) { + node.getScope() = s and + exists(Name n | n = node.asExpr() | + n.getId() = name and + pragma[only_bind_into](n).isUse() + ) + } + + /** + * Gets a reference to the module `m` (including through certain kinds of local and global flow). + */ + DataFlow::Node getModuleReference(Module m) { + // Immedate references to the module + result = getImmediateModuleReference(m) + or + // Flow (local or global) forward to a later reference to the module. + exists(DataFlow::Node ref | ref = getModuleReference(m) | + DataFlow::localFlow(ref, result) + or + exists(DataFlow::ModuleVariableNode mv | + mv.getAWrite() = ref and + result = mv.getARead() + ) + ) + or + // A reference to a name that is bound to a module in an enclosing scope. + exists(DataFlow::Node def, Scope def_scope, Scope use_scope, string name | + module_name_in_scope(pragma[only_bind_into](def), pragma[only_bind_into](def_scope), + pragma[only_bind_into](name), pragma[only_bind_into](m)) and + module_reference_in_scope(result, use_scope, name) and + use_scope.getEnclosingScope*() = def_scope + ) + } + } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/ImportStar.qll b/python/ql/lib/semmle/python/dataflow/new/internal/ImportStar.qll index ae115342dba..564630c47db 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/ImportStar.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/ImportStar.qll @@ -76,7 +76,7 @@ module ImportStar { exists(ImportStar i, DataFlow::CfgNode imported_module | imported_module.getNode().getNode() = i.getModule() and i.getScope() = m and - result = ImportResolution::getModule(imported_module) + result = ImportResolution::getModuleImportedByImportStar(i) ) } From 651afaf11b9ee9798a09f670dba1f1b97008d124 Mon Sep 17 00:00:00 2001 From: Taus Date: Mon, 17 Oct 2022 13:15:07 +0000 Subject: [PATCH 0011/1420] Python: Hook up new implementation Left as its own commit, as otherwise the diff would have been very confusing. --- .../semmle/python/dataflow/new/internal/ImportResolution.qll | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll index 906460d76c1..e1e4381519c 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll @@ -164,10 +164,6 @@ module ImportResolution { ) } - Module getModule(DataFlow::CfgNode node) { - exists(ModuleValue mv | - node.getNode().pointsTo(mv) and - result = mv.getScope() Module getModuleImportedByImportStar(ImportStar i) { isPreferredModuleForName(result.getFile(), i.getImportedModuleName()) } @@ -288,4 +284,5 @@ module ImportResolution { ) } + Module getModule(DataFlow::CfgNode node) { node = getModuleReference(result) } } From ad13fbaeb6b98d169dcfeac6ff5d95230a5b541d Mon Sep 17 00:00:00 2001 From: Taus Date: Mon, 17 Oct 2022 13:42:24 +0000 Subject: [PATCH 0012/1420] Python: Add tests A slightly complicated test setup. I wanted to both make sure I captured the semantics of Python and also the fact that the kinds of global flow we expect to see are indeed present. The code is executable, and prints out both when the execution reaches certain files, and also what values are assigned to the various attributes that are referenced throughout the program. These values are validated in the test as well. My original version used introspection to avoid referencing attributes directly (thus enabling better error diagnostics), but unfortunately that made it so that the model couldn't follow what was going on. The current setup is a bit clunky (and Python's scoping rules makes it especially so -- cf. the explicit calls to `globals` and `locals`), but I think it does the job okay. --- .../experimental/import-resolution/bar.py | 6 ++ .../experimental/import-resolution/foo.py | 14 ++++ .../import-resolution/importflow.expected | 0 .../import-resolution/importflow.ql | 41 ++++++++++++ .../import-resolution/imports.expected | 0 .../experimental/import-resolution/imports.ql | 49 ++++++++++++++ .../experimental/import-resolution/main.py | 65 +++++++++++++++++++ .../namespace_package/namespace_module.py | 6 ++ .../import-resolution/package/__init__.py | 7 ++ .../package/subpackage/__init__.py | 14 ++++ .../package/subpackage/submodule.py | 7 ++ .../experimental/import-resolution/trace.py | 49 ++++++++++++++ 12 files changed, 258 insertions(+) create mode 100644 python/ql/test/experimental/import-resolution/bar.py create mode 100644 python/ql/test/experimental/import-resolution/foo.py create mode 100644 python/ql/test/experimental/import-resolution/importflow.expected create mode 100644 python/ql/test/experimental/import-resolution/importflow.ql create mode 100644 python/ql/test/experimental/import-resolution/imports.expected create mode 100644 python/ql/test/experimental/import-resolution/imports.ql create mode 100644 python/ql/test/experimental/import-resolution/main.py create mode 100644 python/ql/test/experimental/import-resolution/namespace_package/namespace_module.py create mode 100644 python/ql/test/experimental/import-resolution/package/__init__.py create mode 100644 python/ql/test/experimental/import-resolution/package/subpackage/__init__.py create mode 100644 python/ql/test/experimental/import-resolution/package/subpackage/submodule.py create mode 100644 python/ql/test/experimental/import-resolution/trace.py diff --git a/python/ql/test/experimental/import-resolution/bar.py b/python/ql/test/experimental/import-resolution/bar.py new file mode 100644 index 00000000000..5cb6339be17 --- /dev/null +++ b/python/ql/test/experimental/import-resolution/bar.py @@ -0,0 +1,6 @@ +from trace import * +enter(__file__) + +bar_attr = "bar_attr" + +exit(__file__) diff --git a/python/ql/test/experimental/import-resolution/foo.py b/python/ql/test/experimental/import-resolution/foo.py new file mode 100644 index 00000000000..d112007ad03 --- /dev/null +++ b/python/ql/test/experimental/import-resolution/foo.py @@ -0,0 +1,14 @@ +from trace import * +enter(__file__) + +# A simple attribute. Used in main.py +foo_attr = "foo_attr" + +# A private attribute. Accessible from main.py despite this. +__private_foo_attr = "__private_foo_attr" + +# A reexport of bar under a new name. Used in main.py +import bar as bar_reexported #$ imports=bar as=bar_reexported +check("bar_reexported.bar_attr", bar_reexported.bar_attr, "bar_attr", globals()) #$ prints=bar_attr + +exit(__file__) diff --git a/python/ql/test/experimental/import-resolution/importflow.expected b/python/ql/test/experimental/import-resolution/importflow.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/experimental/import-resolution/importflow.ql b/python/ql/test/experimental/import-resolution/importflow.ql new file mode 100644 index 00000000000..0e7d300d328 --- /dev/null +++ b/python/ql/test/experimental/import-resolution/importflow.ql @@ -0,0 +1,41 @@ +import python +import semmle.python.dataflow.new.DataFlow +import semmle.python.ApiGraphs +import TestUtilities.InlineExpectationsTest + +private class SourceString extends DataFlow::Node { + string contents; + + SourceString() { + this.asExpr().(StrConst).getText() = contents and + this.asExpr().getParent() instanceof Assign + } + + string getContents() { result = contents } +} + +private class ImportConfiguration extends DataFlow::Configuration { + ImportConfiguration() { this = "ImportConfiguration" } + + override predicate isSource(DataFlow::Node source) { source instanceof SourceString } + + override predicate isSink(DataFlow::Node sink) { + sink = API::moduleImport("trace").getMember("check").getACall().getArg(1) + } +} + +class ResolutionTest extends InlineExpectationsTest { + ResolutionTest() { this = "ResolutionTest" } + + override string getARelevantTag() { result = "import" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + exists(DataFlow::PathNode source, DataFlow::PathNode sink, ImportConfiguration config | + config.hasFlowPath(source, sink) and + tag = "prints" and + location = sink.getNode().getLocation() and + value = source.getNode().(SourceString).getContents() and + element = sink.getNode().toString() + ) + } +} diff --git a/python/ql/test/experimental/import-resolution/imports.expected b/python/ql/test/experimental/import-resolution/imports.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/experimental/import-resolution/imports.ql b/python/ql/test/experimental/import-resolution/imports.ql new file mode 100644 index 00000000000..e5d357c5ef4 --- /dev/null +++ b/python/ql/test/experimental/import-resolution/imports.ql @@ -0,0 +1,49 @@ +import python +import TestUtilities.InlineExpectationsTest +import semmle.python.dataflow.new.DataFlow +import semmle.python.dataflow.new.internal.ImportResolution + +private class ImmediateModuleRef extends DataFlow::Node { + Module mod; + string alias; + + ImmediateModuleRef() { + this = ImportResolution::getImmediateModuleReference(mod) and + not mod.getName() in ["__future__", "trace"] and + this.asExpr() = any(Alias a | alias = a.getAsname().(Name).getId()).getAsname() + } + + Module getModule() { result = mod } + + string getAsname() { result = alias } +} + +class ImportTest extends InlineExpectationsTest { + ImportTest() { this = "ImportTest" } + + override string getARelevantTag() { result = "imports" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + exists(ImmediateModuleRef ref | + tag = "imports" and + location = ref.getLocation() and + value = ref.getModule().getName() and + element = ref.toString() + ) + } +} + +class AliasTest extends InlineExpectationsTest { + AliasTest() { this = "AliasTest" } + + override string getARelevantTag() { result = "as" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + exists(ImmediateModuleRef ref | + tag = "as" and + location = ref.getLocation() and + value = ref.getAsname() and + element = ref.toString() + ) + } +} diff --git a/python/ql/test/experimental/import-resolution/main.py b/python/ql/test/experimental/import-resolution/main.py new file mode 100644 index 00000000000..827bb78896b --- /dev/null +++ b/python/ql/test/experimental/import-resolution/main.py @@ -0,0 +1,65 @@ +#! /usr/bin/env python3 + +from __future__ import print_function +import sys +from trace import * +enter(__file__) + +# A simple import. Binds foo to the foo module +import foo #$ imports=foo as=foo +check("foo.foo_attr", foo.foo_attr, "foo_attr", globals()) #$ prints=foo_attr + +# Private attributes are still accessible. +check("foo.__private_foo_attr", foo.__private_foo_attr, "__private_foo_attr", globals()) #$ prints=__private_foo_attr + +# An aliased import, binding foo to foo_alias +import foo as foo_alias #$ imports=foo as=foo_alias +check("foo_alias.foo_attr", foo_alias.foo_attr, "foo_attr", globals()) #$ prints=foo_attr + +# A reference to a reexported module +check("foo.bar_reexported.bar_attr", foo.bar_reexported.bar_attr, "bar_attr", globals()) #$ prints=bar_attr + +# A simple "import from" statement. +from bar import bar_attr +check("bar_attr", bar_attr, "bar_attr", globals()) #$ prints=bar_attr + +# Importing an attribute from a subpackage of a package. +from package.subpackage import subpackage_attr +check("subpackage_attr", subpackage_attr, "subpackage_attr", globals()) #$ prints=subpackage_attr + +# Importing a package attribute under an alias. +from package import package_attr as package_attr_alias +check("package_attr_alias", package_attr_alias, "package_attr", globals()) #$ prints=package_attr + +# Importing a subpackage under an alias. +from package import subpackage as aliased_subpackage #$ imports=package.subpackage.__init__ as=aliased_subpackage +check("aliased_subpackage.subpackage_attr", aliased_subpackage.subpackage_attr, "subpackage_attr", globals()) #$ prints=subpackage_attr + +def local_import(): + # Same as above, but in a local scope. + import package.subpackage as local_subpackage #$ imports=package.subpackage.__init__ as=local_subpackage + check("local_subpackage.subpackage_attr", local_subpackage.subpackage_attr, "subpackage_attr", locals()) #$ prints=subpackage_attr + +local_import() + +# Importing a subpacking using `import` and binding it to a name. +import package.subpackage as aliased_subpackage #$ imports=package.subpackage.__init__ as=aliased_subpackage +check("aliased_subpackage.subpackage_attr", aliased_subpackage.subpackage_attr, "subpackage_attr", globals()) #$ prints=subpackage_attr + +# Importing without binding instead binds the top level name. +import package.subpackage #$ imports=package.__init__ as=package +check("package.package_attr", package.package_attr, "package_attr", globals()) #$ prints=package_attr + +if sys.version_info[0] >= 3: + # Importing from a namespace module. + from namespace_package.namespace_module import namespace_module_attr + check("namespace_module_attr", namespace_module_attr, "namespace_module_attr", globals()) #$ prints=namespace_module_attr + +exit(__file__) + +print() + +if status() == 0: + print("PASS") +else: + print("FAIL") diff --git a/python/ql/test/experimental/import-resolution/namespace_package/namespace_module.py b/python/ql/test/experimental/import-resolution/namespace_package/namespace_module.py new file mode 100644 index 00000000000..2c831662f47 --- /dev/null +++ b/python/ql/test/experimental/import-resolution/namespace_package/namespace_module.py @@ -0,0 +1,6 @@ +from trace import * +enter(__file__) + +namespace_module_attr = "namespace_module_attr" + +exit(__file__) diff --git a/python/ql/test/experimental/import-resolution/package/__init__.py b/python/ql/test/experimental/import-resolution/package/__init__.py new file mode 100644 index 00000000000..ee2e3211a2c --- /dev/null +++ b/python/ql/test/experimental/import-resolution/package/__init__.py @@ -0,0 +1,7 @@ +from trace import * +enter(__file__) + +attr_used_in_subpackage = "attr_used_in_subpackage" +package_attr = "package_attr" + +exit(__file__) diff --git a/python/ql/test/experimental/import-resolution/package/subpackage/__init__.py b/python/ql/test/experimental/import-resolution/package/subpackage/__init__.py new file mode 100644 index 00000000000..86cc92dd37b --- /dev/null +++ b/python/ql/test/experimental/import-resolution/package/subpackage/__init__.py @@ -0,0 +1,14 @@ +from trace import * +enter(__file__) + +subpackage_attr = "subpackage_attr" + +# Importing an attribute from the parent package. +from .. import attr_used_in_subpackage as imported_attr +check("imported_attr", imported_attr, "attr_used_in_subpackage", globals()) #$ prints=attr_used_in_subpackage + +# Importing an irrelevant attribute from a sibling module binds the name to the module. +from .submodule import irrelevant_attr +check("submodule.submodule_attr", submodule.submodule_attr, "submodule_attr", globals()) #$ prints=submodule_attr + +exit(__file__) diff --git a/python/ql/test/experimental/import-resolution/package/subpackage/submodule.py b/python/ql/test/experimental/import-resolution/package/subpackage/submodule.py new file mode 100644 index 00000000000..89f9dd497a4 --- /dev/null +++ b/python/ql/test/experimental/import-resolution/package/subpackage/submodule.py @@ -0,0 +1,7 @@ +from trace import * +enter(__file__) + +submodule_attr = "submodule_attr" +irrelevant_attr = "irrelevant_attr" + +exit(__file__) diff --git a/python/ql/test/experimental/import-resolution/trace.py b/python/ql/test/experimental/import-resolution/trace.py new file mode 100644 index 00000000000..04e847304e9 --- /dev/null +++ b/python/ql/test/experimental/import-resolution/trace.py @@ -0,0 +1,49 @@ +from __future__ import print_function + +_indent_level = 0 + +_print = print + +def print(*args, **kwargs): + _print(" " * _indent_level, end="") + _print(*args, **kwargs) + +def enter(file_name): + global _indent_level + print("Entering {}".format(file_name)) + _indent_level += 1 + +def exit(file_name): + global _indent_level + _indent_level -= 1 + print("Leaving {}".format(file_name)) + +_status = 0 + +def status(): + return _status + +def check(attr_path, actual_value, expected_value, bindings): + parts = attr_path.split(".") + base, parts = parts[0], parts[1:] + if base not in bindings: + print("Error: {} not in bindings".format(base)) + _status = 1 + return + val = bindings[base] + for part in parts: + if not hasattr(val, part): + print("Error: Unknown attribute {}".format(part)) + _status = 1 + return + val = getattr(val, part) + if val != actual_value: + print("Error: Value at path {} and actual value are out of sync! {} != {}".format(attr_path, val, actual_value)) + _status = 1 + if val != expected_value: + print("Error: Expected {} to be {}, got {}".format(attr_path, expected_value, val)) + _status = 1 + return + print("OK: {} = {}".format(attr_path, val)) + +__all__ = ["enter", "exit", "check", "status"] From 58754982cefc5d4b7c2b4082d233a81d06f35066 Mon Sep 17 00:00:00 2001 From: Taus Date: Mon, 17 Oct 2022 14:34:10 +0000 Subject: [PATCH 0013/1420] Python: Update type tracking tests No longer missing! :tada: --- .../experimental/dataflow/typetracking_imports/pkg/use.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/ql/test/experimental/dataflow/typetracking_imports/pkg/use.py b/python/ql/test/experimental/dataflow/typetracking_imports/pkg/use.py index 023af8684f2..a23ddc54b2d 100644 --- a/python/ql/test/experimental/dataflow/typetracking_imports/pkg/use.py +++ b/python/ql/test/experimental/dataflow/typetracking_imports/pkg/use.py @@ -6,8 +6,8 @@ test_direct_import() def test_alias_problem(): - from .alias_problem import foo # $ MISSING: tracked - print(foo) # $ MISSING: tracked + from .alias_problem import foo # $ tracked + print(foo) # $ tracked test_alias_problem() @@ -34,8 +34,8 @@ test_alias_only_direct() def test_problem_absolute_import(): - from pkg.problem_absolute_import import foo # $ MISSING: tracked - print(foo) # $ MISSING: tracked + from pkg.problem_absolute_import import foo # $ tracked + print(foo) # $ tracked test_problem_absolute_import() From 6ab62da015ff8cc69e67ddcdda2df5ba0d32a75d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz?= Date: Mon, 3 Oct 2022 15:50:57 +0200 Subject: [PATCH 0014/1420] Add Restify/Spife support --- ...-restify-framework-support-improvements.md | 5 + .../2022-10-13-spife-framework-support.md | 5 + .../javascript/frameworks/HttpFrameworks.qll | 1 + .../semmle/javascript/frameworks/Restify.qll | 376 +++++++++++++-- .../semmle/javascript/frameworks/Spife.qll | 440 ++++++++++++++++++ .../heuristics/AdditionalRouteHandlers.qll | 16 + .../HTTP/RemoteRequestInput.expected | 1 - .../frameworks/NodeJSLib/tests.expected | 2 + .../frameworks/Restify2/src/index.js | 207 ++++++++ .../frameworks/Restify2/tests.expected | 105 +++++ .../frameworks/Restify2/tests.ql | 194 ++++++++ .../frameworks/Spife/lib/routes/index.js | 17 + .../frameworks/Spife/lib/routes/parameters.js | 0 .../frameworks/Spife/lib/settings.js | 46 ++ .../frameworks/Spife/lib/views/index.js | 115 +++++ .../frameworks/Spife/tests.expected | 75 +++ .../library-tests/frameworks/Spife/tests.ql | 194 ++++++++ .../frameworks/restify/tests.expected | 12 + 18 files changed, 1775 insertions(+), 36 deletions(-) create mode 100644 javascript/ql/lib/change-notes/2022-10-13-restify-framework-support-improvements.md create mode 100644 javascript/ql/lib/change-notes/2022-10-13-spife-framework-support.md create mode 100644 javascript/ql/lib/semmle/javascript/frameworks/Spife.qll create mode 100644 javascript/ql/test/library-tests/frameworks/Restify2/src/index.js create mode 100644 javascript/ql/test/library-tests/frameworks/Restify2/tests.expected create mode 100644 javascript/ql/test/library-tests/frameworks/Restify2/tests.ql create mode 100644 javascript/ql/test/library-tests/frameworks/Spife/lib/routes/index.js create mode 100644 javascript/ql/test/library-tests/frameworks/Spife/lib/routes/parameters.js create mode 100644 javascript/ql/test/library-tests/frameworks/Spife/lib/settings.js create mode 100644 javascript/ql/test/library-tests/frameworks/Spife/lib/views/index.js create mode 100644 javascript/ql/test/library-tests/frameworks/Spife/tests.expected create mode 100644 javascript/ql/test/library-tests/frameworks/Spife/tests.ql diff --git a/javascript/ql/lib/change-notes/2022-10-13-restify-framework-support-improvements.md b/javascript/ql/lib/change-notes/2022-10-13-restify-framework-support-improvements.md new file mode 100644 index 00000000000..99e19b51757 --- /dev/null +++ b/javascript/ql/lib/change-notes/2022-10-13-restify-framework-support-improvements.md @@ -0,0 +1,5 @@ +--- +category: feature +--- + +- Improved support for [Restify](http://restify.com/) framework, leading to more results when scanning applications developed with this framework. diff --git a/javascript/ql/lib/change-notes/2022-10-13-spife-framework-support.md b/javascript/ql/lib/change-notes/2022-10-13-spife-framework-support.md new file mode 100644 index 00000000000..52606d44502 --- /dev/null +++ b/javascript/ql/lib/change-notes/2022-10-13-spife-framework-support.md @@ -0,0 +1,5 @@ +--- +category: feature +--- + +- Added support for the [Spife](https://github.com/npm/spife) framework. diff --git a/javascript/ql/lib/semmle/javascript/frameworks/HttpFrameworks.qll b/javascript/ql/lib/semmle/javascript/frameworks/HttpFrameworks.qll index 7568280dcc4..8e91230d3b2 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/HttpFrameworks.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/HttpFrameworks.qll @@ -7,3 +7,4 @@ import semmle.javascript.frameworks.Micro import semmle.javascript.frameworks.Restify import semmle.javascript.frameworks.Connect import semmle.javascript.frameworks.Fastify +import semmle.javascript.frameworks.Spife diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll b/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll index 889369c6ea4..fb80beb4832 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll @@ -4,7 +4,11 @@ import javascript import semmle.javascript.frameworks.HTTP +import semmle.javascript.security.dataflow.RequestForgeryCustomizations as RFC +/** + * Provides classes for working with [Restify](https://restify.com/) servers. + */ module Restify { /** * An expression that creates a new Restify server. @@ -19,23 +23,23 @@ module Restify { /** * A Restify route handler. */ - class RouteHandler extends Http::Servers::StandardRouteHandler, DataFlow::ValueNode { - Function function; - - RouteHandler() { - function = astNode and - any(RouteSetup setup).getARouteHandler() = this - } - + abstract class RouteHandler extends Http::Servers::StandardRouteHandler, DataFlow::FunctionNode { /** * Gets the parameter of the route handler that contains the request object. */ - Parameter getRequestParameter() { result = function.getParameter(0) } + DataFlow::ParameterNode getRequestParameter() { result = this.getParameter(0) } /** * Gets the parameter of the route handler that contains the response object. */ - Parameter getResponseParameter() { result = function.getParameter(1) } + DataFlow::ParameterNode getResponseParameter() { result = this.getParameter(1) } + } + + /** + * A standard Restify route handler. + */ + class StandardRouteHandler extends RouteHandler, DataFlow::FunctionNode { + StandardRouteHandler() { any(RouteSetup setup).getARouteHandler() = this } } /** @@ -45,7 +49,7 @@ module Restify { private class ResponseSource extends Http::Servers::ResponseSource { RouteHandler rh; - ResponseSource() { this = DataFlow::parameterNode(rh.getResponseParameter()) } + ResponseSource() { this = rh.getResponseParameter() } /** * Gets the route handler that provides this response. @@ -60,7 +64,7 @@ module Restify { private class RequestSource extends Http::Servers::RequestSource { RouteHandler rh; - RequestSource() { this = DataFlow::parameterNode(rh.getRequestParameter()) } + RequestSource() { this = rh.getRequestParameter() } /** * Gets the route handler that handles this request. @@ -80,7 +84,7 @@ module Restify { * A Node.js HTTP response provided by Restify. */ class ResponseNode extends NodeJSLib::ResponseNode { - ResponseNode() { src instanceof ResponseSource } + ResponseNode() { src instanceof ResponseSource or src instanceof FormatterResponseSource } } /** @@ -95,14 +99,14 @@ module Restify { * A Node.js HTTP request provided by Restify. */ class RequestNode extends NodeJSLib::RequestNode { - RequestNode() { src instanceof RequestSource } + RequestNode() { src instanceof RequestSource or src instanceof FormatterRequestSource } } /** * An access to a user-controlled Restify request input. */ private class RequestInputAccess extends Http::RequestInputAccess { - RequestNode request; + Http::RequestNode request; string kind; RequestInputAccess() { @@ -113,23 +117,17 @@ module Restify { this.(DataFlow::PropRead).accesses(query, _) ) or - exists(string methodName | - // `request.href()` or `request.getPath()` - kind = "url" and - this.(DataFlow::MethodCallNode).calls(request, methodName) - | - methodName = "href" or - methodName = "getPath" + exists(DataFlow::PropRead prop | + // `request.params.` + // `request.query.` + kind = "parameter" and + prop.accesses(request, ["params", "query"]) and + this.(DataFlow::PropRead).accesses(prop, _) ) or - // `request.getContentType()`, `request.userAgent()`, `request.trailer(...)`, `request.header(...)` - kind = "header" and - this.(DataFlow::MethodCallNode) - .calls(request, ["getContentType", "userAgent", "trailer", "header"]) - or - // `req.cookies - kind = "cookie" and - this.(DataFlow::PropRead).accesses(request, "cookies") + // `request.href()` or `request.getPath()` + kind = "url" and + this.(DataFlow::MethodCallNode).calls(request, ["href", "getPath"]) } override RouteHandler getRouteHandler() { result = request.getRouteHandler() } @@ -137,12 +135,35 @@ module Restify { override string getKind() { result = kind } } + /** + * An access to a header on a Restify request. + */ + private class RequestHeaderAccess extends Http::RequestHeaderAccess { + RouteHandler rh; + + RequestHeaderAccess() { + // `request.getContentType()`, `request.userAgent()`, `request.trailer(...)`, `request.header(...)` + this = + rh.getARequestSource() + .ref() + .getAMethodCall(["header", "trailer", "userAgent", "getContentType"]) + } + + override string getAHeaderName() { + result = this.(DataFlow::MethodCallNode).getArgument(0).getStringValue().toLowerCase() + } + + override RouteHandler getRouteHandler() { result = rh } + + override string getKind() { result = "header" } + } + /** * An HTTP header defined in a Restify server. */ private class HeaderDefinition extends Http::Servers::StandardHeaderDefinition { HeaderDefinition() { - // response.header('Cache-Control', 'no-cache') + // res.header('Cache-Control', 'no-cache') this.getReceiver() instanceof ResponseNode and this.getMethodName() = "header" } @@ -150,6 +171,41 @@ module Restify { override RouteHandler getRouteHandler() { this.getReceiver() = result.getAResponseNode() } } + /** + * An invocation that sets any number of headers of the HTTP response. + */ + private class MultipleHeaderDefinitions extends Http::ExplicitHeaderDefinition, + DataFlow::MethodCallNode { + MultipleHeaderDefinitions() { + // res.set({'Cache-Control': 'no-cache'}) + this.getReceiver() instanceof ResponseNode and + this.getMethodName() = "set" + } + + /** + * Gets a reference to the multiple headers object that is to be set. + */ + private DataFlow::SourceNode getAHeaderSource() { + this.getArgument(0).getALocalSource() instanceof DataFlow::ObjectLiteralNode and + result.flowsTo(this.getArgument(0)) + } + + override predicate definesHeaderValue(string headerName, DataFlow::Node headerValue) { + exists(string header | + this.getAHeaderSource().hasPropertyWrite(header, headerValue) and + headerName = header.toLowerCase() + ) + } + + override DataFlow::Node getNameNode() { + exists(DataFlow::PropWrite write | this.getAHeaderSource().getAPropertyWrite() = write | + result = write.getPropertyNameExpr().flow() + ) + } + + override RouteHandler getRouteHandler() { this.getReceiver() = result.getAResponseNode() } + } + /** * A call to a Restify method that sets up a route. */ @@ -157,13 +213,263 @@ module Restify { ServerDefinition server; RouteSetup() { - // server.get('/', fun) - // server.head('/', fun) - server.ref().getAMethodCall(any(Http::RequestMethodName m).toLowerCase()) = this + server + .ref() + .getAMethodCall([ + "del", "get", "head", "opts", "post", "put", "patch", "param", "pre", "use", "on" + ]) = this } - override DataFlow::SourceNode getARouteHandler() { result.flowsTo(this.getArgument(1)) } + override DataFlow::SourceNode getARouteHandler() { + exists(DataFlow::Node arg | + // server.get('/', fun) + // server.get('/', fun1, fun2) + // server.get('/', [fun1, fun2]) + // server.param('name', fun) + // server.on('event', fun) + this.getMethodName() = ["del", "get", "head", "opts", "post", "put", "patch", "param", "on"] and + arg = this.getAnArgument() and + not arg = this.getArgument(0) + or + // server.use(fun) + // server.use(fun1, fun2) + // server.use([fun1, fun2]) + this.getMethodName() = ["use", "pre"] and + arg = this.getAnArgument() + | + ( + // server.use(fun1, fun2) + result.flowsTo(arg) and + not arg.getALocalSource() instanceof DataFlow::ArrayCreationNode + or + result.flowsTo(arg.getALocalSource().(DataFlow::ArrayCreationNode).getAnElement()) + ) + ) + } override DataFlow::Node getServer() { result = server } } + + /** + * A call to a Restify's createServer method that sets up a formatter. + */ + class FormatterSetup extends DataFlow::CallNode { + DataFlow::ObjectLiteralNode formatters; + + FormatterSetup() { + // `server = restify.createServer({ formatters: { ... } })` + this = DataFlow::moduleMember("restify", "createServer").getACall() and + this.getArgument(0) + .getALocalSource() + .(DataFlow::ObjectLiteralNode) + .hasPropertyWrite("formatters", formatters) + } + + DataFlow::SourceNode getAFormatterHandler() { formatters.hasPropertyWrite(_, result) } + } + + /** + * A Restify route handler. + */ + class FormatterHandler extends Http::Servers::StandardRouteHandler, DataFlow::FunctionNode { + Function function; + + FormatterHandler() { + function = astNode and + any(FormatterSetup setup).getAFormatterHandler() = this + } + + /** + * Gets the parameter of the formatter handler that contains the request object. + */ + Parameter getRequestParameter() { result = function.getParameter(0) } + + /** + * Gets the parameter of the formatter handler that contains the response object. + */ + Parameter getResponseParameter() { result = function.getParameter(1) } + + /** + * Gets the parameter of the formatter handler that contains the body object. + */ + Parameter getBodyParameter() { result = function.getParameter(2) } + } + + /** + * A Restify request source, that is, the request parameter of a + * route handler. + */ + private class FormatterRequestSource extends Http::Servers::RequestSource { + FormatterHandler fh; + + FormatterRequestSource() { this = DataFlow::parameterNode(fh.getRequestParameter()) } + + /** + * Gets the formatter handler that handles this request. + */ + override RouteHandler getRouteHandler() { result = fh } + } + + /** + * A Restify response source, that is, the response parameter of a + * route handler. + */ + private class FormatterResponseSource extends Http::Servers::ResponseSource { + FormatterHandler fh; + + FormatterResponseSource() { this = DataFlow::parameterNode(fh.getResponseParameter()) } + + /** + * Gets the route handler that provides this response. + */ + override RouteHandler getRouteHandler() { result = fh } + } + + /** + * An argument passed to the `send` method of an HTTP response object. + */ + private class ResponseSendArgument extends Http::ResponseSendArgument { + RouteHandler rh; + + ResponseSendArgument() { + this = rh.getAResponseSource().ref().getAMethodCall(["send", "sendRaw"]).getArgument(0) + } + + override RouteHandler getRouteHandler() { result = rh } + } + + /** + * An expression returned by a formatter + */ + private class FormatterOutput extends Http::ResponseSendArgument { + FormatterHandler fh; + + FormatterOutput() { this = fh.getAReturn() } + + override Http::RouteHandler getRouteHandler() { result = fh } + } + + /** + * An invocation of the `redirect` method of an HTTP response object. + */ + private class RedirectInvocation extends Http::RedirectInvocation, DataFlow::MethodCallNode { + RouteHandler rh; + + RedirectInvocation() { this = rh.getAResponseSource().ref().getAMethodCall("redirect") } + + override DataFlow::Node getUrlArgument() { + this.getNumArgument() = 3 and + result = this.getArgument(1) + or + this.getNumArgument() = 2 and + this.getArgument(0) + .getALocalSource() + .(DataFlow::ObjectLiteralNode) + .hasPropertyWrite("hostname", result) + or + this.getNumArgument() = 2 and + result = this.getArgument(0) + } + + override RouteHandler getRouteHandler() { result = rh } + } + + /** + * A function that looks like a Restify route handler. + * + * For example, this could be the function `function(req, res, next){...}`. + */ + class RouteHandlerCandidate extends Http::RouteHandlerCandidate { + RouteHandlerCandidate() { + // heuristic: parameter names match the Restify documentation + astNode.getNumParameter() = [2, 3] and + astNode.getParameter(0).getName() = ["request", "req"] and + astNode.getParameter(1).getName() = ["response", "res"] and + not astNode.getParameter(2).getName() != "next" and + // heuristic: is not invoked (Restify invokes this at a call site we cannot reason precisely about) + not exists(DataFlow::InvokeNode cs | cs.getACallee() = astNode) + } + } + + /** + * The URL of a REstify client, viewed as a sink for request forgery. + */ + class RequestForgerySink extends RFC::RequestForgery::Sink { + RequestForgerySink() { + exists(DataFlow::Node arg | + DataFlow::moduleMember("restify-clients", + ["createClient", "createJsonClient", "createStringClient"]).getACall().getArgument(0) = + arg and + ( + arg.getALocalSource().(DataFlow::ObjectLiteralNode).hasPropertyWrite("url", this) + or + not arg.getALocalSource() instanceof DataFlow::ObjectLiteralNode and + this = arg + ) + ) + } + + override DataFlow::Node getARequest() { + // returning the createClient argument itself since there is no request associated to the client yet. + // `getARequest()` is only used for display purposes + result = this + } + + override string getKind() { result = "host" } + } + + /** + * A header produced by a formatter + */ + private class FormatterContentTypeHeader extends Http::ImplicitHeaderDefinition, + DataFlow::FunctionNode instanceof FormatterHandler { + string contentType; + + FormatterContentTypeHeader() { + exists(DataFlow::PropWrite write | + write.getRhs() = this and + write.getPropertyName() = contentType + ) + } + + override predicate defines(string headerName, string headerValue) { + headerName = "content-type" and headerValue = contentType + } + + override Http::RouteHandler getRouteHandler() { result = this } + } + + /** + * A header produced by a route handler with no explicit declaration of a Content-Type. + */ + private class ContentTypeRouteHandlerHeader extends Http::ImplicitHeaderDefinition, + DataFlow::FunctionNode { + ContentTypeRouteHandlerHeader() { this instanceof RouteHandler } + + override predicate defines(string headerName, string headerValue) { + headerName = "content-type" and headerValue = "application/json" + } + + override Http::RouteHandler getRouteHandler() { result = this } + } + + /** A Restify router */ + private class RouterRange extends Routing::Router::Range { + ServerDefinition def; + + RouterRange() { this = def } + + override DataFlow::SourceNode getAReference() { result = def.ref() } + } + + private class RoutingTreeSetup extends Routing::RouteSetup::MethodCall { + RoutingTreeSetup() { this instanceof RouteSetup } + + override string getRelativePath() { + not this.getMethodName() = ["use", "pre", "param", "on"] and // do not treat parameter name as a path + result = this.getArgument(0).getStringValue() + } + + override Http::RequestMethodName getHttpMethod() { result.toLowerCase() = this.getMethodName() } + } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll b/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll new file mode 100644 index 00000000000..35d168a3e10 --- /dev/null +++ b/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll @@ -0,0 +1,440 @@ +/** + * Provides classes for working with [Spife](https://github.com/npm/spife) applications. + */ + +import javascript +import semmle.javascript.frameworks.HTTP + +/** + * Provides classes for working with [Spife](https://github.com/npm/spife) applications. + */ +module Spife { + /** + * An API graph entry point ensuring all tagged template exprs are part of the API graph + */ + private class TaggedTemplateEntryPoint extends API::EntryPoint { + TaggedTemplateEntryPoint() { this = "TaggedTemplateEntryPoint" } + + override DataFlow::SourceNode getASource() { result.asExpr() instanceof TaggedTemplateExpr } + } + + /** + * A call to a Spife method that sets up a route. + */ + private class RouteSetup extends API::CallNode, Http::Servers::StandardRouteSetup { + TaggedTemplateExpr template; + + RouteSetup() { + exists(CallExpr templateCall | + this.getCalleeNode().asExpr() = template and + API::moduleImport(["@npm/spife/routing", "spife/routing"]) + .asSource() + .flowsToExpr(template.getTag()) and + templateCall.getAChild() = template + ) + } + + private string getRoutePattern() { + // Concatenate the constant parts of the expression + result = + concat(Expr e, int i | + e = template.getTemplate().getElement(i) and exists(e.getStringValue()) + | + e.getStringValue() order by i + ) + } + + private string getARouteLine() { + result = this.getRoutePattern().splitAt("\n").regexpReplaceAll(" +", " ").trim() + } + + private predicate hasLine(string method, string path, string handlerName) { + exists(string line | line = this.getARouteLine() | + line.splitAt(" ", 0) = method and + line.splitAt(" ", 1) = path and + line.splitAt(" ", 2) = handlerName + ) + } + + API::Node getHandlerByName(string name) { result = this.getParameter(0).getMember(name) } + + API::Node getHandlerByRoute(string method, string path) { + exists(string handlerName | + this.hasLine(method, path, handlerName) and + result = this.getHandlerByName(handlerName) + ) + } + + override DataFlow::SourceNode getARouteHandler() { + result = this.getHandlerByRoute(_, _).getAValueReachingSink().(DataFlow::FunctionNode) + or + exists(DataFlow::MethodCallNode validation | + validation = this.getHandlerByRoute(_, _).getAValueReachingSink() and + result = validation.getArgument(1).getAFunctionValue() + ) + } + + override DataFlow::Node getServer() { none() } + } + + /** + * A Spife route handler. + */ + abstract class RouteHandler extends Http::Servers::StandardRouteHandler, DataFlow::FunctionNode { + /** + * Gets the parameter of the route handler that contains the request object. + */ + DataFlow::ParameterNode getRequestParameter() { result = this.getParameter(0) } + + /** + * Gets the parameter of the route handler that contains the context object. + */ + DataFlow::ParameterNode getContextParameter() { result = this.getParameter(1) } + } + + /** + * A standard Spife route handler. + */ + private class StandardRouteHandler extends RouteHandler, DataFlow::FunctionNode { + StandardRouteHandler() { any(RouteSetup setup).getARouteHandler() = this } + } + + /** + * A function that looks like a Spife route handler. + * + * For example, this could be the function `function(req, res, next){...}`. + */ + class RouteHandlerCandidate extends Http::RouteHandlerCandidate { + RouteHandlerCandidate() { + // heuristic: parameter names match the Restify documentation + astNode.getNumParameter() = 2 and + astNode.getParameter(0).getName() = ["request", "req"] and + astNode.getParameter(1).getName() = ["context", "ctx"] + } + } + + /** + * A Spife request source, that is, the request parameter of a + * route handler. + */ + private class RequestSource extends Http::Servers::RequestSource { + RouteHandler rh; + + RequestSource() { this = rh.getRequestParameter() } + + /** + * Gets the route handler that handles this request. + */ + override RouteHandler getRouteHandler() { result = rh } + } + + /** + * A Spife context source, that is, the context the parameter of a + * route handler. + */ + private class ContextSource extends Http::Servers::RequestSource { + RouteHandler rh; + + ContextSource() { this = rh.getContextParameter() } + + /** + * Gets the route handler that handles this request. + */ + override RouteHandler getRouteHandler() { result = rh } + } + + /** + * An access to a user-controlled Spife request input. + */ + private class RequestInputAccess extends Http::RequestInputAccess { + RouteHandler rh; + string kind; + + RequestInputAccess() { + this = rh.getARequestSource().ref().getAPropertyRead("body") and + kind = "body" + or + this = rh.getARequestSource().ref().getAPropertyRead("query").getAPropertyRead() and + kind = "parameter" + or + this = rh.getARequestSource().ref().getAPropertyRead("raw") and + kind = "raw" + or + this = rh.getARequestSource().ref().getAPropertyRead(["url", "urlObject"]) and + kind = "url" + or + this = rh.getARequestSource().ref().getAMethodCall() and + this.(DataFlow::MethodCallNode).getMethodName() = ["cookie", "cookies"] and + kind = "cookie" + or + exists(DataFlow::PropRead validated, DataFlow::MethodCallNode get | + rh.getARequestSource().ref().getAPropertyRead() = validated and + validated.getPropertyName().matches("validated%") and + get.getReceiver() = validated and + this = get and + kind = "body" + ) + } + + override RouteHandler getRouteHandler() { result = rh } + + override string getKind() { result = kind } + } + + /** + * An access to a user-controlled Spife context input. + */ + private class ContextInputAccess extends Http::RequestInputAccess { + ContextSource request; + string kind; + + ContextInputAccess() { + request.ref().flowsTo(this.(DataFlow::MethodCallNode).getReceiver()) and + this.(DataFlow::MethodCallNode).getMethodName() = "get" and + kind = "path" + } + + override RouteHandler getRouteHandler() { result = request.getRouteHandler() } + + override string getKind() { result = kind } + } + + /** + * An access to a header on a Spife request. + */ + private class RequestHeaderAccess extends Http::RequestHeaderAccess { + RouteHandler rh; + + RequestHeaderAccess() { + this = + rh.getARequestSource().ref().getAPropertyRead(["headers", "rawHeaders"]).getAPropertyRead() + } + + override string getAHeaderName() { + result = this.(DataFlow::PropRead).getPropertyName().toLowerCase() + } + + override RouteHandler getRouteHandler() { result = rh } + + override string getKind() { result = "header" } + } + + /** + * A Spife response source, that is, the response variable used by a + * route handler. + */ + private class ReplySource extends Http::Servers::ResponseSource { + ReplySource() { + // const reply = require("@npm/spife/reply") + // reply(resp) + // reply.header(resp, 'foo', 'bar') + this = API::moduleImport(["@npm/spife/reply", "spife/reply"]).getACall() or + this = API::moduleImport(["@npm/spife/reply", "spife/reply"]).getAMember().getACall() + } + + private DataFlow::SourceNode reachesHandlerReturn( + DataFlow::CallNode headerCall, DataFlow::TypeTracker t + ) { + result = headerCall and + t.start() + or + exists(DataFlow::TypeTracker t2 | + result = this.reachesHandlerReturn(headerCall, t2).track(t2, t) + ) + } + + /** + * Gets the route handler that provides this response. + */ + override RouteHandler getRouteHandler() { + exists(RouteHandler handler | + handler.(DataFlow::FunctionNode).getAReturn().getALocalSource() = + this.reachesHandlerReturn(this, DataFlow::TypeTracker::end()) and + result = handler + ) + } + } + + /** + * An HTTP header defined in a Spife response. + */ + private class HeaderDefinition extends Http::ExplicitHeaderDefinition, DataFlow::MethodCallNode { + ReplySource reply; + + HeaderDefinition() { + // reply.header(RESPONSE, 'Cache-Control', 'no-cache') + reply.ref().(DataFlow::MethodCallNode).getMethodName() = "header" and + reply.ref().(DataFlow::MethodCallNode).getNumArgument() = 3 and + this = reply + } + + override predicate definesHeaderValue(string headerName, DataFlow::Node headerValue) { + // reply.header(RESPONSE, 'Cache-Control', 'no-cache') + headerName = this.getNameNode().getStringValue() and + headerValue = this.getArgument(2) + } + + override DataFlow::Node getNameNode() { result = this.getArgument(1) } + + override RouteHandler getRouteHandler() { result = reply.getRouteHandler() } + } + + /** + * An invocation that sets any number of headers of the HTTP response. + */ + private class MultipleHeaderDefinitions extends Http::ExplicitHeaderDefinition, DataFlow::CallNode { + ReplySource reply; + + MultipleHeaderDefinitions() { + // reply.header(RESPONSE, {'Cache-Control': 'no-cache'}) + // reply(RESPONSE, {'Cache-Control': 'no-cache'}) + reply.ref().(DataFlow::CallNode).getCalleeName() = ["header", "reply"] and + reply.ref().(DataFlow::CallNode).getAnArgument().getALocalSource() instanceof + DataFlow::ObjectLiteralNode and + this = reply + } + + /** + * Gets a reference to the multiple headers object that is to be set. + */ + private DataFlow::SourceNode getAHeaderSource() { + exists(int i | + this.getArgument(i).getALocalSource() instanceof DataFlow::ObjectLiteralNode and + result.flowsTo(this.getArgument(i)) + ) + } + + override predicate definesHeaderValue(string headerName, DataFlow::Node headerValue) { + exists(string header | + this.getAHeaderSource().hasPropertyWrite(header, headerValue) and + headerName = header.toLowerCase() + ) + } + + override DataFlow::Node getNameNode() { + exists(DataFlow::PropWrite write | this.getAHeaderSource().getAPropertyWrite() = write | + result = write.getPropertyNameExpr().flow() + ) + } + + override RouteHandler getRouteHandler() { result = reply.getRouteHandler() } + } + + /** + * A header produced by a route handler with no explicit declaration of a Content-Type. + */ + private class ContentTypeRouteHandlerHeader extends Http::ImplicitHeaderDefinition, + DataFlow::FunctionNode { + ContentTypeRouteHandlerHeader() { this instanceof RouteHandler } + + override predicate defines(string headerName, string headerValue) { + headerName = "content-type" and headerValue = "application/json" + } + + override Http::RouteHandler getRouteHandler() { result = this } + } + + /** + * An HTTP cookie defined in a Spife HTTP response. + */ + private class CookieDefinition extends Http::CookieDefinition, DataFlow::MethodCallNode { + ReplySource reply; + + CookieDefinition() { + // reply.cookie(RESPONSE, 'TEST', 'FOO', {"maxAge": 1000, "httpOnly": true, "secure": true}) + this = reply.ref().(DataFlow::MethodCallNode) and + this.getMethodName() = "cookie" + } + + override DataFlow::Node getNameArgument() { result = this.getArgument(1) } + + override DataFlow::Node getValueArgument() { result = this.getArgument(2) } + + override RouteHandler getRouteHandler() { result = reply.getRouteHandler() } + } + + /** + * A response argument passed to the `reply` method. + */ + private class ReplyArgument extends Http::ResponseSendArgument, DataFlow::Node { + RouteHandler rh; + + ReplyArgument() { + exists(ReplySource reply | + reply.ref().(DataFlow::CallNode).getCalleeName() = + ["reply", "cookie", "link", "header", "headers", "raw", "status", "toStream", "vary"] and + this = reply.ref().(DataFlow::CallNode).getArgument(0) and + rh = reply.getRouteHandler() + ) + or + this = rh.(DataFlow::FunctionNode).getAReturn() + } + + override RouteHandler getRouteHandler() { result = rh } + } + + /** + * An expression passed to the `template` method of the reply object + * as the value of a template variable. + */ + private class TemplateInput extends Http::ResponseBody { + TemplateObjectInput obj; + + TemplateInput() { + obj.getALocalSource().(DataFlow::ObjectLiteralNode).hasPropertyWrite(_, this) + } + + override RouteHandler getRouteHandler() { result = obj.getRouteHandler() } + } + + /** + * An object passed to the `template` method of the reply object. + */ + private class TemplateObjectInput extends DataFlow::Node { + ReplySource reply; + + TemplateObjectInput() { + reply.ref().(DataFlow::MethodCallNode).getMethodName() = "template" and + this = reply.ref().(DataFlow::MethodCallNode).getArgument(1) + } + + /** + * Gets the route handler that uses this object. + */ + RouteHandler getRouteHandler() { result = reply.getRouteHandler() } + } + + /** + * An invocation of the `redirect` method of an HTTP response object. + */ + private class RedirectInvocation extends Http::RedirectInvocation, DataFlow::MethodCallNode { + ReplySource reply; + + RedirectInvocation() { + this = reply.ref().(DataFlow::MethodCallNode) and + this.getMethodName() = "redirect" + } + + override DataFlow::Node getUrlArgument() { result = this.getAnArgument() } + + override RouteHandler getRouteHandler() { result = reply.getRouteHandler() } + } + + /** + * A call to `reply.template('template', { ... })`, seen as a template instantiation. + */ + private class TemplateCall extends Templating::TemplateInstantiation::Range, DataFlow::CallNode { + TemplateCall() { + exists(ReplySource reply | + reply.ref().(DataFlow::MethodCallNode).getMethodName() = "template" and + this = reply.ref() + ) + } + + override DataFlow::SourceNode getOutput() { result = this } + + override DataFlow::Node getTemplateFileNode() { result = this.getArgument(0) } + + override DataFlow::Node getTemplateParamsNode() { result = this.getArgument(1) } + } +} diff --git a/javascript/ql/lib/semmle/javascript/heuristics/AdditionalRouteHandlers.qll b/javascript/ql/lib/semmle/javascript/heuristics/AdditionalRouteHandlers.qll index 76a8aff557b..36467c8e966 100644 --- a/javascript/ql/lib/semmle/javascript/heuristics/AdditionalRouteHandlers.qll +++ b/javascript/ql/lib/semmle/javascript/heuristics/AdditionalRouteHandlers.qll @@ -45,3 +45,19 @@ private class PromotedConnectCandidate extends Connect::RouteHandler, result = ConnectExpressShared::getRouteHandlerParameter(this, kind) } } + +/** + * Add `Restify::RouteHandlerCandidate` to the extent of `Restify::RouteHandler`. + */ +private class PromotedRestifyCandidate extends Restify::RouteHandler, + Http::Servers::StandardRouteHandler { + PromotedRestifyCandidate() { this instanceof Restify::RouteHandlerCandidate } +} + +/** + * Add `Spife::RouteHandlerCandidate` to the extent of `Spife::RouteHandler`. + */ +private class PromotedSpifeCandidate extends Spife::RouteHandler, + Http::Servers::StandardRouteHandler { + PromotedSpifeCandidate() { this instanceof Spife::RouteHandlerCandidate } +} diff --git a/javascript/ql/test/library-tests/frameworks/HTTP/RemoteRequestInput.expected b/javascript/ql/test/library-tests/frameworks/HTTP/RemoteRequestInput.expected index c3bd4a86afe..e6c285607c4 100644 --- a/javascript/ql/test/library-tests/frameworks/HTTP/RemoteRequestInput.expected +++ b/javascript/ql/test/library-tests/frameworks/HTTP/RemoteRequestInput.expected @@ -35,4 +35,3 @@ | restify.js:9:5:9:17 | req.trailer() | header | | restify.js:10:5:10:16 | req.header() | header | | restify.js:11:5:11:11 | req.url | url | -| restify.js:12:5:12:15 | req.cookies | cookie | diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/tests.expected b/javascript/ql/test/library-tests/frameworks/NodeJSLib/tests.expected index 7d2d6abfddb..78c2c35309f 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/tests.expected @@ -299,11 +299,13 @@ test_RemoteFlowSources | src/http.js:30:28:30:32 | chunk | | src/http.js:40:23:40:30 | authInfo | | src/http.js:45:23:45:27 | error | +| src/http.js:63:17:63:33 | req.query.myParam | | src/http.js:73:18:73:22 | chunk | | src/http.js:82:18:82:22 | chunk | | src/https.js:6:26:6:32 | req.url | | src/https.js:8:3:8:20 | req.headers.cookie | | src/https.js:9:3:9:17 | req.headers.foo | +| src/indirect2.js:10:12:10:25 | req.params.key | | src/indirect.js:17:28:17:34 | req.url | test_RouteHandler | createServer.js:2:20:2:41 | functio ... res) {} | createServer.js:2:1:2:42 | https.c ... es) {}) | diff --git a/javascript/ql/test/library-tests/frameworks/Restify2/src/index.js b/javascript/ql/test/library-tests/frameworks/Restify2/src/index.js new file mode 100644 index 00000000000..94c678c93b7 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Restify2/src/index.js @@ -0,0 +1,207 @@ +var restify = require('restify'); +const restifyPlugins = require('restify-plugins'); +var clients = require('restify-clients'); + +const opts = { + formatters: { + 'text/plain': function(req, res, body) { // test: formatter + if (body instanceof Error) { + return '' + body.message + ''; // test: stackTraceExposureSink + } else { + return '' + body + req.params.name + ''; // test: source, stackTraceExposureSink, !xssSink, !xss + } + } + } +} +const _server = restify.createServer(opts) + +const server = restify.createServer({ + formatters: { + 'text/html': function(req, res, body) { // test: formatter + if (body instanceof Error) { + return '' + body.message + ''; // test: stackTraceExposureSink, xssSink + } else { + return '' + body + req.params.name + ''; // test: source, stackTraceExposureSink, xssSink, xss + } + } + } +}); + +// The pre handler chain is executed before routing. That means these handlers will execute for an incoming request even if it’s for a route that you did not register. +server.pre(restify.plugins.pre.dedupeSlashes()); +server.pre(function(req, res, next) { // test: handler + return next(); +}); + +// The use handler chains is executed after a route has been chosen to service the request. +server.use(restifyPlugins.jsonBodyParser({ mapParams: true })); // TODO: prototype pollution? +server.use(restifyPlugins.acceptParser(server.acceptable)); +server.use(restifyPlugins.queryParser({ mapParams: true })); // TODO: prototype pollution? +server.use(restifyPlugins.fullResponse()); +server.use(function(req, res, next) { // test: handler + return next(); +}); +function filter(req, res, next) { // test: handler + return next(); +} +function filter1(req, res, next) { // test: handler + return next(); +} +function filter2(req, res, next) { // test: handler + return next(); +} +function filter3(req, res, next) { // test: handler + return next(); +} +function filter4(req, res, next) { // test: handler + return next(); +} +function filter5(req, res, next) { // test: handler + return next(); +} +function filter6(req, res, next) { // test: handler + return next(); +} +const handlers = [filter5, filter6]; +server.use(filter); // test: setup +server.use(filter1, filter2); // test: setup +server.use([filter3, filter4]); // test: setup +server.use(handlers); // setup + +function respond(req, res, next) { // test: handler + res.send('hello ' + req.params.name); // test: source, stackTraceExposureSink + res.send('hello ' + req.params["name"]); // test: source, stackTraceExposureSink + res.send('hello ' + req.query.name); // test: source, stackTraceExposureSink + res.send('hello ' + req.params[0]); // test: source, stackTraceExposureSink + + res.redirect({ + hostname: req.params.name, // test: source, redirectSink + pathname: '/bar', + port: 80, + secure: true, + permanent: true, + query: { + a: 1 + } + }, next); + res.redirect(301, req.params.name, next); // test: source, redirectSink + res.redirect(req.params.name, next); // test: source, redirectSink + next(); +} +server.get('/hello/:name', respond); // test: setup +server.head('/hello/:name', respond); // test: setup +server.get('/', function(req, res, next) { // test: setup, handler + res.send('home') + return next(); +}); + +server.get('/foo', // test: setup + function(req, res, next) { // test: handler + req.someData = req.params.name; // test: source + return next(); + }, + function(req, res, next) { // test: handler + res.header("Content-Type", "text/html"); + res.send(req.someData); // test: stackTraceExposureSink, xssSink, xss + return next(); + } +); + +server.get('/foo2', // test: setup + [function(req, res, next) { // test: handler + req.someData = 'foo'; + return next(); + }, + function(req, res, next) { // test: handler + res.send(req.someData); // test: stackTraceExposureSink + return next(); + }] +); + +function xss(req, res, next) { // test: handler + res.header("Content-Type", "text/html"); + res.send('hello ' + req.query.name); // test: source, stackTraceExposureSink, xssSink, xss + next(); +} +server["get"]('/xss', xss); // test: setup + +function xss2(req, res, next) { // test: handler + var body = req.params.name; // test: source + res.writeHead(200, { + 'Content-Length': Buffer.byteLength(body), + 'Content-Type': 'text/html' + }); + res.write(body); // test: stackTraceExposureSink, xssSink, xss + res.end(); + next(); +} +["get", "head"].forEach(method => { + server[method]('/xss2', xss2); +}); + +function xss3(req, res, next) { // test: handler + res.header("Content-Type", "text/html"); + res.send('hello ' + req.header("foo")); // test: source, stackTraceExposureSink, xssSink, !xss + next(); +} +server["get"]('/xss3', xss3); // test: setup + + +function sendV2(req, res, next) { // test: candidateHandler + res.set({ + "Content-Type": "text/html", + "access-control-allow-origin": "*", // test: corsMiconfigurationSink + "access-control-allow-headers": "Content-Type, Authorization, Content-Length, X-Requested-With", + "access-control-allow-methods": "GET, POST, PUT, DELETE, OPTIONS", + "access-control-allow-credentials": "true" + }) + res.send('hello ' + req.params.name); // test: source, stackTraceExposureSink, xssSink, xss + clients.createJsonClient({ + url: req.params.uri, // test: source, ssrfSink + }); + clients.createJsonClient(req.params.uri); // test: source, ssrfSink + + next(); +} +server.get('/hello2/:name', restify.plugins.conditionalHandler([ // test: setup + { version: ['2.0.0', '2.1.0', '2.2.0'], handler: sendV2 } +])); + +server.get('/version/test', restify.plugins.conditionalHandler([ //test: setup + { + version: ['2.0.0', '2.1.0', '2.2.0'], + handler: function(req, res, next) { // test: candidateHandler + res.send(200, { + requestedVersion: req.version(), + matchedVersion: req.matchedVersion() + }); + return next(); + } + } +])); + +server.on('InternalServer', function(req, res, err, callback) { // test: setup, handler + return callback(); +}); + +server.on('restifyError', function(req, res, err, callback) { // test: setup, handler + return callback(); +}); +server.on('after', function(req, res, route, error) { // test: setup, handler +}); +server.on('pre', function(req, res) { // test: setup, handler +}); +server.on('routed', function(req, res, route) { // test: setup, handler + res.header("Content-Type", "text/plain") + res.send(req.params.foo) // test: source, !xssSink, !xss +}); +server.on('uncaughtException', function(req, res, route, err) { // test: setup, handler + res.header("Content-Type", "text/html") + res.send(req.params.foo) // test: source, xssSink, xss +}); + + +server.listen(8080, function() { + console.log('%s listening at %s', server.name, server.url); +}); + diff --git a/javascript/ql/test/library-tests/frameworks/Restify2/tests.expected b/javascript/ql/test/library-tests/frameworks/Restify2/tests.expected new file mode 100644 index 00000000000..1c9c7a2b45c --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Restify2/tests.expected @@ -0,0 +1,105 @@ +passingPositiveTests +| PASSED | candidateHandler | src/index.js:150:35:150:59 | // test ... Handler | +| PASSED | candidateHandler | src/index.js:173:42:173:66 | // test ... Handler | +| PASSED | corsMiconfigurationSink | src/index.js:153:41:153:72 | // test ... ionSink | +| PASSED | handler | src/index.js:32:39:32:54 | // test: handler | +| PASSED | handler | src/index.js:41:39:41:54 | // test: handler | +| PASSED | handler | src/index.js:44:35:44:50 | // test: handler | +| PASSED | handler | src/index.js:47:36:47:51 | // test: handler | +| PASSED | handler | src/index.js:50:36:50:51 | // test: handler | +| PASSED | handler | src/index.js:53:36:53:51 | // test: handler | +| PASSED | handler | src/index.js:56:36:56:51 | // test: handler | +| PASSED | handler | src/index.js:59:36:59:51 | // test: handler | +| PASSED | handler | src/index.js:62:36:62:51 | // test: handler | +| PASSED | handler | src/index.js:71:36:71:51 | // test: handler | +| PASSED | handler | src/index.js:93:44:93:66 | // test ... handler | +| PASSED | handler | src/index.js:99:30:99:45 | // test: handler | +| PASSED | handler | src/index.js:103:30:103:45 | // test: handler | +| PASSED | handler | src/index.js:111:31:111:46 | // test: handler | +| PASSED | handler | src/index.js:115:30:115:45 | // test: handler | +| PASSED | handler | src/index.js:121:32:121:47 | // test: handler | +| PASSED | handler | src/index.js:128:33:128:48 | // test: handler | +| PASSED | handler | src/index.js:142:33:142:48 | // test: handler | +| PASSED | handler | src/index.js:183:65:183:87 | // test ... handler | +| PASSED | handler | src/index.js:187:63:187:85 | // test ... handler | +| PASSED | handler | src/index.js:190:55:190:77 | // test ... handler | +| PASSED | handler | src/index.js:192:39:192:61 | // test ... handler | +| PASSED | handler | src/index.js:194:49:194:71 | // test ... handler | +| PASSED | handler | src/index.js:198:65:198:87 | // test ... handler | +| PASSED | redirectSink | src/index.js:78:32:78:60 | // test ... ectSink | +| PASSED | redirectSink | src/index.js:87:45:87:73 | // test ... ectSink | +| PASSED | redirectSink | src/index.js:88:40:88:68 | // test ... ectSink | +| PASSED | setup | src/index.js:66:21:66:34 | // test: setup | +| PASSED | setup | src/index.js:67:31:67:44 | // test: setup | +| PASSED | setup | src/index.js:68:33:68:46 | // test: setup | +| PASSED | setup | src/index.js:91:38:91:51 | // test: setup | +| PASSED | setup | src/index.js:92:39:92:52 | // test: setup | +| PASSED | setup | src/index.js:93:44:93:66 | // test ... handler | +| PASSED | setup | src/index.js:98:20:98:33 | // test: setup | +| PASSED | setup | src/index.js:110:21:110:34 | // test: setup | +| PASSED | setup | src/index.js:126:29:126:42 | // test: setup | +| PASSED | setup | src/index.js:147:31:147:44 | // test: setup | +| PASSED | setup | src/index.js:166:66:166:79 | // test: setup | +| PASSED | setup | src/index.js:170:66:170:78 | //test: setup | +| PASSED | setup | src/index.js:183:65:183:87 | // test ... handler | +| PASSED | setup | src/index.js:187:63:187:85 | // test ... handler | +| PASSED | setup | src/index.js:190:55:190:77 | // test ... handler | +| PASSED | setup | src/index.js:192:39:192:61 | // test ... handler | +| PASSED | setup | src/index.js:194:49:194:71 | // test ... handler | +| PASSED | setup | src/index.js:198:65:198:87 | // test ... handler | +| PASSED | source | src/index.js:11:76:11:130 | // test ... k, !xss | +| PASSED | source | src/index.js:24:76:24:128 | // test ... nk, xss | +| PASSED | source | src/index.js:72:41:72:80 | // test ... reSink | +| PASSED | source | src/index.js:73:44:73:82 | // test ... ureSink | +| PASSED | source | src/index.js:74:40:74:78 | // test ... ureSink | +| PASSED | source | src/index.js:75:39:75:77 | // test ... ureSink | +| PASSED | source | src/index.js:78:32:78:60 | // test ... ectSink | +| PASSED | source | src/index.js:87:45:87:73 | // test ... ectSink | +| PASSED | source | src/index.js:88:40:88:68 | // test ... ectSink | +| PASSED | source | src/index.js:100:37:100:51 | // test: source | +| PASSED | source | src/index.js:123:40:123:92 | // test ... nk, xss | +| PASSED | source | src/index.js:129:31:129:45 | // test: source | +| PASSED | source | src/index.js:144:43:144:96 | // test ... k, !xss | +| PASSED | source | src/index.js:158:41:158:93 | // test ... nk, xss | +| PASSED | source | src/index.js:160:26:160:50 | // test ... srfSink | +| PASSED | source | src/index.js:162:45:162:69 | // test ... srfSink | +| PASSED | source | src/index.js:196:28:196:58 | // test ... k, !xss | +| PASSED | source | src/index.js:200:28:200:56 | // test ... nk, xss | +| PASSED | ssrfSink | src/index.js:160:26:160:50 | // test ... srfSink | +| PASSED | ssrfSink | src/index.js:162:45:162:69 | // test ... srfSink | +| PASSED | stackTraceExposureSink | src/index.js:9:66:9:96 | // test ... ureSink | +| PASSED | stackTraceExposureSink | src/index.js:11:76:11:130 | // test ... k, !xss | +| PASSED | stackTraceExposureSink | src/index.js:22:66:22:105 | // test ... xssSink | +| PASSED | stackTraceExposureSink | src/index.js:24:76:24:128 | // test ... nk, xss | +| PASSED | stackTraceExposureSink | src/index.js:72:41:72:80 | // test ... reSink | +| PASSED | stackTraceExposureSink | src/index.js:73:44:73:82 | // test ... ureSink | +| PASSED | stackTraceExposureSink | src/index.js:74:40:74:78 | // test ... ureSink | +| PASSED | stackTraceExposureSink | src/index.js:75:39:75:77 | // test ... ureSink | +| PASSED | stackTraceExposureSink | src/index.js:105:29:105:73 | // test ... nk, xss | +| PASSED | stackTraceExposureSink | src/index.js:116:29:116:59 | // test ... ureSink | +| PASSED | stackTraceExposureSink | src/index.js:123:40:123:92 | // test ... nk, xss | +| PASSED | stackTraceExposureSink | src/index.js:134:20:134:64 | // test ... nk, xss | +| PASSED | stackTraceExposureSink | src/index.js:144:43:144:96 | // test ... k, !xss | +| PASSED | stackTraceExposureSink | src/index.js:158:41:158:93 | // test ... nk, xss | +| PASSED | xss | src/index.js:24:76:24:128 | // test ... nk, xss | +| PASSED | xss | src/index.js:105:29:105:73 | // test ... nk, xss | +| PASSED | xss | src/index.js:123:40:123:92 | // test ... nk, xss | +| PASSED | xss | src/index.js:134:20:134:64 | // test ... nk, xss | +| PASSED | xss | src/index.js:158:41:158:93 | // test ... nk, xss | +| PASSED | xss | src/index.js:200:28:200:56 | // test ... nk, xss | +| PASSED | xssSink | src/index.js:22:66:22:105 | // test ... xssSink | +| PASSED | xssSink | src/index.js:24:76:24:128 | // test ... nk, xss | +| PASSED | xssSink | src/index.js:105:29:105:73 | // test ... nk, xss | +| PASSED | xssSink | src/index.js:123:40:123:92 | // test ... nk, xss | +| PASSED | xssSink | src/index.js:134:20:134:64 | // test ... nk, xss | +| PASSED | xssSink | src/index.js:144:43:144:96 | // test ... k, !xss | +| PASSED | xssSink | src/index.js:158:41:158:93 | // test ... nk, xss | +| PASSED | xssSink | src/index.js:200:28:200:56 | // test ... nk, xss | +failingPositiveTests +passingNegativeTests +| PASSED | !xss | src/index.js:11:76:11:130 | // test ... k, !xss | +| PASSED | !xss | src/index.js:144:43:144:96 | // test ... k, !xss | +| PASSED | !xss | src/index.js:196:28:196:58 | // test ... k, !xss | +| PASSED | !xssSink | src/index.js:11:76:11:130 | // test ... k, !xss | +| PASSED | !xssSink | src/index.js:196:28:196:58 | // test ... k, !xss | +failingNegativeTests diff --git a/javascript/ql/test/library-tests/frameworks/Restify2/tests.ql b/javascript/ql/test/library-tests/frameworks/Restify2/tests.ql new file mode 100644 index 00000000000..5666ca201ad --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Restify2/tests.ql @@ -0,0 +1,194 @@ +import javascript +import semmle.javascript.security.dataflow.CleartextStorageCustomizations +import semmle.javascript.security.dataflow.CorsMisconfigurationForCredentialsCustomizations +import semmle.javascript.security.dataflow.StackTraceExposureCustomizations +import semmle.javascript.security.dataflow.ServerSideUrlRedirectCustomizations +import semmle.javascript.security.dataflow.RequestForgeryCustomizations +import semmle.javascript.security.dataflow.ReflectedXssCustomizations +import semmle.javascript.security.dataflow.ReflectedXssQuery as XssConfig +import semmle.javascript.heuristics.AdditionalRouteHandlers + +class InlineTest extends LineComment { + string tests; + + InlineTest() { tests = this.getText().regexpCapture("\\s*test:(.*)", 1) } + + string getPositiveTest() { + result = tests.trim().splitAt(",").trim() and not result.matches("!%") + } + + string getNegativeTest() { result = tests.trim().splitAt(",").trim() and result.matches("!%") } + + predicate hasPositiveTest(string test) { test = this.getPositiveTest() } + + predicate hasNegativeTest(string test) { test = this.getNegativeTest() } + + predicate inNode(DataFlow::Node n) { + this.getLocation().getFile() = n.getFile() and + this.getLocation().getStartLine() = n.getStartLine() + } +} + +query predicate passingPositiveTests(string res, string expectation, InlineTest t) { + res = "PASSED" and + t.hasPositiveTest(expectation) and + ( + expectation = "source" and + exists(RemoteFlowSource n | t.inNode(n)) + or + expectation = "setup" and + exists(Http::RouteSetup n | t.inNode(n)) + or + expectation = "handler" and + exists(Http::RouteHandler n | t.inNode(n)) + or + expectation = "candidateHandler" and + exists(Http::RouteHandlerCandidate n | t.inNode(n)) + or + expectation = "xssSink" and + exists(ReflectedXss::Sink n | t.inNode(n)) + or + expectation = "xss" and + exists(XssConfig::Configuration cfg, DataFlow::Node sink | + cfg.hasFlow(_, sink) and t.inNode(sink) + ) + or + expectation = "cleartextStorageSink" and + exists(CleartextStorage::Sink n | t.inNode(n)) + or + expectation = "corsMiconfigurationSink" and + exists(CorsMisconfigurationForCredentials::Sink n | t.inNode(n)) + or + expectation = "stackTraceExposureSink" and + exists(StackTraceExposure::Sink n | t.inNode(n)) + or + expectation = "redirectSink" and + exists(ServerSideUrlRedirect::Sink n | t.inNode(n)) + or + expectation = "ssrfSink" and + exists(RequestForgery::Sink n | t.inNode(n)) + ) +} + +query predicate failingPositiveTests(string res, string expectation, InlineTest t) { + res = "FAILED" and + t.hasPositiveTest(expectation) and + ( + expectation = "source" and + not exists(RemoteFlowSource n | t.inNode(n)) + or + expectation = "setup" and + not exists(Http::RouteSetup n | t.inNode(n)) + or + expectation = "handler" and + not exists(Http::RouteHandler n | t.inNode(n)) + or + expectation = "candidateHandler" and + not exists(Http::RouteHandlerCandidate n | t.inNode(n)) + or + expectation = "xssSink" and + not exists(ReflectedXss::Sink n | t.inNode(n)) + or + expectation = "xss" and + not exists(XssConfig::Configuration cfg, DataFlow::Node sink | + cfg.hasFlow(_, sink) and t.inNode(sink) + ) + or + expectation = "cleartextStorageSink" and + not exists(CleartextStorage::Sink n | t.inNode(n)) + or + expectation = "corsMiconfigurationSink" and + not exists(CorsMisconfigurationForCredentials::Sink n | t.inNode(n)) + or + expectation = "stackTraceExposureSink" and + not exists(StackTraceExposure::Sink n | t.inNode(n)) + or + expectation = "redirectSink" and + not exists(ServerSideUrlRedirect::Sink n | t.inNode(n)) + or + expectation = "ssrfSink" and + not exists(RequestForgery::Sink n | t.inNode(n)) + ) +} + +query predicate passingNegativeTests(string res, string expectation, InlineTest t) { + res = "PASSED" and + t.hasNegativeTest(expectation) and + ( + expectation = "!source" and + not exists(RemoteFlowSource n | t.inNode(n)) + or + expectation = "!setup" and + not exists(Http::RouteSetup n | t.inNode(n)) + or + expectation = "!handler" and + not exists(Http::RouteHandler n | t.inNode(n)) + or + expectation = "!candidateHandler" and + not exists(Http::RouteHandlerCandidate n | t.inNode(n)) + or + expectation = "!xssSink" and + not exists(ReflectedXss::Sink n | t.inNode(n)) + or + expectation = "!xss" and + not exists(XssConfig::Configuration cfg, DataFlow::Node sink | + cfg.hasFlow(_, sink) and t.inNode(sink) + ) + or + expectation = "!cleartextStorageSink" and + not exists(CleartextStorage::Sink n | t.inNode(n)) + or + expectation = "!corsMiconfigurationSink" and + not exists(CorsMisconfigurationForCredentials::Sink n | t.inNode(n)) + or + expectation = "!stackTraceExposureSink" and + not exists(StackTraceExposure::Sink n | t.inNode(n)) + or + expectation = "!redirectSink" and + not exists(ServerSideUrlRedirect::Sink n | t.inNode(n)) + or + expectation = "!ssrfSink" and + not exists(RequestForgery::Sink n | t.inNode(n)) + ) +} + +query predicate failingNegativeTests(string res, string expectation, InlineTest t) { + res = "FAILED" and + t.hasNegativeTest(expectation) and + ( + expectation = "!source" and + exists(RemoteFlowSource n | t.inNode(n)) + or + expectation = "!setup" and + exists(Http::RouteSetup n | t.inNode(n)) + or + expectation = "!handler" and + exists(Http::RouteHandler n | t.inNode(n)) + or + expectation = "!candidateHandler" and + exists(Http::RouteHandlerCandidate n | t.inNode(n)) + or + expectation = "!xssSink" and + exists(ReflectedXss::Sink n | t.inNode(n)) + or + expectation = "!xss" and + exists(XssConfig::Configuration cfg, DataFlow::Node sink | + cfg.hasFlow(_, sink) and t.inNode(sink) + ) + or + expectation = "!cleartextStorageSink" and + exists(CleartextStorage::Sink n | t.inNode(n)) + or + expectation = "!corsMiconfigurationSink" and + exists(CorsMisconfigurationForCredentials::Sink n | t.inNode(n)) + or + expectation = "!stackTraceExposureSink" and + exists(StackTraceExposure::Sink n | t.inNode(n)) + or + expectation = "!redirectSink" and + exists(ServerSideUrlRedirect::Sink n | t.inNode(n)) + or + expectation = "!ssrfSink" and + exists(RequestForgery::Sink n | t.inNode(n)) + ) +} diff --git a/javascript/ql/test/library-tests/frameworks/Spife/lib/routes/index.js b/javascript/ql/test/library-tests/frameworks/Spife/lib/routes/index.js new file mode 100644 index 00000000000..1598d11f035 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Spife/lib/routes/index.js @@ -0,0 +1,17 @@ +'use strict' + +const routes = require('@npm/spife/routing') + +module.exports = routes` + GET / homepage + GET /test1 test1 + GET /test1 test2 + GET /test4 test4 + GET /test5 test5 + GET /test6 test6 + GET /raw1 raw1 + GET /raw2 raw2 + POST /body parseBody + GET /redirect/:redirect_url redirect + POST /packages/new createPackage +`(require('../views')) diff --git a/javascript/ql/test/library-tests/frameworks/Spife/lib/routes/parameters.js b/javascript/ql/test/library-tests/frameworks/Spife/lib/routes/parameters.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/javascript/ql/test/library-tests/frameworks/Spife/lib/settings.js b/javascript/ql/test/library-tests/frameworks/Spife/lib/settings.js new file mode 100644 index 00000000000..5d3b9824132 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Spife/lib/settings.js @@ -0,0 +1,46 @@ +'use strict' + +const { Environment, FileSystemLoader } = require('nunjucks') +const Loader = require('@npm/spife/templates/loader') +const path = require('path') + +const isDev = !new Set(['prod', 'production', 'stag', 'staging']).has( + process.env.NODE_ENV +) + +const templateDirs = [path.join(__dirname, '..', 'templates')] +const nunjucksEnv = new Environment(new FileSystemLoader(templateDirs)) +const nunjucksLoader = new Loader({ + dirs: templateDirs, + load (resolved) { + const template = nunjucksEnv.getTemplate(resolved.path, true) + return context => { + return template.render(context) + } + } +}) + +module.exports = { + DEBUG: process.env.DEBUG, + ENABLE_FORM_PARSING: false, + METRICS: process.env.METRICS, + MIDDLEWARE: [ + '@npm/spife/middleware/debug', + ['@npm/spife/middleware/template', [ + nunjucksLoader + ], [ + // template context processors go here + ]], + '@npm/spife/middleware/common', + '@npm/spife/middleware/logging', + '@npm/spife/middleware/metrics', + '@npm/spife/middleware/monitor', + '@npm/spife/middleware/hot-reload', + ['@npm/spife/middleware/csrf', { secureCookie: !isDev }] + ], + NAME: 'nunjucks-example', + NODE_ENV: process.env.NODE_ENV, + PORT: 8124, + ROUTER: './routes/index.js', + HOT: true +} diff --git a/javascript/ql/test/library-tests/frameworks/Spife/lib/views/index.js b/javascript/ql/test/library-tests/frameworks/Spife/lib/views/index.js new file mode 100644 index 00000000000..c64299853bf --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Spife/lib/views/index.js @@ -0,0 +1,115 @@ +'use strict' + +const reply = require('@npm/spife/reply') +const validate = require('@npm/spife/decorators/validate') +const joi = require('@npm/spife/joi') +const concat = require('concat-stream') + +const createPackageSchema = joi.object().keys({ + contents: joi.string().max(200).required(), + destination: joi.any().valid([ + joi.object({ + name: joi.string().max(200).required(), + address: joi.string().max(200).required() + }), + joi.string().min(1) + ]) +}) + +module.exports = { homepage, parseBody, raw1, raw2, test1, test2, test3, test4, test5, test6, redirect, createPackage: validate.body(createPackageSchema, createPackage), } + +function sink(obj) { console.log(obj) } + +function createPackage(req, context) { // test: handler + const tainted = req.validatedBody.get('destination') // test: source + sink(taitned) +} + +function homepage(req, context) { // test: handler + sink(req.cookie("test")) // test: source + sink(req.cookies().test) // test: source + sink(req.headers.test) // test: source + sink(req.rawHeaders[0]) // test: source + sink(req.raw.headers) // test: source + sink(req.url) // test: source + sink(req.urlObject.pathname) // test: source + sink(context.get('package')) // test: source + sink(context) + return reply.template('home', { target: req.query.name }) // test: source, templateInstantiation, stackTraceExposureSink +} + +function raw1(req, context) { // test: handler + sink(req.query.name) // test: source + return reply(req.query.name, 200, { // test: source, xssSink, stackTraceExposureSink, xss + "content-type": "text/html", + "access-control-allow-origin": "*", // test: corsMiconfigurationSink + "access-control-allow-headers": "Content-Type, Authorization, Content-Length, X-Requested-With", + "access-control-allow-methods": "GET, POST, PUT, DELETE, OPTIONS", + "access-control-allow-credentials": "true" + + }) +} + +function redirect(req, context) { // test: handler + return reply.redirect(context.get('redirect_url')) // test: redirectSink, source, stackTraceExposureSink +} +function raw2(req, context) { // test: handler + return reply.cookie({ "test": req.query.name }, "test", req.query.name, { "httpOnly": false, "secure": false }) // test: source, cleartextStorageSink, stackTraceExposureSink +} + +function test1(req, context) { // test: handler + switch (req.accept.type(['json', 'html', 'plain'])) { + case 'json': + return { "some": req.query.name } // test: source, stackTraceExposureSink + case 'html': + return reply.header('

' + req.query.name + '

', 'content-type', 'text/html') // test: source, xssSink, stackTraceExposureSink, xss + case 'plain': + return reply.header('

' + req.query.name + '

', { 'content-type': 'text/plain' }) // test: source, stackTraceExposureSink, !xssSink, !xss + } + return 'well, I guess you just want plaintext.' +} + +function test2(req, context) { // test: handler + switch (req.accept.type(['json', 'html'])) { + case 'json': + return { "some": req.query.name } // test: source, stackTraceExposureSink + case 'html': + return reply.header('

' + req.query.name + '

', { 'content-type': 'text/plain' }) // test: source, stackTraceExposureSink, !xssSink, !xss + } + return 'well, I guess you just want plaintext.' +} + +function test3(req, context) { // test: candidateHandler + return reply('

' + req.query.name + '

') // test: source, stackTraceExposureSink, !xssSink, !xss +} + +function test4(req, context) { // test: handler + const body = req.body // test: source + const newPackument = body['package-json'] + const message = `INFO: User invited to package ${newPackument._id} successfully.` + return reply(message, 200, { 'npm-notice': message }) // test: stackTraceExposureSink, !xssSink, !xss +} + +function test5(req, context) { // test: handler + const body = req.body // test: source + const newPackument = body['package-json'] + const message = `INFO: User invited to package ${newPackument._id} successfully.` + return reply(message, 200) // test: stackTraceExposureSink, !xssSink, !xss +} + +function test6(req, context) { // test: handler + const body = req.body // test: source + const newPackument = body['package-json'] + const message = `INFO: User invited to package ${newPackument._id} successfully.` + if (message.contains('foo')) { + return reply(message, 200, { 'npm-notice': message }) // test: stackTraceExposureSink, !xssSink, !xss + } else { + return reply(message, 200, { 'npm-notice': message, 'content-type': 'text/html' }) // test: stackTraceExposureSink, xssSink, xss + } +} + +function parseBody(req, context) { + return req.body.then(data => { // test: source, stackTraceExposureSink + sink(data.name) + }) +} diff --git a/javascript/ql/test/library-tests/frameworks/Spife/tests.expected b/javascript/ql/test/library-tests/frameworks/Spife/tests.expected new file mode 100644 index 00000000000..98c16194268 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Spife/tests.expected @@ -0,0 +1,75 @@ +passingPositiveTests +| PASSED | candidateHandler | lib/views/index.js:82:32:82:56 | // test ... Handler | +| PASSED | cleartextStorageSink | lib/views/index.js:57:115:57:175 | // test ... ureSink | +| PASSED | corsMiconfigurationSink | lib/views/index.js:45:41:45:72 | // test ... ionSink | +| PASSED | handler | lib/views/index.js:23:40:23:55 | // test: handler | +| PASSED | handler | lib/views/index.js:28:35:28:50 | // test: handler | +| PASSED | handler | lib/views/index.js:41:31:41:46 | // test: handler | +| PASSED | handler | lib/views/index.js:53:35:53:50 | // test: handler | +| PASSED | handler | lib/views/index.js:56:31:56:46 | // test: handler | +| PASSED | handler | lib/views/index.js:60:32:60:47 | // test: handler | +| PASSED | handler | lib/views/index.js:72:32:72:47 | // test: handler | +| PASSED | handler | lib/views/index.js:86:32:86:47 | // test: handler | +| PASSED | handler | lib/views/index.js:93:32:93:47 | // test: handler | +| PASSED | handler | lib/views/index.js:100:32:100:47 | // test: handler | +| PASSED | redirectSink | lib/views/index.js:54:54:54:106 | // test ... ureSink | +| PASSED | source | lib/views/index.js:24:56:24:70 | // test: source | +| PASSED | source | lib/views/index.js:29:28:29:42 | // test: source | +| PASSED | source | lib/views/index.js:30:28:30:42 | // test: source | +| PASSED | source | lib/views/index.js:31:26:31:40 | // test: source | +| PASSED | source | lib/views/index.js:32:27:32:41 | // test: source | +| PASSED | source | lib/views/index.js:33:25:33:39 | // test: source | +| PASSED | source | lib/views/index.js:34:17:34:31 | // test: source | +| PASSED | source | lib/views/index.js:35:32:35:46 | // test: source | +| PASSED | source | lib/views/index.js:36:32:36:46 | // test: source | +| PASSED | source | lib/views/index.js:38:61:38:122 | // test ... ureSink | +| PASSED | source | lib/views/index.js:42:24:42:38 | // test: source | +| PASSED | source | lib/views/index.js:43:39:43:91 | // test ... nk, xss | +| PASSED | source | lib/views/index.js:54:54:54:106 | // test ... ureSink | +| PASSED | source | lib/views/index.js:57:115:57:175 | // test ... ureSink | +| PASSED | source | lib/views/index.js:63:41:63:79 | // test ... ureSink | +| PASSED | source | lib/views/index.js:65:89:65:141 | // test ... nk, xss | +| PASSED | source | lib/views/index.js:67:94:67:148 | // test ... k, !xss | +| PASSED | source | lib/views/index.js:75:41:75:79 | // test ... ureSink | +| PASSED | source | lib/views/index.js:77:94:77:148 | // test ... k, !xss | +| PASSED | source | lib/views/index.js:83:49:83:103 | // test ... k, !xss | +| PASSED | source | lib/views/index.js:87:25:87:39 | // test: source | +| PASSED | source | lib/views/index.js:94:25:94:39 | // test: source | +| PASSED | source | lib/views/index.js:101:25:101:39 | // test: source | +| PASSED | source | lib/views/index.js:112:34:112:72 | // test ... ureSink | +| PASSED | stackTraceExposureSink | lib/views/index.js:38:61:38:122 | // test ... ureSink | +| PASSED | stackTraceExposureSink | lib/views/index.js:43:39:43:91 | // test ... nk, xss | +| PASSED | stackTraceExposureSink | lib/views/index.js:54:54:54:106 | // test ... ureSink | +| PASSED | stackTraceExposureSink | lib/views/index.js:57:115:57:175 | // test ... ureSink | +| PASSED | stackTraceExposureSink | lib/views/index.js:63:41:63:79 | // test ... ureSink | +| PASSED | stackTraceExposureSink | lib/views/index.js:65:89:65:141 | // test ... nk, xss | +| PASSED | stackTraceExposureSink | lib/views/index.js:67:94:67:148 | // test ... k, !xss | +| PASSED | stackTraceExposureSink | lib/views/index.js:75:41:75:79 | // test ... ureSink | +| PASSED | stackTraceExposureSink | lib/views/index.js:77:94:77:148 | // test ... k, !xss | +| PASSED | stackTraceExposureSink | lib/views/index.js:83:49:83:103 | // test ... k, !xss | +| PASSED | stackTraceExposureSink | lib/views/index.js:90:57:90:103 | // test ... k, !xss | +| PASSED | stackTraceExposureSink | lib/views/index.js:97:30:97:76 | // test ... k, !xss | +| PASSED | stackTraceExposureSink | lib/views/index.js:105:59:105:105 | // test ... k, !xss | +| PASSED | stackTraceExposureSink | lib/views/index.js:107:88:107:132 | // test ... nk, xss | +| PASSED | stackTraceExposureSink | lib/views/index.js:112:34:112:72 | // test ... ureSink | +| PASSED | xss | lib/views/index.js:43:39:43:91 | // test ... nk, xss | +| PASSED | xss | lib/views/index.js:65:89:65:141 | // test ... nk, xss | +| PASSED | xss | lib/views/index.js:107:88:107:132 | // test ... nk, xss | +| PASSED | xssSink | lib/views/index.js:43:39:43:91 | // test ... nk, xss | +| PASSED | xssSink | lib/views/index.js:65:89:65:141 | // test ... nk, xss | +| PASSED | xssSink | lib/views/index.js:107:88:107:132 | // test ... nk, xss | +failingPositiveTests +passingNegativeTests +| PASSED | !xss | lib/views/index.js:67:94:67:148 | // test ... k, !xss | +| PASSED | !xss | lib/views/index.js:77:94:77:148 | // test ... k, !xss | +| PASSED | !xss | lib/views/index.js:83:49:83:103 | // test ... k, !xss | +| PASSED | !xss | lib/views/index.js:97:30:97:76 | // test ... k, !xss | +| PASSED | !xssSink | lib/views/index.js:67:94:67:148 | // test ... k, !xss | +| PASSED | !xssSink | lib/views/index.js:77:94:77:148 | // test ... k, !xss | +| PASSED | !xssSink | lib/views/index.js:83:49:83:103 | // test ... k, !xss | +| PASSED | !xssSink | lib/views/index.js:97:30:97:76 | // test ... k, !xss | +failingNegativeTests +| FAILED | !xss | lib/views/index.js:90:57:90:103 | // test ... k, !xss | +| FAILED | !xss | lib/views/index.js:105:59:105:105 | // test ... k, !xss | +| FAILED | !xssSink | lib/views/index.js:90:57:90:103 | // test ... k, !xss | +| FAILED | !xssSink | lib/views/index.js:105:59:105:105 | // test ... k, !xss | diff --git a/javascript/ql/test/library-tests/frameworks/Spife/tests.ql b/javascript/ql/test/library-tests/frameworks/Spife/tests.ql new file mode 100644 index 00000000000..5666ca201ad --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Spife/tests.ql @@ -0,0 +1,194 @@ +import javascript +import semmle.javascript.security.dataflow.CleartextStorageCustomizations +import semmle.javascript.security.dataflow.CorsMisconfigurationForCredentialsCustomizations +import semmle.javascript.security.dataflow.StackTraceExposureCustomizations +import semmle.javascript.security.dataflow.ServerSideUrlRedirectCustomizations +import semmle.javascript.security.dataflow.RequestForgeryCustomizations +import semmle.javascript.security.dataflow.ReflectedXssCustomizations +import semmle.javascript.security.dataflow.ReflectedXssQuery as XssConfig +import semmle.javascript.heuristics.AdditionalRouteHandlers + +class InlineTest extends LineComment { + string tests; + + InlineTest() { tests = this.getText().regexpCapture("\\s*test:(.*)", 1) } + + string getPositiveTest() { + result = tests.trim().splitAt(",").trim() and not result.matches("!%") + } + + string getNegativeTest() { result = tests.trim().splitAt(",").trim() and result.matches("!%") } + + predicate hasPositiveTest(string test) { test = this.getPositiveTest() } + + predicate hasNegativeTest(string test) { test = this.getNegativeTest() } + + predicate inNode(DataFlow::Node n) { + this.getLocation().getFile() = n.getFile() and + this.getLocation().getStartLine() = n.getStartLine() + } +} + +query predicate passingPositiveTests(string res, string expectation, InlineTest t) { + res = "PASSED" and + t.hasPositiveTest(expectation) and + ( + expectation = "source" and + exists(RemoteFlowSource n | t.inNode(n)) + or + expectation = "setup" and + exists(Http::RouteSetup n | t.inNode(n)) + or + expectation = "handler" and + exists(Http::RouteHandler n | t.inNode(n)) + or + expectation = "candidateHandler" and + exists(Http::RouteHandlerCandidate n | t.inNode(n)) + or + expectation = "xssSink" and + exists(ReflectedXss::Sink n | t.inNode(n)) + or + expectation = "xss" and + exists(XssConfig::Configuration cfg, DataFlow::Node sink | + cfg.hasFlow(_, sink) and t.inNode(sink) + ) + or + expectation = "cleartextStorageSink" and + exists(CleartextStorage::Sink n | t.inNode(n)) + or + expectation = "corsMiconfigurationSink" and + exists(CorsMisconfigurationForCredentials::Sink n | t.inNode(n)) + or + expectation = "stackTraceExposureSink" and + exists(StackTraceExposure::Sink n | t.inNode(n)) + or + expectation = "redirectSink" and + exists(ServerSideUrlRedirect::Sink n | t.inNode(n)) + or + expectation = "ssrfSink" and + exists(RequestForgery::Sink n | t.inNode(n)) + ) +} + +query predicate failingPositiveTests(string res, string expectation, InlineTest t) { + res = "FAILED" and + t.hasPositiveTest(expectation) and + ( + expectation = "source" and + not exists(RemoteFlowSource n | t.inNode(n)) + or + expectation = "setup" and + not exists(Http::RouteSetup n | t.inNode(n)) + or + expectation = "handler" and + not exists(Http::RouteHandler n | t.inNode(n)) + or + expectation = "candidateHandler" and + not exists(Http::RouteHandlerCandidate n | t.inNode(n)) + or + expectation = "xssSink" and + not exists(ReflectedXss::Sink n | t.inNode(n)) + or + expectation = "xss" and + not exists(XssConfig::Configuration cfg, DataFlow::Node sink | + cfg.hasFlow(_, sink) and t.inNode(sink) + ) + or + expectation = "cleartextStorageSink" and + not exists(CleartextStorage::Sink n | t.inNode(n)) + or + expectation = "corsMiconfigurationSink" and + not exists(CorsMisconfigurationForCredentials::Sink n | t.inNode(n)) + or + expectation = "stackTraceExposureSink" and + not exists(StackTraceExposure::Sink n | t.inNode(n)) + or + expectation = "redirectSink" and + not exists(ServerSideUrlRedirect::Sink n | t.inNode(n)) + or + expectation = "ssrfSink" and + not exists(RequestForgery::Sink n | t.inNode(n)) + ) +} + +query predicate passingNegativeTests(string res, string expectation, InlineTest t) { + res = "PASSED" and + t.hasNegativeTest(expectation) and + ( + expectation = "!source" and + not exists(RemoteFlowSource n | t.inNode(n)) + or + expectation = "!setup" and + not exists(Http::RouteSetup n | t.inNode(n)) + or + expectation = "!handler" and + not exists(Http::RouteHandler n | t.inNode(n)) + or + expectation = "!candidateHandler" and + not exists(Http::RouteHandlerCandidate n | t.inNode(n)) + or + expectation = "!xssSink" and + not exists(ReflectedXss::Sink n | t.inNode(n)) + or + expectation = "!xss" and + not exists(XssConfig::Configuration cfg, DataFlow::Node sink | + cfg.hasFlow(_, sink) and t.inNode(sink) + ) + or + expectation = "!cleartextStorageSink" and + not exists(CleartextStorage::Sink n | t.inNode(n)) + or + expectation = "!corsMiconfigurationSink" and + not exists(CorsMisconfigurationForCredentials::Sink n | t.inNode(n)) + or + expectation = "!stackTraceExposureSink" and + not exists(StackTraceExposure::Sink n | t.inNode(n)) + or + expectation = "!redirectSink" and + not exists(ServerSideUrlRedirect::Sink n | t.inNode(n)) + or + expectation = "!ssrfSink" and + not exists(RequestForgery::Sink n | t.inNode(n)) + ) +} + +query predicate failingNegativeTests(string res, string expectation, InlineTest t) { + res = "FAILED" and + t.hasNegativeTest(expectation) and + ( + expectation = "!source" and + exists(RemoteFlowSource n | t.inNode(n)) + or + expectation = "!setup" and + exists(Http::RouteSetup n | t.inNode(n)) + or + expectation = "!handler" and + exists(Http::RouteHandler n | t.inNode(n)) + or + expectation = "!candidateHandler" and + exists(Http::RouteHandlerCandidate n | t.inNode(n)) + or + expectation = "!xssSink" and + exists(ReflectedXss::Sink n | t.inNode(n)) + or + expectation = "!xss" and + exists(XssConfig::Configuration cfg, DataFlow::Node sink | + cfg.hasFlow(_, sink) and t.inNode(sink) + ) + or + expectation = "!cleartextStorageSink" and + exists(CleartextStorage::Sink n | t.inNode(n)) + or + expectation = "!corsMiconfigurationSink" and + exists(CorsMisconfigurationForCredentials::Sink n | t.inNode(n)) + or + expectation = "!stackTraceExposureSink" and + exists(StackTraceExposure::Sink n | t.inNode(n)) + or + expectation = "!redirectSink" and + exists(ServerSideUrlRedirect::Sink n | t.inNode(n)) + or + expectation = "!ssrfSink" and + exists(RequestForgery::Sink n | t.inNode(n)) + ) +} diff --git a/javascript/ql/test/library-tests/frameworks/restify/tests.expected b/javascript/ql/test/library-tests/frameworks/restify/tests.expected index 3f26379a3fa..09ccb149eae 100644 --- a/javascript/ql/test/library-tests/frameworks/restify/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/restify/tests.expected @@ -11,10 +11,16 @@ test_RequestInputAccess | src/test.js:19:5:19:26 | request ... ('bar') | header | src/test.js:12:19:22:1 | functio ... okie;\\n} | | src/test.js:20:5:20:25 | request ... ('baz') | header | src/test.js:12:19:22:1 | functio ... okie;\\n} | test_RouteHandler_getAResponseHeader +| src/test.js:6:1:6:21 | functio ... er1(){} | content-type | src/test.js:6:1:6:21 | functio ... er1(){} | +| src/test.js:9:19:11:1 | functio ... ition\\n} | content-type | src/test.js:9:19:11:1 | functio ... ition\\n} | | src/test.js:9:19:11:1 | functio ... ition\\n} | header1 | src/test.js:10:5:10:34 | respons ... 1', '') | +| src/test.js:12:19:22:1 | functio ... okie;\\n} | content-type | src/test.js:12:19:22:1 | functio ... okie;\\n} | | src/test.js:12:19:22:1 | functio ... okie;\\n} | header2 | src/test.js:13:5:13:37 | respons ... 2', '') | test_HeaderDefinition_defines +| src/test.js:6:1:6:21 | functio ... er1(){} | content-type | application/json | +| src/test.js:9:19:11:1 | functio ... ition\\n} | content-type | application/json | | src/test.js:10:5:10:34 | respons ... 1', '') | header1 | | +| src/test.js:12:19:22:1 | functio ... okie;\\n} | content-type | application/json | | src/test.js:13:5:13:37 | respons ... 2', '') | header2 | | test_ResponseExpr | src/test.js:9:46:9:53 | response | src/test.js:9:19:11:1 | functio ... ition\\n} | @@ -24,14 +30,20 @@ test_ResponseExpr | src/test.js:12:46:12:53 | response | src/test.js:12:19:22:1 | functio ... okie;\\n} | | src/test.js:13:5:13:12 | response | src/test.js:12:19:22:1 | functio ... okie;\\n} | test_HeaderDefinition +| src/test.js:6:1:6:21 | functio ... er1(){} | src/test.js:6:1:6:21 | functio ... er1(){} | +| src/test.js:9:19:11:1 | functio ... ition\\n} | src/test.js:9:19:11:1 | functio ... ition\\n} | | src/test.js:10:5:10:34 | respons ... 1', '') | src/test.js:9:19:11:1 | functio ... ition\\n} | +| src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:12:19:22:1 | functio ... okie;\\n} | | src/test.js:13:5:13:37 | respons ... 2', '') | src/test.js:12:19:22:1 | functio ... okie;\\n} | test_RouteSetup_getServer | src/test.js:7:1:7:26 | server2 ... ndler1) | src/test.js:4:15:4:36 | restify ... erver() | | src/test.js:9:1:11:2 | server2 ... tion\\n}) | src/test.js:4:15:4:36 | restify ... erver() | | src/test.js:12:1:22:2 | server2 ... kie;\\n}) | src/test.js:4:15:4:36 | restify ... erver() | test_HeaderDefinition_getAHeaderName +| src/test.js:6:1:6:21 | functio ... er1(){} | content-type | +| src/test.js:9:19:11:1 | functio ... ition\\n} | content-type | | src/test.js:10:5:10:34 | respons ... 1', '') | header1 | +| src/test.js:12:19:22:1 | functio ... okie;\\n} | content-type | | src/test.js:13:5:13:37 | respons ... 2', '') | header2 | test_ServerDefinition | src/test.js:1:15:1:47 | require ... erver() | From b79f7f3e95addfa2eb8a5d962b5d969373c557f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz?= Date: Tue, 18 Oct 2022 13:29:28 +0200 Subject: [PATCH 0015/1420] Address code review comments Co-authored-by: Erik Krogh Kristensen --- .../semmle/javascript/frameworks/Restify.qll | 37 +++---- .../semmle/javascript/frameworks/Spife.qll | 99 ++++++++----------- .../frameworks/Restify2/src/index.js | 6 +- .../frameworks/Restify2/tests.expected | 3 + .../frameworks/Restify2/tests.ql | 37 +++++++ .../frameworks/Spife/lib/views/index.js | 10 +- .../frameworks/Spife/tests.expected | 42 ++++---- .../library-tests/frameworks/Spife/tests.ql | 36 +++++++ 8 files changed, 164 insertions(+), 106 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll b/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll index fb80beb4832..1556f31c630 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll @@ -138,7 +138,7 @@ module Restify { /** * An access to a header on a Restify request. */ - private class RequestHeaderAccess extends Http::RequestHeaderAccess { + private class RequestHeaderAccess extends Http::RequestHeaderAccess instanceof DataFlow::MethodCallNode { RouteHandler rh; RequestHeaderAccess() { @@ -150,7 +150,7 @@ module Restify { } override string getAHeaderName() { - result = this.(DataFlow::MethodCallNode).getArgument(0).getStringValue().toLowerCase() + super.getArgument(0).mayHaveStringValue(any(string s | s.toLowerCase() = result)) } override RouteHandler getRouteHandler() { result = rh } @@ -185,9 +185,8 @@ module Restify { /** * Gets a reference to the multiple headers object that is to be set. */ - private DataFlow::SourceNode getAHeaderSource() { - this.getArgument(0).getALocalSource() instanceof DataFlow::ObjectLiteralNode and - result.flowsTo(this.getArgument(0)) + private DataFlow::ObjectLiteralNode getAHeaderSource() { + result = this.getArgument(0).getALocalSource() } override predicate definesHeaderValue(string headerName, DataFlow::Node headerValue) { @@ -198,9 +197,7 @@ module Restify { } override DataFlow::Node getNameNode() { - exists(DataFlow::PropWrite write | this.getAHeaderSource().getAPropertyWrite() = write | - result = write.getPropertyNameExpr().flow() - ) + result = this.getAHeaderSource().getAPropertyWrite().getPropertyNameExpr().flow() } override RouteHandler getRouteHandler() { this.getReceiver() = result.getAResponseNode() } @@ -265,34 +262,29 @@ module Restify { .hasPropertyWrite("formatters", formatters) } - DataFlow::SourceNode getAFormatterHandler() { formatters.hasPropertyWrite(_, result) } + DataFlow::FunctionNode getAFormatterHandler() { formatters.hasPropertyWrite(_, result) } } /** * A Restify route handler. */ class FormatterHandler extends Http::Servers::StandardRouteHandler, DataFlow::FunctionNode { - Function function; - - FormatterHandler() { - function = astNode and - any(FormatterSetup setup).getAFormatterHandler() = this - } + FormatterHandler() { any(FormatterSetup setup).getAFormatterHandler() = this } /** * Gets the parameter of the formatter handler that contains the request object. */ - Parameter getRequestParameter() { result = function.getParameter(0) } + DataFlow::ParameterNode getRequestParameter() { result = this.getParameter(0) } /** * Gets the parameter of the formatter handler that contains the response object. */ - Parameter getResponseParameter() { result = function.getParameter(1) } + DataFlow::ParameterNode getResponseParameter() { result = this.getParameter(1) } /** * Gets the parameter of the formatter handler that contains the body object. */ - Parameter getBodyParameter() { result = function.getParameter(2) } + DataFlow::ParameterNode getBodyParameter() { result = this.getParameter(2) } } /** @@ -302,7 +294,7 @@ module Restify { private class FormatterRequestSource extends Http::Servers::RequestSource { FormatterHandler fh; - FormatterRequestSource() { this = DataFlow::parameterNode(fh.getRequestParameter()) } + FormatterRequestSource() { this = fh.getRequestParameter() } /** * Gets the formatter handler that handles this request. @@ -317,7 +309,7 @@ module Restify { private class FormatterResponseSource extends Http::Servers::ResponseSource { FormatterHandler fh; - FormatterResponseSource() { this = DataFlow::parameterNode(fh.getResponseParameter()) } + FormatterResponseSource() { this = fh.getResponseParameter() } /** * Gets the route handler that provides this response. @@ -362,10 +354,7 @@ module Restify { result = this.getArgument(1) or this.getNumArgument() = 2 and - this.getArgument(0) - .getALocalSource() - .(DataFlow::ObjectLiteralNode) - .hasPropertyWrite("hostname", result) + this.getArgument(0).getALocalSource().hasPropertyWrite("hostname", result) or this.getNumArgument() = 2 and result = this.getArgument(0) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll b/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll index 35d168a3e10..09c7850bd48 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll @@ -25,13 +25,10 @@ module Spife { TaggedTemplateExpr template; RouteSetup() { - exists(CallExpr templateCall | - this.getCalleeNode().asExpr() = template and - API::moduleImport(["@npm/spife/routing", "spife/routing"]) - .asSource() - .flowsToExpr(template.getTag()) and - templateCall.getAChild() = template - ) + this.getCalleeNode().asExpr() = template and + API::moduleImport(["@npm/spife/routing", "spife/routing"]) + .asSource() + .flowsToExpr(template.getTag()) } private string getRoutePattern() { @@ -184,13 +181,13 @@ module Spife { /** * An access to a user-controlled Spife context input. */ - private class ContextInputAccess extends Http::RequestInputAccess { + private class ContextInputAccess extends Http::RequestInputAccess instanceof DataFlow::MethodCallNode { ContextSource request; string kind; ContextInputAccess() { - request.ref().flowsTo(this.(DataFlow::MethodCallNode).getReceiver()) and - this.(DataFlow::MethodCallNode).getMethodName() = "get" and + request.ref().flowsTo(super.getReceiver()) and + super.getMethodName() = "get" and kind = "path" } @@ -202,7 +199,7 @@ module Spife { /** * An access to a header on a Spife request. */ - private class RequestHeaderAccess extends Http::RequestHeaderAccess { + private class RequestHeaderAccess extends Http::RequestHeaderAccess instanceof DataFlow::PropRead { RouteHandler rh; RequestHeaderAccess() { @@ -211,7 +208,7 @@ module Spife { } override string getAHeaderName() { - result = this.(DataFlow::PropRead).getPropertyName().toLowerCase() + result = super.getPropertyName().toLowerCase() } override RouteHandler getRouteHandler() { result = rh } @@ -223,7 +220,7 @@ module Spife { * A Spife response source, that is, the response variable used by a * route handler. */ - private class ReplySource extends Http::Servers::ResponseSource { + private class ReplySource extends Http::Servers::ResponseSource instanceof DataFlow::CallNode { ReplySource() { // const reply = require("@npm/spife/reply") // reply(resp) @@ -232,14 +229,12 @@ module Spife { this = API::moduleImport(["@npm/spife/reply", "spife/reply"]).getAMember().getACall() } - private DataFlow::SourceNode reachesHandlerReturn( - DataFlow::CallNode headerCall, DataFlow::TypeTracker t - ) { - result = headerCall and + private DataFlow::SourceNode reachesHandlerReturn(DataFlow::TypeTracker t) { + result = this and t.start() or exists(DataFlow::TypeTracker t2 | - result = this.reachesHandlerReturn(headerCall, t2).track(t2, t) + result = this.reachesHandlerReturn(t2).track(t2, t) ) } @@ -249,7 +244,7 @@ module Spife { override RouteHandler getRouteHandler() { exists(RouteHandler handler | handler.(DataFlow::FunctionNode).getAReturn().getALocalSource() = - this.reachesHandlerReturn(this, DataFlow::TypeTracker::end()) and + this.reachesHandlerReturn(DataFlow::TypeTracker::end()) and result = handler ) } @@ -258,14 +253,15 @@ module Spife { /** * An HTTP header defined in a Spife response. */ - private class HeaderDefinition extends Http::ExplicitHeaderDefinition, DataFlow::MethodCallNode { - ReplySource reply; + private class HeaderDefinition extends Http::ExplicitHeaderDefinition, DataFlow::MethodCallNode instanceof ReplySource { HeaderDefinition() { // reply.header(RESPONSE, 'Cache-Control', 'no-cache') - reply.ref().(DataFlow::MethodCallNode).getMethodName() = "header" and - reply.ref().(DataFlow::MethodCallNode).getNumArgument() = 3 and - this = reply + exists(DataFlow::MethodCallNode call | + this.ref() = call and + call.getMethodName() = "header" and + call.getNumArgument() = 3 + ) } override predicate definesHeaderValue(string headerName, DataFlow::Node headerValue) { @@ -276,7 +272,7 @@ module Spife { override DataFlow::Node getNameNode() { result = this.getArgument(1) } - override RouteHandler getRouteHandler() { result = reply.getRouteHandler() } + override RouteHandler getRouteHandler() { result = this.getRouteHandler() } } /** @@ -297,11 +293,8 @@ module Spife { /** * Gets a reference to the multiple headers object that is to be set. */ - private DataFlow::SourceNode getAHeaderSource() { - exists(int i | - this.getArgument(i).getALocalSource() instanceof DataFlow::ObjectLiteralNode and - result.flowsTo(this.getArgument(i)) - ) + private DataFlow::ObjectLiteralNode getAHeaderSource() { + result = this.getAnArgument().getALocalSource() } override predicate definesHeaderValue(string headerName, DataFlow::Node headerValue) { @@ -312,9 +305,7 @@ module Spife { } override DataFlow::Node getNameNode() { - exists(DataFlow::PropWrite write | this.getAHeaderSource().getAPropertyWrite() = write | - result = write.getPropertyNameExpr().flow() - ) + result = this.getAHeaderSource().getAPropertyWrite().getPropertyNameExpr().flow() } override RouteHandler getRouteHandler() { result = reply.getRouteHandler() } @@ -324,8 +315,7 @@ module Spife { * A header produced by a route handler with no explicit declaration of a Content-Type. */ private class ContentTypeRouteHandlerHeader extends Http::ImplicitHeaderDefinition, - DataFlow::FunctionNode { - ContentTypeRouteHandlerHeader() { this instanceof RouteHandler } + DataFlow::FunctionNode instanceof RouteHandler { override predicate defines(string headerName, string headerValue) { headerName = "content-type" and headerValue = "application/json" @@ -337,20 +327,18 @@ module Spife { /** * An HTTP cookie defined in a Spife HTTP response. */ - private class CookieDefinition extends Http::CookieDefinition, DataFlow::MethodCallNode { - ReplySource reply; + private class CookieDefinition extends Http::CookieDefinition, DataFlow::MethodCallNode instanceof ReplySource { CookieDefinition() { // reply.cookie(RESPONSE, 'TEST', 'FOO', {"maxAge": 1000, "httpOnly": true, "secure": true}) - this = reply.ref().(DataFlow::MethodCallNode) and - this.getMethodName() = "cookie" + this.ref().(DataFlow::MethodCallNode).getMethodName() = "cookie" } override DataFlow::Node getNameArgument() { result = this.getArgument(1) } override DataFlow::Node getValueArgument() { result = this.getArgument(2) } - override RouteHandler getRouteHandler() { result = reply.getRouteHandler() } + override RouteHandler getRouteHandler() { result = this.getRouteHandler() } } /** @@ -360,14 +348,15 @@ module Spife { RouteHandler rh; ReplyArgument() { - exists(ReplySource reply | - reply.ref().(DataFlow::CallNode).getCalleeName() = + exists(ReplySource reply, DataFlow::CallNode call | + reply.ref() = call and + call.getCalleeName() = ["reply", "cookie", "link", "header", "headers", "raw", "status", "toStream", "vary"] and - this = reply.ref().(DataFlow::CallNode).getArgument(0) and + this = call.getArgument(0) and rh = reply.getRouteHandler() ) or - this = rh.(DataFlow::FunctionNode).getAReturn() + this = rh.getAReturn() } override RouteHandler getRouteHandler() { result = rh } @@ -394,8 +383,11 @@ module Spife { ReplySource reply; TemplateObjectInput() { - reply.ref().(DataFlow::MethodCallNode).getMethodName() = "template" and - this = reply.ref().(DataFlow::MethodCallNode).getArgument(1) + exists(DataFlow::MethodCallNode call | + reply.ref() = call and + call.getMethodName() = "template" and + this = call.getArgument(1) + ) } /** @@ -407,28 +399,23 @@ module Spife { /** * An invocation of the `redirect` method of an HTTP response object. */ - private class RedirectInvocation extends Http::RedirectInvocation, DataFlow::MethodCallNode { - ReplySource reply; + private class RedirectInvocation extends Http::RedirectInvocation, DataFlow::MethodCallNode instanceof ReplySource { RedirectInvocation() { - this = reply.ref().(DataFlow::MethodCallNode) and - this.getMethodName() = "redirect" + this.ref().(DataFlow::MethodCallNode).getMethodName() = "redirect" } override DataFlow::Node getUrlArgument() { result = this.getAnArgument() } - override RouteHandler getRouteHandler() { result = reply.getRouteHandler() } + override RouteHandler getRouteHandler() { result = this.getRouteHandler() } } /** * A call to `reply.template('template', { ... })`, seen as a template instantiation. */ - private class TemplateCall extends Templating::TemplateInstantiation::Range, DataFlow::CallNode { + private class TemplateCall extends Templating::TemplateInstantiation::Range, DataFlow::MethodCallNode instanceof ReplySource { TemplateCall() { - exists(ReplySource reply | - reply.ref().(DataFlow::MethodCallNode).getMethodName() = "template" and - this = reply.ref() - ) + this.getMethodName() = "template" } override DataFlow::SourceNode getOutput() { result = this } diff --git a/javascript/ql/test/library-tests/frameworks/Restify2/src/index.js b/javascript/ql/test/library-tests/frameworks/Restify2/src/index.js index 94c678c93b7..9e3c901f377 100644 --- a/javascript/ql/test/library-tests/frameworks/Restify2/src/index.js +++ b/javascript/ql/test/library-tests/frameworks/Restify2/src/index.js @@ -101,7 +101,7 @@ server.get('/foo', // test: setup return next(); }, function(req, res, next) { // test: handler - res.header("Content-Type", "text/html"); + res.header("Content-Type", "text/html"); // test: headerDefinition res.send(req.someData); // test: stackTraceExposureSink, xssSink, xss return next(); } @@ -119,7 +119,7 @@ server.get('/foo2', // test: setup ); function xss(req, res, next) { // test: handler - res.header("Content-Type", "text/html"); + res.header("Content-Type", "text/html"); // test: headerDefinition res.send('hello ' + req.query.name); // test: source, stackTraceExposureSink, xssSink, xss next(); } @@ -140,7 +140,7 @@ function xss2(req, res, next) { // test: handler }); function xss3(req, res, next) { // test: handler - res.header("Content-Type", "text/html"); + res.header("Content-Type", "text/html"); // test: headerDefinition res.send('hello ' + req.header("foo")); // test: source, stackTraceExposureSink, xssSink, !xss next(); } diff --git a/javascript/ql/test/library-tests/frameworks/Restify2/tests.expected b/javascript/ql/test/library-tests/frameworks/Restify2/tests.expected index 1c9c7a2b45c..26e82f53391 100644 --- a/javascript/ql/test/library-tests/frameworks/Restify2/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/Restify2/tests.expected @@ -26,6 +26,9 @@ passingPositiveTests | PASSED | handler | src/index.js:192:39:192:61 | // test ... handler | | PASSED | handler | src/index.js:194:49:194:71 | // test ... handler | | PASSED | handler | src/index.js:198:65:198:87 | // test ... handler | +| PASSED | headerDefinition | src/index.js:104:46:104:70 | // test ... inition | +| PASSED | headerDefinition | src/index.js:122:44:122:68 | // test ... inition | +| PASSED | headerDefinition | src/index.js:143:44:143:68 | // test ... inition | | PASSED | redirectSink | src/index.js:78:32:78:60 | // test ... ectSink | | PASSED | redirectSink | src/index.js:87:45:87:73 | // test ... ectSink | | PASSED | redirectSink | src/index.js:88:40:88:68 | // test ... ectSink | diff --git a/javascript/ql/test/library-tests/frameworks/Restify2/tests.ql b/javascript/ql/test/library-tests/frameworks/Restify2/tests.ql index 5666ca201ad..7cc936d12ec 100644 --- a/javascript/ql/test/library-tests/frameworks/Restify2/tests.ql +++ b/javascript/ql/test/library-tests/frameworks/Restify2/tests.ql @@ -33,6 +33,15 @@ query predicate passingPositiveTests(string res, string expectation, InlineTest res = "PASSED" and t.hasPositiveTest(expectation) and ( + expectation = "headerDefinition" and + exists(Http::HeaderDefinition n | t.inNode(n)) + or + expectation = "cookieDefinition" and + exists(Http::CookieDefinition n | t.inNode(n)) + or + expectation = "templateInstantiation" and + exists(Templating::TemplateInstantiation::Range n | t.inNode(n)) + or expectation = "source" and exists(RemoteFlowSource n | t.inNode(n)) or @@ -74,6 +83,15 @@ query predicate failingPositiveTests(string res, string expectation, InlineTest res = "FAILED" and t.hasPositiveTest(expectation) and ( + expectation = "headerDefinition" and + not exists(Http::HeaderDefinition n | t.inNode(n)) + or + expectation = "cookieDefinition" and + not exists(Http::CookieDefinition n | t.inNode(n)) + or + expectation = "templateInstantiation" and + not exists(Templating::TemplateInstantiation::Range n | t.inNode(n)) + or expectation = "source" and not exists(RemoteFlowSource n | t.inNode(n)) or @@ -115,6 +133,15 @@ query predicate passingNegativeTests(string res, string expectation, InlineTest res = "PASSED" and t.hasNegativeTest(expectation) and ( + expectation = "!headerDefinition" and + not exists(Http::HeaderDefinition n | t.inNode(n)) + or + expectation = "!cookieDefinition" and + not exists(Http::CookieDefinition n | t.inNode(n)) + or + expectation = "!templateInstantiation" and + not exists(Templating::TemplateInstantiation::Range n | t.inNode(n)) + or expectation = "!source" and not exists(RemoteFlowSource n | t.inNode(n)) or @@ -156,6 +183,15 @@ query predicate failingNegativeTests(string res, string expectation, InlineTest res = "FAILED" and t.hasNegativeTest(expectation) and ( + expectation = "!headerDefinition" and + exists(Http::HeaderDefinition n | t.inNode(n)) + or + expectation = "!cookieDefinition" and + exists(Http::CookieDefinition n | t.inNode(n)) + or + expectation = "!templateInstantiation" and + exists(Templating::TemplateInstantiation::Range n | t.inNode(n)) + or expectation = "!source" and exists(RemoteFlowSource n | t.inNode(n)) or @@ -192,3 +228,4 @@ query predicate failingNegativeTests(string res, string expectation, InlineTest exists(RequestForgery::Sink n | t.inNode(n)) ) } + diff --git a/javascript/ql/test/library-tests/frameworks/Spife/lib/views/index.js b/javascript/ql/test/library-tests/frameworks/Spife/lib/views/index.js index c64299853bf..83488be55ae 100644 --- a/javascript/ql/test/library-tests/frameworks/Spife/lib/views/index.js +++ b/javascript/ql/test/library-tests/frameworks/Spife/lib/views/index.js @@ -54,7 +54,7 @@ function redirect(req, context) { // test: handler return reply.redirect(context.get('redirect_url')) // test: redirectSink, source, stackTraceExposureSink } function raw2(req, context) { // test: handler - return reply.cookie({ "test": req.query.name }, "test", req.query.name, { "httpOnly": false, "secure": false }) // test: source, cleartextStorageSink, stackTraceExposureSink + return reply.cookie({ "test": req.query.name }, "test", req.query.name, { "httpOnly": false, "secure": false }) // test: source, cleartextStorageSink, stackTraceExposureSink, cookieDefinition } function test1(req, context) { // test: handler @@ -62,9 +62,9 @@ function test1(req, context) { // test: handler case 'json': return { "some": req.query.name } // test: source, stackTraceExposureSink case 'html': - return reply.header('

' + req.query.name + '

', 'content-type', 'text/html') // test: source, xssSink, stackTraceExposureSink, xss + return reply.header('

' + req.query.name + '

', 'content-type', 'text/html') // test: source, xssSink, stackTraceExposureSink, xss, headerDefinition case 'plain': - return reply.header('

' + req.query.name + '

', { 'content-type': 'text/plain' }) // test: source, stackTraceExposureSink, !xssSink, !xss + return reply.header('

' + req.query.name + '

', { 'content-type': 'text/plain' }) // test: source, stackTraceExposureSink, !xssSink, !xss, headerDefinition } return 'well, I guess you just want plaintext.' } @@ -74,7 +74,7 @@ function test2(req, context) { // test: handler case 'json': return { "some": req.query.name } // test: source, stackTraceExposureSink case 'html': - return reply.header('

' + req.query.name + '

', { 'content-type': 'text/plain' }) // test: source, stackTraceExposureSink, !xssSink, !xss + return reply.header('

' + req.query.name + '

', { 'content-type': 'text/plain' }) // test: source, stackTraceExposureSink, !xssSink, !xss, headerDefinition } return 'well, I guess you just want plaintext.' } @@ -104,7 +104,7 @@ function test6(req, context) { // test: handler if (message.contains('foo')) { return reply(message, 200, { 'npm-notice': message }) // test: stackTraceExposureSink, !xssSink, !xss } else { - return reply(message, 200, { 'npm-notice': message, 'content-type': 'text/html' }) // test: stackTraceExposureSink, xssSink, xss + return reply(message, 200, { 'npm-notice': message, 'content-type': 'text/html' }) // test: stackTraceExposureSink, xssSink, xss, headerDefinition } } diff --git a/javascript/ql/test/library-tests/frameworks/Spife/tests.expected b/javascript/ql/test/library-tests/frameworks/Spife/tests.expected index 98c16194268..3d83ca43213 100644 --- a/javascript/ql/test/library-tests/frameworks/Spife/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/Spife/tests.expected @@ -1,6 +1,7 @@ passingPositiveTests | PASSED | candidateHandler | lib/views/index.js:82:32:82:56 | // test ... Handler | -| PASSED | cleartextStorageSink | lib/views/index.js:57:115:57:175 | // test ... ureSink | +| PASSED | cleartextStorageSink | lib/views/index.js:57:115:57:193 | // test ... inition | +| PASSED | cookieDefinition | lib/views/index.js:57:115:57:193 | // test ... inition | | PASSED | corsMiconfigurationSink | lib/views/index.js:45:41:45:72 | // test ... ionSink | | PASSED | handler | lib/views/index.js:23:40:23:55 | // test: handler | | PASSED | handler | lib/views/index.js:28:35:28:50 | // test: handler | @@ -12,6 +13,10 @@ passingPositiveTests | PASSED | handler | lib/views/index.js:86:32:86:47 | // test: handler | | PASSED | handler | lib/views/index.js:93:32:93:47 | // test: handler | | PASSED | handler | lib/views/index.js:100:32:100:47 | // test: handler | +| PASSED | headerDefinition | lib/views/index.js:65:89:65:159 | // test ... inition | +| PASSED | headerDefinition | lib/views/index.js:67:94:67:166 | // test ... inition | +| PASSED | headerDefinition | lib/views/index.js:77:94:77:166 | // test ... inition | +| PASSED | headerDefinition | lib/views/index.js:107:88:107:150 | // test ... inition | | PASSED | redirectSink | lib/views/index.js:54:54:54:106 | // test ... ureSink | | PASSED | source | lib/views/index.js:24:56:24:70 | // test: source | | PASSED | source | lib/views/index.js:29:28:29:42 | // test: source | @@ -26,12 +31,12 @@ passingPositiveTests | PASSED | source | lib/views/index.js:42:24:42:38 | // test: source | | PASSED | source | lib/views/index.js:43:39:43:91 | // test ... nk, xss | | PASSED | source | lib/views/index.js:54:54:54:106 | // test ... ureSink | -| PASSED | source | lib/views/index.js:57:115:57:175 | // test ... ureSink | +| PASSED | source | lib/views/index.js:57:115:57:193 | // test ... inition | | PASSED | source | lib/views/index.js:63:41:63:79 | // test ... ureSink | -| PASSED | source | lib/views/index.js:65:89:65:141 | // test ... nk, xss | -| PASSED | source | lib/views/index.js:67:94:67:148 | // test ... k, !xss | +| PASSED | source | lib/views/index.js:65:89:65:159 | // test ... inition | +| PASSED | source | lib/views/index.js:67:94:67:166 | // test ... inition | | PASSED | source | lib/views/index.js:75:41:75:79 | // test ... ureSink | -| PASSED | source | lib/views/index.js:77:94:77:148 | // test ... k, !xss | +| PASSED | source | lib/views/index.js:77:94:77:166 | // test ... inition | | PASSED | source | lib/views/index.js:83:49:83:103 | // test ... k, !xss | | PASSED | source | lib/views/index.js:87:25:87:39 | // test: source | | PASSED | source | lib/views/index.js:94:25:94:39 | // test: source | @@ -40,32 +45,33 @@ passingPositiveTests | PASSED | stackTraceExposureSink | lib/views/index.js:38:61:38:122 | // test ... ureSink | | PASSED | stackTraceExposureSink | lib/views/index.js:43:39:43:91 | // test ... nk, xss | | PASSED | stackTraceExposureSink | lib/views/index.js:54:54:54:106 | // test ... ureSink | -| PASSED | stackTraceExposureSink | lib/views/index.js:57:115:57:175 | // test ... ureSink | +| PASSED | stackTraceExposureSink | lib/views/index.js:57:115:57:193 | // test ... inition | | PASSED | stackTraceExposureSink | lib/views/index.js:63:41:63:79 | // test ... ureSink | -| PASSED | stackTraceExposureSink | lib/views/index.js:65:89:65:141 | // test ... nk, xss | -| PASSED | stackTraceExposureSink | lib/views/index.js:67:94:67:148 | // test ... k, !xss | +| PASSED | stackTraceExposureSink | lib/views/index.js:65:89:65:159 | // test ... inition | +| PASSED | stackTraceExposureSink | lib/views/index.js:67:94:67:166 | // test ... inition | | PASSED | stackTraceExposureSink | lib/views/index.js:75:41:75:79 | // test ... ureSink | -| PASSED | stackTraceExposureSink | lib/views/index.js:77:94:77:148 | // test ... k, !xss | +| PASSED | stackTraceExposureSink | lib/views/index.js:77:94:77:166 | // test ... inition | | PASSED | stackTraceExposureSink | lib/views/index.js:83:49:83:103 | // test ... k, !xss | | PASSED | stackTraceExposureSink | lib/views/index.js:90:57:90:103 | // test ... k, !xss | | PASSED | stackTraceExposureSink | lib/views/index.js:97:30:97:76 | // test ... k, !xss | | PASSED | stackTraceExposureSink | lib/views/index.js:105:59:105:105 | // test ... k, !xss | -| PASSED | stackTraceExposureSink | lib/views/index.js:107:88:107:132 | // test ... nk, xss | +| PASSED | stackTraceExposureSink | lib/views/index.js:107:88:107:150 | // test ... inition | | PASSED | stackTraceExposureSink | lib/views/index.js:112:34:112:72 | // test ... ureSink | +| PASSED | templateInstantiation | lib/views/index.js:38:61:38:122 | // test ... ureSink | | PASSED | xss | lib/views/index.js:43:39:43:91 | // test ... nk, xss | -| PASSED | xss | lib/views/index.js:65:89:65:141 | // test ... nk, xss | -| PASSED | xss | lib/views/index.js:107:88:107:132 | // test ... nk, xss | +| PASSED | xss | lib/views/index.js:65:89:65:159 | // test ... inition | +| PASSED | xss | lib/views/index.js:107:88:107:150 | // test ... inition | | PASSED | xssSink | lib/views/index.js:43:39:43:91 | // test ... nk, xss | -| PASSED | xssSink | lib/views/index.js:65:89:65:141 | // test ... nk, xss | -| PASSED | xssSink | lib/views/index.js:107:88:107:132 | // test ... nk, xss | +| PASSED | xssSink | lib/views/index.js:65:89:65:159 | // test ... inition | +| PASSED | xssSink | lib/views/index.js:107:88:107:150 | // test ... inition | failingPositiveTests passingNegativeTests -| PASSED | !xss | lib/views/index.js:67:94:67:148 | // test ... k, !xss | -| PASSED | !xss | lib/views/index.js:77:94:77:148 | // test ... k, !xss | +| PASSED | !xss | lib/views/index.js:67:94:67:166 | // test ... inition | +| PASSED | !xss | lib/views/index.js:77:94:77:166 | // test ... inition | | PASSED | !xss | lib/views/index.js:83:49:83:103 | // test ... k, !xss | | PASSED | !xss | lib/views/index.js:97:30:97:76 | // test ... k, !xss | -| PASSED | !xssSink | lib/views/index.js:67:94:67:148 | // test ... k, !xss | -| PASSED | !xssSink | lib/views/index.js:77:94:77:148 | // test ... k, !xss | +| PASSED | !xssSink | lib/views/index.js:67:94:67:166 | // test ... inition | +| PASSED | !xssSink | lib/views/index.js:77:94:77:166 | // test ... inition | | PASSED | !xssSink | lib/views/index.js:83:49:83:103 | // test ... k, !xss | | PASSED | !xssSink | lib/views/index.js:97:30:97:76 | // test ... k, !xss | failingNegativeTests diff --git a/javascript/ql/test/library-tests/frameworks/Spife/tests.ql b/javascript/ql/test/library-tests/frameworks/Spife/tests.ql index 5666ca201ad..b52e3be5256 100644 --- a/javascript/ql/test/library-tests/frameworks/Spife/tests.ql +++ b/javascript/ql/test/library-tests/frameworks/Spife/tests.ql @@ -33,6 +33,15 @@ query predicate passingPositiveTests(string res, string expectation, InlineTest res = "PASSED" and t.hasPositiveTest(expectation) and ( + expectation = "headerDefinition" and + exists(Http::HeaderDefinition n | t.inNode(n)) + or + expectation = "cookieDefinition" and + exists(Http::CookieDefinition n | t.inNode(n)) + or + expectation = "templateInstantiation" and + exists(Templating::TemplateInstantiation::Range n | t.inNode(n)) + or expectation = "source" and exists(RemoteFlowSource n | t.inNode(n)) or @@ -74,6 +83,15 @@ query predicate failingPositiveTests(string res, string expectation, InlineTest res = "FAILED" and t.hasPositiveTest(expectation) and ( + expectation = "headerDefinition" and + not exists(Http::HeaderDefinition n | t.inNode(n)) + or + expectation = "cookieDefinition" and + not exists(Http::CookieDefinition n | t.inNode(n)) + or + expectation = "templateInstantiation" and + not exists(Templating::TemplateInstantiation::Range n | t.inNode(n)) + or expectation = "source" and not exists(RemoteFlowSource n | t.inNode(n)) or @@ -115,6 +133,15 @@ query predicate passingNegativeTests(string res, string expectation, InlineTest res = "PASSED" and t.hasNegativeTest(expectation) and ( + expectation = "!headerDefinition" and + not exists(Http::HeaderDefinition n | t.inNode(n)) + or + expectation = "!cookieDefinition" and + not exists(Http::CookieDefinition n | t.inNode(n)) + or + expectation = "!templateInstantiation" and + not exists(Templating::TemplateInstantiation::Range n | t.inNode(n)) + or expectation = "!source" and not exists(RemoteFlowSource n | t.inNode(n)) or @@ -156,6 +183,15 @@ query predicate failingNegativeTests(string res, string expectation, InlineTest res = "FAILED" and t.hasNegativeTest(expectation) and ( + expectation = "!headerDefinition" and + exists(Http::HeaderDefinition n | t.inNode(n)) + or + expectation = "!cookieDefinition" and + exists(Http::CookieDefinition n | t.inNode(n)) + or + expectation = "!templateInstantiation" and + exists(Templating::TemplateInstantiation::Range n | t.inNode(n)) + or expectation = "!source" and exists(RemoteFlowSource n | t.inNode(n)) or From 31d271b8e18099550ef5b520de1feeb68767f4d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz?= Date: Wed, 19 Oct 2022 17:32:34 +0200 Subject: [PATCH 0016/1420] Fix format errors --- javascript/ql/test/library-tests/frameworks/Restify2/tests.ql | 1 - 1 file changed, 1 deletion(-) diff --git a/javascript/ql/test/library-tests/frameworks/Restify2/tests.ql b/javascript/ql/test/library-tests/frameworks/Restify2/tests.ql index 7cc936d12ec..b52e3be5256 100644 --- a/javascript/ql/test/library-tests/frameworks/Restify2/tests.ql +++ b/javascript/ql/test/library-tests/frameworks/Restify2/tests.ql @@ -228,4 +228,3 @@ query predicate failingNegativeTests(string res, string expectation, InlineTest exists(RequestForgery::Sink n | t.inNode(n)) ) } - From 976dd7f99fa9b69ed6358de0f8597feca728f859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz?= Date: Wed, 19 Oct 2022 18:14:25 +0200 Subject: [PATCH 0017/1420] Fix format errors --- .../semmle/javascript/frameworks/Spife.qll | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll b/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll index 09c7850bd48..01649d89d4f 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll @@ -207,9 +207,7 @@ module Spife { rh.getARequestSource().ref().getAPropertyRead(["headers", "rawHeaders"]).getAPropertyRead() } - override string getAHeaderName() { - result = super.getPropertyName().toLowerCase() - } + override string getAHeaderName() { result = super.getPropertyName().toLowerCase() } override RouteHandler getRouteHandler() { result = rh } @@ -233,9 +231,7 @@ module Spife { result = this and t.start() or - exists(DataFlow::TypeTracker t2 | - result = this.reachesHandlerReturn(t2).track(t2, t) - ) + exists(DataFlow::TypeTracker t2 | result = this.reachesHandlerReturn(t2).track(t2, t)) } /** @@ -254,7 +250,6 @@ module Spife { * An HTTP header defined in a Spife response. */ private class HeaderDefinition extends Http::ExplicitHeaderDefinition, DataFlow::MethodCallNode instanceof ReplySource { - HeaderDefinition() { // reply.header(RESPONSE, 'Cache-Control', 'no-cache') exists(DataFlow::MethodCallNode call | @@ -316,7 +311,6 @@ module Spife { */ private class ContentTypeRouteHandlerHeader extends Http::ImplicitHeaderDefinition, DataFlow::FunctionNode instanceof RouteHandler { - override predicate defines(string headerName, string headerValue) { headerName = "content-type" and headerValue = "application/json" } @@ -328,7 +322,6 @@ module Spife { * An HTTP cookie defined in a Spife HTTP response. */ private class CookieDefinition extends Http::CookieDefinition, DataFlow::MethodCallNode instanceof ReplySource { - CookieDefinition() { // reply.cookie(RESPONSE, 'TEST', 'FOO', {"maxAge": 1000, "httpOnly": true, "secure": true}) this.ref().(DataFlow::MethodCallNode).getMethodName() = "cookie" @@ -400,10 +393,7 @@ module Spife { * An invocation of the `redirect` method of an HTTP response object. */ private class RedirectInvocation extends Http::RedirectInvocation, DataFlow::MethodCallNode instanceof ReplySource { - - RedirectInvocation() { - this.ref().(DataFlow::MethodCallNode).getMethodName() = "redirect" - } + RedirectInvocation() { this.ref().(DataFlow::MethodCallNode).getMethodName() = "redirect" } override DataFlow::Node getUrlArgument() { result = this.getAnArgument() } @@ -413,10 +403,9 @@ module Spife { /** * A call to `reply.template('template', { ... })`, seen as a template instantiation. */ - private class TemplateCall extends Templating::TemplateInstantiation::Range, DataFlow::MethodCallNode instanceof ReplySource { - TemplateCall() { - this.getMethodName() = "template" - } + private class TemplateCall extends Templating::TemplateInstantiation::Range, + DataFlow::MethodCallNode instanceof ReplySource { + TemplateCall() { this.getMethodName() = "template" } override DataFlow::SourceNode getOutput() { result = this } From 009403b61ec599878eb93af66e87b0fa36a3e185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz?= Date: Wed, 19 Oct 2022 22:18:13 +0200 Subject: [PATCH 0018/1420] Add QLDoc for FormatterSetup.getAFormatterHandler --- javascript/ql/lib/semmle/javascript/frameworks/Restify.qll | 3 +++ 1 file changed, 3 insertions(+) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll b/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll index 1556f31c630..3c4f1e1fd8a 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll @@ -262,6 +262,9 @@ module Restify { .hasPropertyWrite("formatters", formatters) } + /** + * Gets the formatter handler installed by this setup. + */ DataFlow::FunctionNode getAFormatterHandler() { formatters.hasPropertyWrite(_, result) } } From c7ac2379689b2f4d05081d852c9293def8209a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz?= Date: Wed, 19 Oct 2022 23:41:37 +0200 Subject: [PATCH 0019/1420] Update test results after merging new XSS improvements --- .../ql/test/library-tests/frameworks/Spife/tests.expected | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/javascript/ql/test/library-tests/frameworks/Spife/tests.expected b/javascript/ql/test/library-tests/frameworks/Spife/tests.expected index 3d83ca43213..717a8d44973 100644 --- a/javascript/ql/test/library-tests/frameworks/Spife/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/Spife/tests.expected @@ -69,13 +69,13 @@ passingNegativeTests | PASSED | !xss | lib/views/index.js:67:94:67:166 | // test ... inition | | PASSED | !xss | lib/views/index.js:77:94:77:166 | // test ... inition | | PASSED | !xss | lib/views/index.js:83:49:83:103 | // test ... k, !xss | +| PASSED | !xss | lib/views/index.js:90:57:90:103 | // test ... k, !xss | | PASSED | !xss | lib/views/index.js:97:30:97:76 | // test ... k, !xss | +| PASSED | !xss | lib/views/index.js:105:59:105:105 | // test ... k, !xss | | PASSED | !xssSink | lib/views/index.js:67:94:67:166 | // test ... inition | | PASSED | !xssSink | lib/views/index.js:77:94:77:166 | // test ... inition | | PASSED | !xssSink | lib/views/index.js:83:49:83:103 | // test ... k, !xss | +| PASSED | !xssSink | lib/views/index.js:90:57:90:103 | // test ... k, !xss | | PASSED | !xssSink | lib/views/index.js:97:30:97:76 | // test ... k, !xss | +| PASSED | !xssSink | lib/views/index.js:105:59:105:105 | // test ... k, !xss | failingNegativeTests -| FAILED | !xss | lib/views/index.js:90:57:90:103 | // test ... k, !xss | -| FAILED | !xss | lib/views/index.js:105:59:105:105 | // test ... k, !xss | -| FAILED | !xssSink | lib/views/index.js:90:57:90:103 | // test ... k, !xss | -| FAILED | !xssSink | lib/views/index.js:105:59:105:105 | // test ... k, !xss | From 742e4aa471c74ab7a3cad06893b91480937ea430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz?= Date: Mon, 24 Oct 2022 16:17:11 +0200 Subject: [PATCH 0020/1420] Apply suggestions from code review Co-authored-by: Erik Krogh Kristensen --- .../semmle/javascript/frameworks/Restify.qll | 8 +++----- .../lib/semmle/javascript/frameworks/Spife.qll | 17 ++++++++--------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll b/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll index 3c4f1e1fd8a..10c5378e91e 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll @@ -384,7 +384,7 @@ module Restify { } /** - * The URL of a REstify client, viewed as a sink for request forgery. + * The URL of a Restify client, viewed as a sink for request forgery. */ class RequestForgerySink extends RFC::RequestForgery::Sink { RequestForgerySink() { @@ -435,8 +435,7 @@ module Restify { * A header produced by a route handler with no explicit declaration of a Content-Type. */ private class ContentTypeRouteHandlerHeader extends Http::ImplicitHeaderDefinition, - DataFlow::FunctionNode { - ContentTypeRouteHandlerHeader() { this instanceof RouteHandler } + DataFlow::FunctionNode instanceof RouteHandler { override predicate defines(string headerName, string headerValue) { headerName = "content-type" and headerValue = "application/json" @@ -454,8 +453,7 @@ module Restify { override DataFlow::SourceNode getAReference() { result = def.ref() } } - private class RoutingTreeSetup extends Routing::RouteSetup::MethodCall { - RoutingTreeSetup() { this instanceof RouteSetup } + private class RoutingTreeSetup extends Routing::RouteSetup::MethodCall instanceof RouteSetup { override string getRelativePath() { not this.getMethodName() = ["use", "pre", "param", "on"] and // do not treat parameter name as a path diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll b/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll index 01649d89d4f..36c796bccb1 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll @@ -186,8 +186,7 @@ module Spife { string kind; ContextInputAccess() { - request.ref().flowsTo(super.getReceiver()) and - super.getMethodName() = "get" and + this = request.ref().getAMethodCall("get") kind = "path" } @@ -261,7 +260,7 @@ module Spife { override predicate definesHeaderValue(string headerName, DataFlow::Node headerValue) { // reply.header(RESPONSE, 'Cache-Control', 'no-cache') - headerName = this.getNameNode().getStringValue() and + this.getNameNode().mayHaveStringValue(headerName) and headerValue = this.getArgument(2) } @@ -279,10 +278,10 @@ module Spife { MultipleHeaderDefinitions() { // reply.header(RESPONSE, {'Cache-Control': 'no-cache'}) // reply(RESPONSE, {'Cache-Control': 'no-cache'}) - reply.ref().(DataFlow::CallNode).getCalleeName() = ["header", "reply"] and - reply.ref().(DataFlow::CallNode).getAnArgument().getALocalSource() instanceof - DataFlow::ObjectLiteralNode and - this = reply + exists(DataFlow::CallNode call | call = [reply.ref(), reply.ref().getAMethodCall("header")] | + call.getAnArgument().getALocalSource() instanceof DataFlow::ObjectLiteralNode and + this = call + ) } /** @@ -321,10 +320,10 @@ module Spife { /** * An HTTP cookie defined in a Spife HTTP response. */ - private class CookieDefinition extends Http::CookieDefinition, DataFlow::MethodCallNode instanceof ReplySource { + private class CookieDefinition extends Http::CookieDefinition, DataFlow::MethodCallNode { CookieDefinition() { // reply.cookie(RESPONSE, 'TEST', 'FOO', {"maxAge": 1000, "httpOnly": true, "secure": true}) - this.ref().(DataFlow::MethodCallNode).getMethodName() = "cookie" + this = any(ReplySource r).ref().getAMethodCall("cookie") } override DataFlow::Node getNameArgument() { result = this.getArgument(1) } From aafef382dca26e28ef6ea6ead252d4019736ebe9 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Mon, 24 Oct 2022 18:57:24 +0200 Subject: [PATCH 0021/1420] refactor StringPercentCall#getFormatArgument --- .../codeql/ruby/frameworks/StringFormatters.qll | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll b/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll index b11fba8301f..da9ce58345e 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll @@ -87,13 +87,15 @@ class StringPercentCall extends PrintfStyleCall { override DataFlow::Node getFormatString() { result = this.getReceiver() } override DataFlow::Node getFormatArgument(int n) { - exists(DataFlow::CallNode arrCall | - arrCall = this.getArgument(0) and arrCall.getMethodName() = "[]" - | - n = -2 and // -2 is indicates that the index does not make sense in this context - result = arrCall.getKeywordArgument(_) + exists(Ast::Call call | call = this.asExpr().getExpr() | + exists(Ast::ArrayLiteral arrLit | arrLit = call.getArgument(0) | + result.asExpr().getExpr() = arrLit.getElement(n) + ) or - result = arrCall.getArgument(n) + exists(Ast::HashLiteral hashLit | hashLit = call.getArgument(0) | + n = -2 and // -2 is indicates that the index does not make sense in this context + result.asExpr().getExpr() = hashLit.getAnElement() + ) ) } From 37ea3f23f17bb9e2447b7f4a29ed14817088d275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz?= Date: Tue, 25 Oct 2022 11:42:48 +0200 Subject: [PATCH 0022/1420] Refactored `ReplySource` to `ReplyCall`. Got rid of unnecessary `ref()` --- .../semmle/javascript/frameworks/Spife.qll | 197 +++++++++--------- .../frameworks/Spife/lib/views/index.js | 20 +- .../frameworks/Spife/tests.expected | 123 +++++++++-- .../library-tests/frameworks/Spife/tests.ql | 32 ++- 4 files changed, 246 insertions(+), 126 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll b/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll index 36c796bccb1..a97eb98dc29 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll @@ -186,7 +186,7 @@ module Spife { string kind; ContextInputAccess() { - this = request.ref().getAMethodCall("get") + this = request.ref().getAMethodCall("get") and kind = "path" } @@ -217,29 +217,25 @@ module Spife { * A Spife response source, that is, the response variable used by a * route handler. */ - private class ReplySource extends Http::Servers::ResponseSource instanceof DataFlow::CallNode { - ReplySource() { - // const reply = require("@npm/spife/reply") + private class ReplyCall extends API::CallNode { + ReplyCall() { // reply(resp) + this = API::moduleImport(["@npm/spife/reply", "spife/reply"]).getACall() + or // reply.header(resp, 'foo', 'bar') - this = API::moduleImport(["@npm/spife/reply", "spife/reply"]).getACall() or this = API::moduleImport(["@npm/spife/reply", "spife/reply"]).getAMember().getACall() } - private DataFlow::SourceNode reachesHandlerReturn(DataFlow::TypeTracker t) { - result = this and - t.start() - or - exists(DataFlow::TypeTracker t2 | result = this.reachesHandlerReturn(t2).track(t2, t)) + predicate isDirectReplyCall() { + this = API::moduleImport(["@npm/spife/reply", "spife/reply"]).getACall() } /** * Gets the route handler that provides this response. */ - override RouteHandler getRouteHandler() { + RouteHandler getRouteHandler() { exists(RouteHandler handler | - handler.(DataFlow::FunctionNode).getAReturn().getALocalSource() = - this.reachesHandlerReturn(DataFlow::TypeTracker::end()) and + handler.getAReturn() = this.getReturn().getAValueReachableFromSource() and result = handler ) } @@ -248,47 +244,46 @@ module Spife { /** * An HTTP header defined in a Spife response. */ - private class HeaderDefinition extends Http::ExplicitHeaderDefinition, DataFlow::MethodCallNode instanceof ReplySource { - HeaderDefinition() { + private class SingleHeaderDefinition extends Http::ExplicitHeaderDefinition instanceof ReplyCall { + SingleHeaderDefinition() { // reply.header(RESPONSE, 'Cache-Control', 'no-cache') - exists(DataFlow::MethodCallNode call | - this.ref() = call and - call.getMethodName() = "header" and - call.getNumArgument() = 3 - ) + this.getCalleeName() = "header" and + this.getNumArgument() = 3 } override predicate definesHeaderValue(string headerName, DataFlow::Node headerValue) { // reply.header(RESPONSE, 'Cache-Control', 'no-cache') this.getNameNode().mayHaveStringValue(headerName) and - headerValue = this.getArgument(2) + headerValue = this.(DataFlow::MethodCallNode).getArgument(2) } - override DataFlow::Node getNameNode() { result = this.getArgument(1) } + override DataFlow::Node getNameNode() { + result = this.(DataFlow::MethodCallNode).getArgument(1) + } - override RouteHandler getRouteHandler() { result = this.getRouteHandler() } + override RouteHandler getRouteHandler() { result = this.(ReplyCall).getRouteHandler() } } /** * An invocation that sets any number of headers of the HTTP response. */ - private class MultipleHeaderDefinitions extends Http::ExplicitHeaderDefinition, DataFlow::CallNode { - ReplySource reply; - + private class MultipleHeaderDefinitions extends Http::ExplicitHeaderDefinition instanceof ReplyCall { MultipleHeaderDefinitions() { - // reply.header(RESPONSE, {'Cache-Control': 'no-cache'}) - // reply(RESPONSE, {'Cache-Control': 'no-cache'}) - exists(DataFlow::CallNode call | call = [reply.ref(), reply.ref().getAMethodCall("header")] | - call.getAnArgument().getALocalSource() instanceof DataFlow::ObjectLiteralNode and - this = call - ) + ( + // reply.header(RESPONSE, {'Cache-Control': 'no-cache'}) + this.getCalleeName() = "header" + or + // reply(RESPONSE, {'Cache-Control': 'no-cache'}) + this.isDirectReplyCall() + ) and + this.getAnArgument().getALocalSource() instanceof DataFlow::ObjectLiteralNode } /** * Gets a reference to the multiple headers object that is to be set. */ - private DataFlow::ObjectLiteralNode getAHeaderSource() { - result = this.getAnArgument().getALocalSource() + DataFlow::ObjectLiteralNode getAHeaderSource() { + result = this.(DataFlow::CallNode).getAnArgument().getALocalSource() } override predicate definesHeaderValue(string headerName, DataFlow::Node headerValue) { @@ -302,58 +297,108 @@ module Spife { result = this.getAHeaderSource().getAPropertyWrite().getPropertyNameExpr().flow() } - override RouteHandler getRouteHandler() { result = reply.getRouteHandler() } + override RouteHandler getRouteHandler() { result = this.(ReplyCall).getRouteHandler() } } /** * A header produced by a route handler with no explicit declaration of a Content-Type. */ - private class ContentTypeRouteHandlerHeader extends Http::ImplicitHeaderDefinition, - DataFlow::FunctionNode instanceof RouteHandler { + private class ContentTypeRouteHandlerHeader extends Http::ImplicitHeaderDefinition instanceof RouteHandler { override predicate defines(string headerName, string headerValue) { headerName = "content-type" and headerValue = "application/json" } - override Http::RouteHandler getRouteHandler() { result = this } + override RouteHandler getRouteHandler() { result = this } } /** * An HTTP cookie defined in a Spife HTTP response. */ - private class CookieDefinition extends Http::CookieDefinition, DataFlow::MethodCallNode { + private class CookieDefinition extends Http::CookieDefinition instanceof ReplyCall { CookieDefinition() { // reply.cookie(RESPONSE, 'TEST', 'FOO', {"maxAge": 1000, "httpOnly": true, "secure": true}) - this = any(ReplySource r).ref().getAMethodCall("cookie") + this.getCalleeName() = "cookie" } - override DataFlow::Node getNameArgument() { result = this.getArgument(1) } + // this = any(ReplyCall r).ref().getAMethodCall("cookie") + override DataFlow::Node getNameArgument() { result = this.(ReplyCall).getArgument(1) } - override DataFlow::Node getValueArgument() { result = this.getArgument(2) } + override DataFlow::Node getValueArgument() { result = this.(ReplyCall).getArgument(2) } - override RouteHandler getRouteHandler() { result = this.getRouteHandler() } + override RouteHandler getRouteHandler() { result = this.(ReplyCall).getRouteHandler() } } /** - * A response argument passed to the `reply` method. + * A response sent using a method on the `reply` object. */ - private class ReplyArgument extends Http::ResponseSendArgument, DataFlow::Node { + private class ReplyMethodCallArgument extends Http::ResponseSendArgument { + ReplyCall reply; + + ReplyMethodCallArgument() { + // reply.header(RESPONSE, {'Cache-Control': 'no-cache'}) + reply.getCalleeName() = + ["cookie", "link", "header", "headers", "raw", "status", "toStream", "vary"] and + reply.getArgument(0) = this + } + + override RouteHandler getRouteHandler() { result = reply.getRouteHandler() } + } + + /** + * A response sent using the `reply()` method. + */ + private class ReplyCallArgument extends Http::ResponseSendArgument { + ReplyCall reply; + + ReplyCallArgument() { + // reply(RESPONSE, {'Cache-Control': 'no-cache'}) + reply.isDirectReplyCall() and + reply.getArgument(0) = this + } + + override RouteHandler getRouteHandler() { result = reply.getRouteHandler() } + } + + /** + * The return statement for a route handler. + */ + private class RouteHandlerReturn extends Http::ResponseSendArgument { RouteHandler rh; - ReplyArgument() { - exists(ReplySource reply, DataFlow::CallNode call | - reply.ref() = call and - call.getCalleeName() = - ["reply", "cookie", "link", "header", "headers", "raw", "status", "toStream", "vary"] and - this = call.getArgument(0) and - rh = reply.getRouteHandler() - ) - or - this = rh.getAReturn() + RouteHandlerReturn() { + this = rh.getAReturn() and not this.getALocalSource() instanceof ReplyCall } override RouteHandler getRouteHandler() { result = rh } } + /** + * A call to `reply.template('template', { ... })`, seen as a template instantiation. + */ + private class TemplateCall extends Templating::TemplateInstantiation::Range instanceof ReplyCall { + TemplateCall() { this.getCalleeName() = "template" } + + override DataFlow::SourceNode getOutput() { result = this } + + override DataFlow::Node getTemplateFileNode() { result = this.(ReplyCall).getArgument(0) } + + override DataFlow::Node getTemplateParamsNode() { result = this.(ReplyCall).getArgument(1) } + } + + /** + * An object passed to the `template` method of the reply object. + */ + private class TemplateObjectInput extends DataFlow::Node { + TemplateCall call; + + TemplateObjectInput() { this = call.(ReplyCall).getArgument(1) } + + /** + * Gets the route handler that uses this object. + */ + RouteHandler getRouteHandler() { result = call.(ReplyCall).getRouteHandler() } + } + /** * An expression passed to the `template` method of the reply object * as the value of a template variable. @@ -361,55 +406,19 @@ module Spife { private class TemplateInput extends Http::ResponseBody { TemplateObjectInput obj; - TemplateInput() { - obj.getALocalSource().(DataFlow::ObjectLiteralNode).hasPropertyWrite(_, this) - } + TemplateInput() { obj.getALocalSource().hasPropertyWrite(_, this) } override RouteHandler getRouteHandler() { result = obj.getRouteHandler() } } - /** - * An object passed to the `template` method of the reply object. - */ - private class TemplateObjectInput extends DataFlow::Node { - ReplySource reply; - - TemplateObjectInput() { - exists(DataFlow::MethodCallNode call | - reply.ref() = call and - call.getMethodName() = "template" and - this = call.getArgument(1) - ) - } - - /** - * Gets the route handler that uses this object. - */ - RouteHandler getRouteHandler() { result = reply.getRouteHandler() } - } - /** * An invocation of the `redirect` method of an HTTP response object. */ - private class RedirectInvocation extends Http::RedirectInvocation, DataFlow::MethodCallNode instanceof ReplySource { - RedirectInvocation() { this.ref().(DataFlow::MethodCallNode).getMethodName() = "redirect" } + private class RedirectInvocation extends Http::RedirectInvocation instanceof ReplyCall { + RedirectInvocation() { this.getCalleeName() = "redirect" } override DataFlow::Node getUrlArgument() { result = this.getAnArgument() } override RouteHandler getRouteHandler() { result = this.getRouteHandler() } } - - /** - * A call to `reply.template('template', { ... })`, seen as a template instantiation. - */ - private class TemplateCall extends Templating::TemplateInstantiation::Range, - DataFlow::MethodCallNode instanceof ReplySource { - TemplateCall() { this.getMethodName() = "template" } - - override DataFlow::SourceNode getOutput() { result = this } - - override DataFlow::Node getTemplateFileNode() { result = this.getArgument(0) } - - override DataFlow::Node getTemplateParamsNode() { result = this.getArgument(1) } - } } diff --git a/javascript/ql/test/library-tests/frameworks/Spife/lib/views/index.js b/javascript/ql/test/library-tests/frameworks/Spife/lib/views/index.js index 83488be55ae..3f8706f46b3 100644 --- a/javascript/ql/test/library-tests/frameworks/Spife/lib/views/index.js +++ b/javascript/ql/test/library-tests/frameworks/Spife/lib/views/index.js @@ -35,23 +35,23 @@ function homepage(req, context) { // test: handler sink(req.urlObject.pathname) // test: source sink(context.get('package')) // test: source sink(context) - return reply.template('home', { target: req.query.name }) // test: source, templateInstantiation, stackTraceExposureSink + return reply.template('home', { target: req.query.name }) // test: source, templateInstantiation, stackTraceExposureSink, responseBody } function raw1(req, context) { // test: handler sink(req.query.name) // test: source return reply(req.query.name, 200, { // test: source, xssSink, stackTraceExposureSink, xss - "content-type": "text/html", - "access-control-allow-origin": "*", // test: corsMiconfigurationSink - "access-control-allow-headers": "Content-Type, Authorization, Content-Length, X-Requested-With", - "access-control-allow-methods": "GET, POST, PUT, DELETE, OPTIONS", - "access-control-allow-credentials": "true" + "content-type": "text/html", // test: headerDefinition + "access-control-allow-origin": "*", // test: corsMiconfigurationSink, headerDefinition + "access-control-allow-headers": "Content-Type, Authorization, Content-Length, X-Requested-With", // test: headerDefinition + "access-control-allow-methods": "GET, POST, PUT, DELETE, OPTIONS", // test: headerDefinition + "access-control-allow-credentials": "true" //test: headerDefinition }) } function redirect(req, context) { // test: handler - return reply.redirect(context.get('redirect_url')) // test: redirectSink, source, stackTraceExposureSink + return reply.redirect(context.get('redirect_url')) // test: redirectSink, source } function raw2(req, context) { // test: handler return reply.cookie({ "test": req.query.name }, "test", req.query.name, { "httpOnly": false, "secure": false }) // test: source, cleartextStorageSink, stackTraceExposureSink, cookieDefinition @@ -64,7 +64,7 @@ function test1(req, context) { // test: handler case 'html': return reply.header('

' + req.query.name + '

', 'content-type', 'text/html') // test: source, xssSink, stackTraceExposureSink, xss, headerDefinition case 'plain': - return reply.header('

' + req.query.name + '

', { 'content-type': 'text/plain' }) // test: source, stackTraceExposureSink, !xssSink, !xss, headerDefinition + return reply.header('

' + req.query.name + '

', { 'content-type': 'text/plain' }) // test: source, stackTraceExposureSink, !xssSink, !xss, headerDefinition, headerDefinition } return 'well, I guess you just want plaintext.' } @@ -87,7 +87,7 @@ function test4(req, context) { // test: handler const body = req.body // test: source const newPackument = body['package-json'] const message = `INFO: User invited to package ${newPackument._id} successfully.` - return reply(message, 200, { 'npm-notice': message }) // test: stackTraceExposureSink, !xssSink, !xss + return reply(message, 200, { 'npm-notice': message }) // test: stackTraceExposureSink, !xssSink, !xss, headerDefinition } function test5(req, context) { // test: handler @@ -102,7 +102,7 @@ function test6(req, context) { // test: handler const newPackument = body['package-json'] const message = `INFO: User invited to package ${newPackument._id} successfully.` if (message.contains('foo')) { - return reply(message, 200, { 'npm-notice': message }) // test: stackTraceExposureSink, !xssSink, !xss + return reply(message, 200, { 'npm-notice': message }) // test: stackTraceExposureSink, !xssSink, !xss, headerDefinition } else { return reply(message, 200, { 'npm-notice': message, 'content-type': 'text/html' }) // test: stackTraceExposureSink, xssSink, xss, headerDefinition } diff --git a/javascript/ql/test/library-tests/frameworks/Spife/tests.expected b/javascript/ql/test/library-tests/frameworks/Spife/tests.expected index 717a8d44973..dc4c3451ce0 100644 --- a/javascript/ql/test/library-tests/frameworks/Spife/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/Spife/tests.expected @@ -1,8 +1,88 @@ +WARNING: Unused method getCredentialsHeader (tests.ql:70,26-46) +test0 +| lib/views/index.js:23:1:26:1 | functio ... tned)\\n} | content-type | application/json | +| lib/views/index.js:28:1:39:1 | functio ... eBody\\n} | content-type | application/json | +| lib/views/index.js:41:1:51:1 | functio ... \\n })\\n} | content-type | application/json | +| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-credentials | true | +| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-headers | Content-Type, Authorization, Content-Length, X-Requested-With | +| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-methods | GET, POST, PUT, DELETE, OPTIONS | +| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-origin | * | +| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | content-type | text/html | +| lib/views/index.js:53:1:55:1 | functio ... ource\\n} | content-type | application/json | +| lib/views/index.js:56:1:58:1 | functio ... ition\\n} | content-type | application/json | +| lib/views/index.js:60:1:70:1 | functio ... ext.'\\n} | content-type | application/json | +| lib/views/index.js:65:14:65:87 | reply.h ... /html') | content-type | text/html | +| lib/views/index.js:67:14:67:92 | reply.h ... ain' }) | content-type | text/plain | +| lib/views/index.js:72:1:80:1 | functio ... ext.'\\n} | content-type | application/json | +| lib/views/index.js:77:14:77:92 | reply.h ... ain' }) | content-type | text/plain | +| lib/views/index.js:82:1:84:1 | functio ... !xss\\n} | content-type | application/json | +| lib/views/index.js:86:1:91:1 | functio ... ition\\n} | content-type | application/json | +| lib/views/index.js:93:1:98:1 | functio ... !xss\\n} | content-type | application/json | +| lib/views/index.js:100:1:109:1 | functio ... n\\n }\\n} | content-type | application/json | +| lib/views/index.js:107:12:107:86 | reply(m ... tml' }) | content-type | text/html | +| lib/views/index.js:111:1:115:1 | functio ... \\n })\\n} | content-type | application/json | +test +| lib/views/index.js:45:36:45:38 | "*" | +test2 +| lib/views/index.js:43:16:43:29 | req.query.name | +| lib/views/index.js:65:27:65:57 | '

' + ... '

' | +| lib/views/index.js:107:18:107:24 | message | +test3 +| lib/views/index.js:43:16:43:29 | req.query.name | +| lib/views/index.js:65:27:65:57 | '

' + ... '

' | +| lib/views/index.js:107:18:107:24 | message | +test4 +| lib/views/index.js:23:1:26:1 | functio ... tned)\\n} | content-type | application/json | +| lib/views/index.js:28:1:39:1 | functio ... eBody\\n} | content-type | application/json | +| lib/views/index.js:41:1:51:1 | functio ... \\n })\\n} | content-type | application/json | +| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-credentials | true | +| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-headers | Content-Type, Authorization, Content-Length, X-Requested-With | +| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-methods | GET, POST, PUT, DELETE, OPTIONS | +| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-origin | * | +| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | content-type | text/html | +| lib/views/index.js:53:1:55:1 | functio ... ource\\n} | content-type | application/json | +| lib/views/index.js:56:1:58:1 | functio ... ition\\n} | content-type | application/json | +| lib/views/index.js:60:1:70:1 | functio ... ext.'\\n} | content-type | application/json | +| lib/views/index.js:65:14:65:87 | reply.h ... /html') | content-type | text/html | +| lib/views/index.js:67:14:67:92 | reply.h ... ain' }) | content-type | text/plain | +| lib/views/index.js:72:1:80:1 | functio ... ext.'\\n} | content-type | application/json | +| lib/views/index.js:77:14:77:92 | reply.h ... ain' }) | content-type | text/plain | +| lib/views/index.js:82:1:84:1 | functio ... !xss\\n} | content-type | application/json | +| lib/views/index.js:86:1:91:1 | functio ... ition\\n} | content-type | application/json | +| lib/views/index.js:93:1:98:1 | functio ... !xss\\n} | content-type | application/json | +| lib/views/index.js:100:1:109:1 | functio ... n\\n }\\n} | content-type | application/json | +| lib/views/index.js:107:12:107:86 | reply(m ... tml' }) | content-type | text/html | +| lib/views/index.js:111:1:115:1 | functio ... \\n })\\n} | content-type | application/json | +test5 +| lib/views/index.js:23:1:26:1 | functio ... tned)\\n} | lib/views/index.js:23:1:26:1 | functio ... tned)\\n} | content-type | +| lib/views/index.js:28:1:39:1 | functio ... eBody\\n} | lib/views/index.js:28:1:39:1 | functio ... eBody\\n} | content-type | +| lib/views/index.js:41:1:51:1 | functio ... \\n })\\n} | lib/views/index.js:41:1:51:1 | functio ... \\n })\\n} | content-type | +| lib/views/index.js:41:1:51:1 | functio ... \\n })\\n} | lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-credentials | +| lib/views/index.js:41:1:51:1 | functio ... \\n })\\n} | lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-headers | +| lib/views/index.js:41:1:51:1 | functio ... \\n })\\n} | lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-methods | +| lib/views/index.js:41:1:51:1 | functio ... \\n })\\n} | lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-origin | +| lib/views/index.js:41:1:51:1 | functio ... \\n })\\n} | lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | content-type | +| lib/views/index.js:53:1:55:1 | functio ... ource\\n} | lib/views/index.js:53:1:55:1 | functio ... ource\\n} | content-type | +| lib/views/index.js:56:1:58:1 | functio ... ition\\n} | lib/views/index.js:56:1:58:1 | functio ... ition\\n} | content-type | +| lib/views/index.js:60:1:70:1 | functio ... ext.'\\n} | lib/views/index.js:60:1:70:1 | functio ... ext.'\\n} | content-type | +| lib/views/index.js:60:1:70:1 | functio ... ext.'\\n} | lib/views/index.js:65:14:65:87 | reply.h ... /html') | content-type | +| lib/views/index.js:60:1:70:1 | functio ... ext.'\\n} | lib/views/index.js:67:14:67:92 | reply.h ... ain' }) | content-type | +| lib/views/index.js:72:1:80:1 | functio ... ext.'\\n} | lib/views/index.js:72:1:80:1 | functio ... ext.'\\n} | content-type | +| lib/views/index.js:72:1:80:1 | functio ... ext.'\\n} | lib/views/index.js:77:14:77:92 | reply.h ... ain' }) | content-type | +| lib/views/index.js:82:1:84:1 | functio ... !xss\\n} | lib/views/index.js:82:1:84:1 | functio ... !xss\\n} | content-type | +| lib/views/index.js:86:1:91:1 | functio ... ition\\n} | lib/views/index.js:86:1:91:1 | functio ... ition\\n} | content-type | +| lib/views/index.js:86:1:91:1 | functio ... ition\\n} | lib/views/index.js:90:10:90:55 | reply(m ... sage }) | npm-notice | +| lib/views/index.js:93:1:98:1 | functio ... !xss\\n} | lib/views/index.js:93:1:98:1 | functio ... !xss\\n} | content-type | +| lib/views/index.js:100:1:109:1 | functio ... n\\n }\\n} | lib/views/index.js:100:1:109:1 | functio ... n\\n }\\n} | content-type | +| lib/views/index.js:100:1:109:1 | functio ... n\\n }\\n} | lib/views/index.js:105:12:105:57 | reply(m ... sage }) | npm-notice | +| lib/views/index.js:100:1:109:1 | functio ... n\\n }\\n} | lib/views/index.js:107:12:107:86 | reply(m ... tml' }) | content-type | +| lib/views/index.js:100:1:109:1 | functio ... n\\n }\\n} | lib/views/index.js:107:12:107:86 | reply(m ... tml' }) | npm-notice | +| lib/views/index.js:111:1:115:1 | functio ... \\n })\\n} | lib/views/index.js:111:1:115:1 | functio ... \\n })\\n} | content-type | passingPositiveTests | PASSED | candidateHandler | lib/views/index.js:82:32:82:56 | // test ... Handler | | PASSED | cleartextStorageSink | lib/views/index.js:57:115:57:193 | // test ... inition | | PASSED | cookieDefinition | lib/views/index.js:57:115:57:193 | // test ... inition | -| PASSED | corsMiconfigurationSink | lib/views/index.js:45:41:45:72 | // test ... ionSink | +| PASSED | corsMiconfigurationSink | lib/views/index.js:45:41:45:90 | // test ... inition | | PASSED | handler | lib/views/index.js:23:40:23:55 | // test: handler | | PASSED | handler | lib/views/index.js:28:35:28:50 | // test: handler | | PASSED | handler | lib/views/index.js:41:31:41:46 | // test: handler | @@ -13,11 +93,19 @@ passingPositiveTests | PASSED | handler | lib/views/index.js:86:32:86:47 | // test: handler | | PASSED | handler | lib/views/index.js:93:32:93:47 | // test: handler | | PASSED | handler | lib/views/index.js:100:32:100:47 | // test: handler | +| PASSED | headerDefinition | lib/views/index.js:44:34:44:58 | // test ... inition | +| PASSED | headerDefinition | lib/views/index.js:45:41:45:90 | // test ... inition | +| PASSED | headerDefinition | lib/views/index.js:46:102:46:126 | // test ... inition | +| PASSED | headerDefinition | lib/views/index.js:47:72:47:96 | // test ... inition | +| PASSED | headerDefinition | lib/views/index.js:48:48:48:71 | //test: ... inition | | PASSED | headerDefinition | lib/views/index.js:65:89:65:159 | // test ... inition | -| PASSED | headerDefinition | lib/views/index.js:67:94:67:166 | // test ... inition | +| PASSED | headerDefinition | lib/views/index.js:67:94:67:184 | // test ... inition | | PASSED | headerDefinition | lib/views/index.js:77:94:77:166 | // test ... inition | +| PASSED | headerDefinition | lib/views/index.js:90:57:90:121 | // test ... inition | +| PASSED | headerDefinition | lib/views/index.js:105:59:105:123 | // test ... inition | | PASSED | headerDefinition | lib/views/index.js:107:88:107:150 | // test ... inition | -| PASSED | redirectSink | lib/views/index.js:54:54:54:106 | // test ... ureSink | +| PASSED | redirectSink | lib/views/index.js:54:54:54:82 | // test ... source | +| PASSED | responseBody | lib/views/index.js:38:61:38:136 | // test ... nseBody | | PASSED | source | lib/views/index.js:24:56:24:70 | // test: source | | PASSED | source | lib/views/index.js:29:28:29:42 | // test: source | | PASSED | source | lib/views/index.js:30:28:30:42 | // test: source | @@ -27,14 +115,14 @@ passingPositiveTests | PASSED | source | lib/views/index.js:34:17:34:31 | // test: source | | PASSED | source | lib/views/index.js:35:32:35:46 | // test: source | | PASSED | source | lib/views/index.js:36:32:36:46 | // test: source | -| PASSED | source | lib/views/index.js:38:61:38:122 | // test ... ureSink | +| PASSED | source | lib/views/index.js:38:61:38:136 | // test ... nseBody | | PASSED | source | lib/views/index.js:42:24:42:38 | // test: source | | PASSED | source | lib/views/index.js:43:39:43:91 | // test ... nk, xss | -| PASSED | source | lib/views/index.js:54:54:54:106 | // test ... ureSink | +| PASSED | source | lib/views/index.js:54:54:54:82 | // test ... source | | PASSED | source | lib/views/index.js:57:115:57:193 | // test ... inition | | PASSED | source | lib/views/index.js:63:41:63:79 | // test ... ureSink | | PASSED | source | lib/views/index.js:65:89:65:159 | // test ... inition | -| PASSED | source | lib/views/index.js:67:94:67:166 | // test ... inition | +| PASSED | source | lib/views/index.js:67:94:67:184 | // test ... inition | | PASSED | source | lib/views/index.js:75:41:75:79 | // test ... ureSink | | PASSED | source | lib/views/index.js:77:94:77:166 | // test ... inition | | PASSED | source | lib/views/index.js:83:49:83:103 | // test ... k, !xss | @@ -42,22 +130,21 @@ passingPositiveTests | PASSED | source | lib/views/index.js:94:25:94:39 | // test: source | | PASSED | source | lib/views/index.js:101:25:101:39 | // test: source | | PASSED | source | lib/views/index.js:112:34:112:72 | // test ... ureSink | -| PASSED | stackTraceExposureSink | lib/views/index.js:38:61:38:122 | // test ... ureSink | +| PASSED | stackTraceExposureSink | lib/views/index.js:38:61:38:136 | // test ... nseBody | | PASSED | stackTraceExposureSink | lib/views/index.js:43:39:43:91 | // test ... nk, xss | -| PASSED | stackTraceExposureSink | lib/views/index.js:54:54:54:106 | // test ... ureSink | | PASSED | stackTraceExposureSink | lib/views/index.js:57:115:57:193 | // test ... inition | | PASSED | stackTraceExposureSink | lib/views/index.js:63:41:63:79 | // test ... ureSink | | PASSED | stackTraceExposureSink | lib/views/index.js:65:89:65:159 | // test ... inition | -| PASSED | stackTraceExposureSink | lib/views/index.js:67:94:67:166 | // test ... inition | +| PASSED | stackTraceExposureSink | lib/views/index.js:67:94:67:184 | // test ... inition | | PASSED | stackTraceExposureSink | lib/views/index.js:75:41:75:79 | // test ... ureSink | | PASSED | stackTraceExposureSink | lib/views/index.js:77:94:77:166 | // test ... inition | | PASSED | stackTraceExposureSink | lib/views/index.js:83:49:83:103 | // test ... k, !xss | -| PASSED | stackTraceExposureSink | lib/views/index.js:90:57:90:103 | // test ... k, !xss | +| PASSED | stackTraceExposureSink | lib/views/index.js:90:57:90:121 | // test ... inition | | PASSED | stackTraceExposureSink | lib/views/index.js:97:30:97:76 | // test ... k, !xss | -| PASSED | stackTraceExposureSink | lib/views/index.js:105:59:105:105 | // test ... k, !xss | +| PASSED | stackTraceExposureSink | lib/views/index.js:105:59:105:123 | // test ... inition | | PASSED | stackTraceExposureSink | lib/views/index.js:107:88:107:150 | // test ... inition | | PASSED | stackTraceExposureSink | lib/views/index.js:112:34:112:72 | // test ... ureSink | -| PASSED | templateInstantiation | lib/views/index.js:38:61:38:122 | // test ... ureSink | +| PASSED | templateInstantiation | lib/views/index.js:38:61:38:136 | // test ... nseBody | | PASSED | xss | lib/views/index.js:43:39:43:91 | // test ... nk, xss | | PASSED | xss | lib/views/index.js:65:89:65:159 | // test ... inition | | PASSED | xss | lib/views/index.js:107:88:107:150 | // test ... inition | @@ -66,16 +153,16 @@ passingPositiveTests | PASSED | xssSink | lib/views/index.js:107:88:107:150 | // test ... inition | failingPositiveTests passingNegativeTests -| PASSED | !xss | lib/views/index.js:67:94:67:166 | // test ... inition | +| PASSED | !xss | lib/views/index.js:67:94:67:184 | // test ... inition | | PASSED | !xss | lib/views/index.js:77:94:77:166 | // test ... inition | | PASSED | !xss | lib/views/index.js:83:49:83:103 | // test ... k, !xss | -| PASSED | !xss | lib/views/index.js:90:57:90:103 | // test ... k, !xss | +| PASSED | !xss | lib/views/index.js:90:57:90:121 | // test ... inition | | PASSED | !xss | lib/views/index.js:97:30:97:76 | // test ... k, !xss | -| PASSED | !xss | lib/views/index.js:105:59:105:105 | // test ... k, !xss | -| PASSED | !xssSink | lib/views/index.js:67:94:67:166 | // test ... inition | +| PASSED | !xss | lib/views/index.js:105:59:105:123 | // test ... inition | +| PASSED | !xssSink | lib/views/index.js:67:94:67:184 | // test ... inition | | PASSED | !xssSink | lib/views/index.js:77:94:77:166 | // test ... inition | | PASSED | !xssSink | lib/views/index.js:83:49:83:103 | // test ... k, !xss | -| PASSED | !xssSink | lib/views/index.js:90:57:90:103 | // test ... k, !xss | +| PASSED | !xssSink | lib/views/index.js:90:57:90:121 | // test ... inition | | PASSED | !xssSink | lib/views/index.js:97:30:97:76 | // test ... k, !xss | -| PASSED | !xssSink | lib/views/index.js:105:59:105:105 | // test ... k, !xss | +| PASSED | !xssSink | lib/views/index.js:105:59:105:123 | // test ... inition | failingNegativeTests diff --git a/javascript/ql/test/library-tests/frameworks/Spife/tests.ql b/javascript/ql/test/library-tests/frameworks/Spife/tests.ql index b52e3be5256..61225f55359 100644 --- a/javascript/ql/test/library-tests/frameworks/Spife/tests.ql +++ b/javascript/ql/test/library-tests/frameworks/Spife/tests.ql @@ -33,8 +33,14 @@ query predicate passingPositiveTests(string res, string expectation, InlineTest res = "PASSED" and t.hasPositiveTest(expectation) and ( + expectation = "responseBody" and + exists(Http::ResponseBody n | t.inNode(n)) + or expectation = "headerDefinition" and - exists(Http::HeaderDefinition n | t.inNode(n)) + exists(DataFlow::Node n, Http::ExplicitHeaderDefinition h | + t.inNode(n) and + h.definesHeaderValue(_, n) + ) or expectation = "cookieDefinition" and exists(Http::CookieDefinition n | t.inNode(n)) @@ -83,8 +89,14 @@ query predicate failingPositiveTests(string res, string expectation, InlineTest res = "FAILED" and t.hasPositiveTest(expectation) and ( + expectation = "responseBody" and + not exists(Http::ResponseBody n | t.inNode(n)) + or expectation = "headerDefinition" and - not exists(Http::HeaderDefinition n | t.inNode(n)) + not exists(DataFlow::Node n, Http::ExplicitHeaderDefinition h | + t.inNode(n) and + h.definesHeaderValue(_, n) + ) or expectation = "cookieDefinition" and not exists(Http::CookieDefinition n | t.inNode(n)) @@ -133,8 +145,14 @@ query predicate passingNegativeTests(string res, string expectation, InlineTest res = "PASSED" and t.hasNegativeTest(expectation) and ( + expectation = "!responseBody" and + not exists(Http::ResponseBody n | t.inNode(n)) + or expectation = "!headerDefinition" and - not exists(Http::HeaderDefinition n | t.inNode(n)) + not exists(DataFlow::Node n, Http::ExplicitHeaderDefinition h | + t.inNode(n) and + h.definesHeaderValue(_, n) + ) or expectation = "!cookieDefinition" and not exists(Http::CookieDefinition n | t.inNode(n)) @@ -183,8 +201,14 @@ query predicate failingNegativeTests(string res, string expectation, InlineTest res = "FAILED" and t.hasNegativeTest(expectation) and ( + expectation = "!responseBody" and + exists(Http::ResponseBody n | t.inNode(n)) + or expectation = "!headerDefinition" and - exists(Http::HeaderDefinition n | t.inNode(n)) + not exists(DataFlow::Node n, Http::ExplicitHeaderDefinition h | + t.inNode(n) and + h.definesHeaderValue(_, n) + ) or expectation = "!cookieDefinition" and exists(Http::CookieDefinition n | t.inNode(n)) From a80b691358ebed9f44a285fed4448341f39100aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz?= Date: Tue, 25 Oct 2022 11:44:45 +0200 Subject: [PATCH 0023/1420] Remove unnecessary TaggedTemplateEntryPoint --- .../semmle/javascript/frameworks/Spife.qll | 9 --- .../frameworks/Spife/tests.expected | 80 ------------------- 2 files changed, 89 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll b/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll index a97eb98dc29..f2e738d80b6 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Spife.qll @@ -9,15 +9,6 @@ import semmle.javascript.frameworks.HTTP * Provides classes for working with [Spife](https://github.com/npm/spife) applications. */ module Spife { - /** - * An API graph entry point ensuring all tagged template exprs are part of the API graph - */ - private class TaggedTemplateEntryPoint extends API::EntryPoint { - TaggedTemplateEntryPoint() { this = "TaggedTemplateEntryPoint" } - - override DataFlow::SourceNode getASource() { result.asExpr() instanceof TaggedTemplateExpr } - } - /** * A call to a Spife method that sets up a route. */ diff --git a/javascript/ql/test/library-tests/frameworks/Spife/tests.expected b/javascript/ql/test/library-tests/frameworks/Spife/tests.expected index dc4c3451ce0..1f7ecf26ef8 100644 --- a/javascript/ql/test/library-tests/frameworks/Spife/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/Spife/tests.expected @@ -1,83 +1,3 @@ -WARNING: Unused method getCredentialsHeader (tests.ql:70,26-46) -test0 -| lib/views/index.js:23:1:26:1 | functio ... tned)\\n} | content-type | application/json | -| lib/views/index.js:28:1:39:1 | functio ... eBody\\n} | content-type | application/json | -| lib/views/index.js:41:1:51:1 | functio ... \\n })\\n} | content-type | application/json | -| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-credentials | true | -| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-headers | Content-Type, Authorization, Content-Length, X-Requested-With | -| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-methods | GET, POST, PUT, DELETE, OPTIONS | -| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-origin | * | -| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | content-type | text/html | -| lib/views/index.js:53:1:55:1 | functio ... ource\\n} | content-type | application/json | -| lib/views/index.js:56:1:58:1 | functio ... ition\\n} | content-type | application/json | -| lib/views/index.js:60:1:70:1 | functio ... ext.'\\n} | content-type | application/json | -| lib/views/index.js:65:14:65:87 | reply.h ... /html') | content-type | text/html | -| lib/views/index.js:67:14:67:92 | reply.h ... ain' }) | content-type | text/plain | -| lib/views/index.js:72:1:80:1 | functio ... ext.'\\n} | content-type | application/json | -| lib/views/index.js:77:14:77:92 | reply.h ... ain' }) | content-type | text/plain | -| lib/views/index.js:82:1:84:1 | functio ... !xss\\n} | content-type | application/json | -| lib/views/index.js:86:1:91:1 | functio ... ition\\n} | content-type | application/json | -| lib/views/index.js:93:1:98:1 | functio ... !xss\\n} | content-type | application/json | -| lib/views/index.js:100:1:109:1 | functio ... n\\n }\\n} | content-type | application/json | -| lib/views/index.js:107:12:107:86 | reply(m ... tml' }) | content-type | text/html | -| lib/views/index.js:111:1:115:1 | functio ... \\n })\\n} | content-type | application/json | -test -| lib/views/index.js:45:36:45:38 | "*" | -test2 -| lib/views/index.js:43:16:43:29 | req.query.name | -| lib/views/index.js:65:27:65:57 | '

' + ... '

' | -| lib/views/index.js:107:18:107:24 | message | -test3 -| lib/views/index.js:43:16:43:29 | req.query.name | -| lib/views/index.js:65:27:65:57 | '

' + ... '

' | -| lib/views/index.js:107:18:107:24 | message | -test4 -| lib/views/index.js:23:1:26:1 | functio ... tned)\\n} | content-type | application/json | -| lib/views/index.js:28:1:39:1 | functio ... eBody\\n} | content-type | application/json | -| lib/views/index.js:41:1:51:1 | functio ... \\n })\\n} | content-type | application/json | -| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-credentials | true | -| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-headers | Content-Type, Authorization, Content-Length, X-Requested-With | -| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-methods | GET, POST, PUT, DELETE, OPTIONS | -| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-origin | * | -| lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | content-type | text/html | -| lib/views/index.js:53:1:55:1 | functio ... ource\\n} | content-type | application/json | -| lib/views/index.js:56:1:58:1 | functio ... ition\\n} | content-type | application/json | -| lib/views/index.js:60:1:70:1 | functio ... ext.'\\n} | content-type | application/json | -| lib/views/index.js:65:14:65:87 | reply.h ... /html') | content-type | text/html | -| lib/views/index.js:67:14:67:92 | reply.h ... ain' }) | content-type | text/plain | -| lib/views/index.js:72:1:80:1 | functio ... ext.'\\n} | content-type | application/json | -| lib/views/index.js:77:14:77:92 | reply.h ... ain' }) | content-type | text/plain | -| lib/views/index.js:82:1:84:1 | functio ... !xss\\n} | content-type | application/json | -| lib/views/index.js:86:1:91:1 | functio ... ition\\n} | content-type | application/json | -| lib/views/index.js:93:1:98:1 | functio ... !xss\\n} | content-type | application/json | -| lib/views/index.js:100:1:109:1 | functio ... n\\n }\\n} | content-type | application/json | -| lib/views/index.js:107:12:107:86 | reply(m ... tml' }) | content-type | text/html | -| lib/views/index.js:111:1:115:1 | functio ... \\n })\\n} | content-type | application/json | -test5 -| lib/views/index.js:23:1:26:1 | functio ... tned)\\n} | lib/views/index.js:23:1:26:1 | functio ... tned)\\n} | content-type | -| lib/views/index.js:28:1:39:1 | functio ... eBody\\n} | lib/views/index.js:28:1:39:1 | functio ... eBody\\n} | content-type | -| lib/views/index.js:41:1:51:1 | functio ... \\n })\\n} | lib/views/index.js:41:1:51:1 | functio ... \\n })\\n} | content-type | -| lib/views/index.js:41:1:51:1 | functio ... \\n })\\n} | lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-credentials | -| lib/views/index.js:41:1:51:1 | functio ... \\n })\\n} | lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-headers | -| lib/views/index.js:41:1:51:1 | functio ... \\n })\\n} | lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-methods | -| lib/views/index.js:41:1:51:1 | functio ... \\n })\\n} | lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | access-control-allow-origin | -| lib/views/index.js:41:1:51:1 | functio ... \\n })\\n} | lib/views/index.js:43:10:50:4 | reply(r ... n\\n\\n }) | content-type | -| lib/views/index.js:53:1:55:1 | functio ... ource\\n} | lib/views/index.js:53:1:55:1 | functio ... ource\\n} | content-type | -| lib/views/index.js:56:1:58:1 | functio ... ition\\n} | lib/views/index.js:56:1:58:1 | functio ... ition\\n} | content-type | -| lib/views/index.js:60:1:70:1 | functio ... ext.'\\n} | lib/views/index.js:60:1:70:1 | functio ... ext.'\\n} | content-type | -| lib/views/index.js:60:1:70:1 | functio ... ext.'\\n} | lib/views/index.js:65:14:65:87 | reply.h ... /html') | content-type | -| lib/views/index.js:60:1:70:1 | functio ... ext.'\\n} | lib/views/index.js:67:14:67:92 | reply.h ... ain' }) | content-type | -| lib/views/index.js:72:1:80:1 | functio ... ext.'\\n} | lib/views/index.js:72:1:80:1 | functio ... ext.'\\n} | content-type | -| lib/views/index.js:72:1:80:1 | functio ... ext.'\\n} | lib/views/index.js:77:14:77:92 | reply.h ... ain' }) | content-type | -| lib/views/index.js:82:1:84:1 | functio ... !xss\\n} | lib/views/index.js:82:1:84:1 | functio ... !xss\\n} | content-type | -| lib/views/index.js:86:1:91:1 | functio ... ition\\n} | lib/views/index.js:86:1:91:1 | functio ... ition\\n} | content-type | -| lib/views/index.js:86:1:91:1 | functio ... ition\\n} | lib/views/index.js:90:10:90:55 | reply(m ... sage }) | npm-notice | -| lib/views/index.js:93:1:98:1 | functio ... !xss\\n} | lib/views/index.js:93:1:98:1 | functio ... !xss\\n} | content-type | -| lib/views/index.js:100:1:109:1 | functio ... n\\n }\\n} | lib/views/index.js:100:1:109:1 | functio ... n\\n }\\n} | content-type | -| lib/views/index.js:100:1:109:1 | functio ... n\\n }\\n} | lib/views/index.js:105:12:105:57 | reply(m ... sage }) | npm-notice | -| lib/views/index.js:100:1:109:1 | functio ... n\\n }\\n} | lib/views/index.js:107:12:107:86 | reply(m ... tml' }) | content-type | -| lib/views/index.js:100:1:109:1 | functio ... n\\n }\\n} | lib/views/index.js:107:12:107:86 | reply(m ... tml' }) | npm-notice | -| lib/views/index.js:111:1:115:1 | functio ... \\n })\\n} | lib/views/index.js:111:1:115:1 | functio ... \\n })\\n} | content-type | passingPositiveTests | PASSED | candidateHandler | lib/views/index.js:82:32:82:56 | // test ... Handler | | PASSED | cleartextStorageSink | lib/views/index.js:57:115:57:193 | // test ... inition | From 9830d2bebcc21f8264c38499d1238b5d402dadbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz?= Date: Tue, 25 Oct 2022 12:53:44 +0200 Subject: [PATCH 0024/1420] Format Restify.qll --- javascript/ql/lib/semmle/javascript/frameworks/Restify.qll | 2 -- 1 file changed, 2 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll b/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll index 10c5378e91e..a6da26f8b0b 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll @@ -436,7 +436,6 @@ module Restify { */ private class ContentTypeRouteHandlerHeader extends Http::ImplicitHeaderDefinition, DataFlow::FunctionNode instanceof RouteHandler { - override predicate defines(string headerName, string headerValue) { headerName = "content-type" and headerValue = "application/json" } @@ -454,7 +453,6 @@ module Restify { } private class RoutingTreeSetup extends Routing::RouteSetup::MethodCall instanceof RouteSetup { - override string getRelativePath() { not this.getMethodName() = ["use", "pre", "param", "on"] and // do not treat parameter name as a path result = this.getArgument(0).getStringValue() From 39081e9c1c6c869401741674f43e535a88c4a9aa Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 31 May 2022 00:35:48 +0200 Subject: [PATCH 0025/1420] Python: Fix staticmethod datamodel test --- .../dataflow/coverage/dataflow.expected | 35 +++++++++++++------ .../dataflow/coverage/datamodel.py | 9 +++++ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected index c7a0294d66a..9321d941ad8 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected @@ -4,6 +4,7 @@ edges | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:38:6:38:17 | ControlFlowNode for f() | | datamodel.py:44:22:44:22 | ControlFlowNode for x | datamodel.py:46:16:46:16 | ControlFlowNode for x | | datamodel.py:49:26:49:26 | ControlFlowNode for x | datamodel.py:50:16:50:16 | ControlFlowNode for x | +| datamodel.py:53:22:53:22 | ControlFlowNode for x | datamodel.py:54:16:54:16 | ControlFlowNode for x | | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x | | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | | datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x | @@ -12,10 +13,14 @@ edges | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x | | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | -| datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | -| datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | -| datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | -| datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | +| datamodel.py:88:21:88:26 | ControlFlowNode for SOURCE | datamodel.py:53:22:53:22 | ControlFlowNode for x | +| datamodel.py:88:21:88:26 | ControlFlowNode for SOURCE | datamodel.py:88:6:88:27 | ControlFlowNode for Attribute() | +| datamodel.py:89:21:89:26 | ControlFlowNode for SOURCE | datamodel.py:53:22:53:22 | ControlFlowNode for x | +| datamodel.py:89:21:89:26 | ControlFlowNode for SOURCE | datamodel.py:89:6:89:27 | ControlFlowNode for Attribute() | +| datamodel.py:161:5:161:8 | [post store] ControlFlowNode for self [Attribute b] | datamodel.py:164:14:164:25 | ControlFlowNode for Customized() [Attribute b] | +| datamodel.py:161:14:161:19 | ControlFlowNode for SOURCE | datamodel.py:161:5:161:8 | [post store] ControlFlowNode for self [Attribute b] | +| datamodel.py:164:14:164:25 | ControlFlowNode for Customized() [Attribute b] | datamodel.py:168:6:168:15 | ControlFlowNode for customized [Attribute b] | +| datamodel.py:168:6:168:15 | ControlFlowNode for customized [Attribute b] | datamodel.py:168:6:168:17 | ControlFlowNode for Attribute | | test.py:42:10:42:26 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:43:9:43:9 | ControlFlowNode for x [Tuple element at index 1] | | test.py:42:21:42:26 | ControlFlowNode for SOURCE | test.py:42:10:42:26 | ControlFlowNode for Tuple [Tuple element at index 1] | | test.py:43:9:43:9 | ControlFlowNode for x [Tuple element at index 1] | test.py:43:9:43:12 | ControlFlowNode for Subscript | @@ -396,6 +401,8 @@ nodes | datamodel.py:46:16:46:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | | datamodel.py:49:26:49:26 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | | datamodel.py:50:16:50:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| datamodel.py:53:22:53:22 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| datamodel.py:54:16:54:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | @@ -404,11 +411,15 @@ nodes | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | semmle.label | [post store] ControlFlowNode for self [Attribute b] | -| datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | semmle.label | ControlFlowNode for Customized() [Attribute b] | -| datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | semmle.label | ControlFlowNode for customized [Attribute b] | -| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| datamodel.py:88:6:88:27 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| datamodel.py:88:21:88:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| datamodel.py:89:6:89:27 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| datamodel.py:89:21:89:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| datamodel.py:161:5:161:8 | [post store] ControlFlowNode for self [Attribute b] | semmle.label | [post store] ControlFlowNode for self [Attribute b] | +| datamodel.py:161:14:161:19 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| datamodel.py:164:14:164:25 | ControlFlowNode for Customized() [Attribute b] | semmle.label | ControlFlowNode for Customized() [Attribute b] | +| datamodel.py:168:6:168:15 | ControlFlowNode for customized [Attribute b] | semmle.label | ControlFlowNode for customized [Attribute b] | +| datamodel.py:168:6:168:17 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | test.py:42:10:42:26 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | | test.py:42:21:42:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:43:9:43:9 | ControlFlowNode for x [Tuple element at index 1] | semmle.label | ControlFlowNode for x [Tuple element at index 1] | @@ -845,6 +856,8 @@ subpaths | datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x | datamodel.py:46:16:46:16 | ControlFlowNode for x | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x | datamodel.py:50:16:50:16 | ControlFlowNode for x | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x | datamodel.py:50:16:50:16 | ControlFlowNode for x | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | +| datamodel.py:88:21:88:26 | ControlFlowNode for SOURCE | datamodel.py:53:22:53:22 | ControlFlowNode for x | datamodel.py:54:16:54:16 | ControlFlowNode for x | datamodel.py:88:6:88:27 | ControlFlowNode for Attribute() | +| datamodel.py:89:21:89:26 | ControlFlowNode for SOURCE | datamodel.py:53:22:53:22 | ControlFlowNode for x | datamodel.py:54:16:54:16 | ControlFlowNode for x | datamodel.py:89:6:89:27 | ControlFlowNode for Attribute() | | test.py:380:28:380:33 | ControlFlowNode for SOURCE | test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b | test.py:380:10:380:34 | ControlFlowNode for second() | | test.py:388:30:388:35 | ControlFlowNode for SOURCE | test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b | test.py:388:10:388:36 | ControlFlowNode for second() | | test.py:396:10:396:43 | KwUnpacked b | test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b | test.py:396:10:396:43 | ControlFlowNode for second() | @@ -862,7 +875,9 @@ subpaths | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | Flow found | | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | Flow found | | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | Flow found | -| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | Flow found | +| datamodel.py:88:6:88:27 | ControlFlowNode for Attribute() | datamodel.py:88:21:88:26 | ControlFlowNode for SOURCE | datamodel.py:88:6:88:27 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:89:6:89:27 | ControlFlowNode for Attribute() | datamodel.py:89:21:89:26 | ControlFlowNode for SOURCE | datamodel.py:89:6:89:27 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:168:6:168:17 | ControlFlowNode for Attribute | datamodel.py:161:14:161:19 | ControlFlowNode for SOURCE | datamodel.py:168:6:168:17 | ControlFlowNode for Attribute | Flow found | | test.py:44:10:44:10 | ControlFlowNode for y | test.py:42:21:42:26 | ControlFlowNode for SOURCE | test.py:44:10:44:10 | ControlFlowNode for y | Flow found | | test.py:56:10:56:10 | ControlFlowNode for x | test.py:55:9:55:14 | ControlFlowNode for SOURCE | test.py:56:10:56:10 | ControlFlowNode for x | Flow found | | test.py:62:10:62:10 | ControlFlowNode for x | test.py:61:9:61:16 | ControlFlowNode for Str | test.py:62:10:62:10 | ControlFlowNode for x | Flow found | diff --git a/python/ql/test/experimental/dataflow/coverage/datamodel.py b/python/ql/test/experimental/dataflow/coverage/datamodel.py index f165e3d67e1..55a21410368 100644 --- a/python/ql/test/experimental/dataflow/coverage/datamodel.py +++ b/python/ql/test/experimental/dataflow/coverage/datamodel.py @@ -81,6 +81,15 @@ SINK(c.classmethod(SOURCE)) #$ flow="SOURCE -> c.classmethod(..)" SINK(C.classmethod(SOURCE)) #$ flow="SOURCE -> C.classmethod(..)" SINK(c_func_obj(C, SOURCE)) #$ MISSING: flow="SOURCE -> c_func_obj(..)" +# When an instance method object is created by retrieving a class method object from a class or instance, its __self__ attribute is the class itself, and its __func__ attribute is the function object underlying the class method. +s_func_obj = C.staticmethod.__func__ + +# When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function. +SINK(c.staticmethod(SOURCE)) #$ flow="SOURCE -> c.staticmethod(..)" +SINK(C.staticmethod(SOURCE)) #$ flow="SOURCE -> C.staticmethod(..)" +SINK(s_func_obj(SOURCE)) #$ MISSING: flow="SOURCE -> s_func_obj(..)" + + # Generator functions # A function or method which uses the yield statement (see section The yield statement) is called a generator function. Such a function, when called, always returns an iterator object which can be used to execute the body of the function: calling the iterator’s iterator.__next__() method will cause the function to execute until it provides a value using the yield statement. When the function executes a return statement or falls off the end, a StopIteration exception is raised and the iterator will have reached the end of the set of values to be returned. def gen(x, count): From 609a4cfd42dccf4728eefcd973022fa2b12eacfc Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 22 Jun 2022 14:42:22 +0200 Subject: [PATCH 0026/1420] Python: validate tests in `datamodel.py` And adopt argument passing tests as well. turns out that `C.staticmethod.__func__` doesn't actually work :O --- .../dataflow/coverage/datamodel.py | 88 +++++++++++++------ .../test/experimental/dataflow/validTest.py | 1 + 2 files changed, 62 insertions(+), 27 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/datamodel.py b/python/ql/test/experimental/dataflow/coverage/datamodel.py index 55a21410368..364dbb299d7 100644 --- a/python/ql/test/experimental/dataflow/coverage/datamodel.py +++ b/python/ql/test/experimental/dataflow/coverage/datamodel.py @@ -8,15 +8,30 @@ # Intended sources should be the variable `SOURCE` and intended sinks should be # arguments to the function `SINK` (see python/ql/test/experimental/dataflow/testConfig.qll). +import sys +import os +import functools + +sys.path.append(os.path.dirname(os.path.dirname((__file__)))) +from testlib import expects + # These are defined so that we can evaluate the test code. NONSOURCE = "not a source" SOURCE = "source" +arg1 = "source1" +arg2 = "source2" +arg3 = "source3" +arg4 = "source4" +arg5 = "source5" +arg6 = "source6" +arg7 = "source7" + def is_source(x): return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j -def SINK(x): - if is_source(x): +def SINK(x, expected=SOURCE): + if is_source(x) or x == expected: print("OK") else: print("Unexpected flow", x) @@ -27,6 +42,14 @@ def SINK_F(x): else: print("OK") +SINK1 = functools.partial(SINK, expected=arg1) +SINK2 = functools.partial(SINK, expected=arg2) +SINK3 = functools.partial(SINK, expected=arg3) +SINK4 = functools.partial(SINK, expected=arg4) +SINK5 = functools.partial(SINK, expected=arg5) +SINK6 = functools.partial(SINK, expected=arg6) +SINK7 = functools.partial(SINK, expected=arg7) + # Callable types # These are the types to which the function call operation (see section Calls) can be applied: @@ -41,17 +64,19 @@ SINK(f(SOURCE, 3)) #$ flow="SOURCE -> f(..)" # An instance method object combines a class, a class instance and any callable object (normally a user-defined function). class C(object): - def method(self, x, cls): - assert cls is self.__class__ - return x + def method(self, x, y): + SINK1(x) + SINK2(y) @classmethod - def classmethod(cls, x): - return x + def classmethod(cls, x, y): + SINK1(x) + SINK2(y) @staticmethod - def staticmethod(x): - return x + def staticmethod(x, y): + SINK1(x) + SINK2(y) def gen(self, x, count): n = count @@ -64,30 +89,39 @@ class C(object): c = C() -# When an instance method object is created by retrieving a user-defined function object from a class via one of its instances, its __self__ attribute is the instance, and the method object is said to be bound. The new method’s __func__ attribute is the original function object. -func_obj = c.method.__func__ +@expects(6) +def test_method_call(): + # When an instance method object is created by retrieving a user-defined function object from a class via one of its instances, its __self__ attribute is the instance, and the method object is said to be bound. The new method’s __func__ attribute is the original function object. + func_obj = c.method.__func__ -# When an instance method object is called, the underlying function (__func__) is called, inserting the class instance (__self__) in front of the argument list. For instance, when C is a class which contains a definition for a function f(), and x is an instance of C, calling x.f(1) is equivalent to calling C.f(x, 1). -SINK(c.method(SOURCE, C)) #$ flow="SOURCE -> c.method(..)" -SINK(C.method(c, SOURCE, C)) #$ flow="SOURCE -> C.method(..)" -SINK(func_obj(c, SOURCE, C)) #$ MISSING: flow="SOURCE -> func_obj(..)" + # When an instance method object is called, the underlying function (__func__) is called, inserting the class instance (__self__) in front of the argument list. For instance, when C is a class which contains a definition for a function f(), and x is an instance of C, calling x.f(1) is equivalent to calling C.f(x, 1). + c.method(arg1, arg2) # $ func=C.method arg1 arg2 + C.method(c, arg1, arg2) # $ func=C.method arg1 arg2 + func_obj(c, arg1, arg2) # $ MISSING: func=C.method arg1 arg2 -# When an instance method object is created by retrieving a class method object from a class or instance, its __self__ attribute is the class itself, and its __func__ attribute is the function object underlying the class method. -c_func_obj = C.classmethod.__func__ +@expects(6) +def test_classmethod_call(): + # When an instance method object is created by retrieving a class method object from a class or instance, its __self__ attribute is the class itself, and its __func__ attribute is the function object underlying the class method. + c_func_obj = C.classmethod.__func__ -# When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function. -SINK(c.classmethod(SOURCE)) #$ flow="SOURCE -> c.classmethod(..)" -SINK(C.classmethod(SOURCE)) #$ flow="SOURCE -> C.classmethod(..)" -SINK(c_func_obj(C, SOURCE)) #$ MISSING: flow="SOURCE -> c_func_obj(..)" + # When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function. + c.classmethod(arg1, arg2) # $ func=C.classmethod arg1 arg2 + C.classmethod(arg1, arg2) # $ func=C.classmethod arg1 arg2 + c_func_obj(C, arg1, arg2) # $ MISSING: func=C.classmethod arg1 arg2 -# When an instance method object is created by retrieving a class method object from a class or instance, its __self__ attribute is the class itself, and its __func__ attribute is the function object underlying the class method. -s_func_obj = C.staticmethod.__func__ -# When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function. -SINK(c.staticmethod(SOURCE)) #$ flow="SOURCE -> c.staticmethod(..)" -SINK(C.staticmethod(SOURCE)) #$ flow="SOURCE -> C.staticmethod(..)" -SINK(s_func_obj(SOURCE)) #$ MISSING: flow="SOURCE -> s_func_obj(..)" +@expects(5) +def test_staticmethod_call(): + # staticmethods does not have a __func__ attribute + try: + C.staticmethod.__func__ + except AttributeError: + print("OK") + + # When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function. + c.staticmethod(arg1, arg2) # $ func=C.staticmethod arg1 arg2 + C.staticmethod(arg1, arg2) # $ func=C.staticmethod arg1 arg2 # Generator functions diff --git a/python/ql/test/experimental/dataflow/validTest.py b/python/ql/test/experimental/dataflow/validTest.py index 86336af05ba..edfe685c266 100644 --- a/python/ql/test/experimental/dataflow/validTest.py +++ b/python/ql/test/experimental/dataflow/validTest.py @@ -55,6 +55,7 @@ if __name__ == "__main__": check_tests_valid("coverage.classes") check_tests_valid("coverage.test") check_tests_valid("coverage.argumentPassing") + check_tests_valid("coverage.datamodel") check_tests_valid("variable-capture.in") check_tests_valid("variable-capture.nonlocal") check_tests_valid("variable-capture.dict") From 7d8c0c663f39ef8d2e15ea894b4723105810d1ec Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 3 Jun 2022 18:33:44 +0200 Subject: [PATCH 0027/1420] Python: Remove `dataflow/coverage/dataflow.ql` The selected edges is covered by `NormalDataflowTest.ql` now... and reading the test-output changes in `edges` is just going to make commits larger while not providing any real value. --- .../dataflow/coverage/dataflow.expected | 985 ------------------ .../dataflow/coverage/dataflow.ql | 11 - 2 files changed, 996 deletions(-) delete mode 100644 python/ql/test/experimental/dataflow/coverage/dataflow.expected delete mode 100644 python/ql/test/experimental/dataflow/coverage/dataflow.ql diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected deleted file mode 100644 index 9321d941ad8..00000000000 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ /dev/null @@ -1,985 +0,0 @@ -edges -| datamodel.py:35:7:35:7 | ControlFlowNode for a | datamodel.py:36:10:36:10 | ControlFlowNode for a | -| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:35:7:35:7 | ControlFlowNode for a | -| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:38:6:38:17 | ControlFlowNode for f() | -| datamodel.py:44:22:44:22 | ControlFlowNode for x | datamodel.py:46:16:46:16 | ControlFlowNode for x | -| datamodel.py:49:26:49:26 | ControlFlowNode for x | datamodel.py:50:16:50:16 | ControlFlowNode for x | -| datamodel.py:53:22:53:22 | ControlFlowNode for x | datamodel.py:54:16:54:16 | ControlFlowNode for x | -| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x | -| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | -| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x | -| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | -| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x | -| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | -| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x | -| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | -| datamodel.py:88:21:88:26 | ControlFlowNode for SOURCE | datamodel.py:53:22:53:22 | ControlFlowNode for x | -| datamodel.py:88:21:88:26 | ControlFlowNode for SOURCE | datamodel.py:88:6:88:27 | ControlFlowNode for Attribute() | -| datamodel.py:89:21:89:26 | ControlFlowNode for SOURCE | datamodel.py:53:22:53:22 | ControlFlowNode for x | -| datamodel.py:89:21:89:26 | ControlFlowNode for SOURCE | datamodel.py:89:6:89:27 | ControlFlowNode for Attribute() | -| datamodel.py:161:5:161:8 | [post store] ControlFlowNode for self [Attribute b] | datamodel.py:164:14:164:25 | ControlFlowNode for Customized() [Attribute b] | -| datamodel.py:161:14:161:19 | ControlFlowNode for SOURCE | datamodel.py:161:5:161:8 | [post store] ControlFlowNode for self [Attribute b] | -| datamodel.py:164:14:164:25 | ControlFlowNode for Customized() [Attribute b] | datamodel.py:168:6:168:15 | ControlFlowNode for customized [Attribute b] | -| datamodel.py:168:6:168:15 | ControlFlowNode for customized [Attribute b] | datamodel.py:168:6:168:17 | ControlFlowNode for Attribute | -| test.py:42:10:42:26 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:43:9:43:9 | ControlFlowNode for x [Tuple element at index 1] | -| test.py:42:21:42:26 | ControlFlowNode for SOURCE | test.py:42:10:42:26 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:43:9:43:9 | ControlFlowNode for x [Tuple element at index 1] | test.py:43:9:43:12 | ControlFlowNode for Subscript | -| test.py:43:9:43:12 | ControlFlowNode for Subscript | test.py:44:10:44:10 | ControlFlowNode for y | -| test.py:55:9:55:14 | ControlFlowNode for SOURCE | test.py:56:10:56:10 | ControlFlowNode for x | -| test.py:61:9:61:16 | ControlFlowNode for Str | test.py:62:10:62:10 | ControlFlowNode for x | -| test.py:66:9:66:17 | ControlFlowNode for Str | test.py:67:10:67:10 | ControlFlowNode for x | -| test.py:71:9:71:10 | ControlFlowNode for IntegerLiteral | test.py:72:10:72:10 | ControlFlowNode for x | -| test.py:76:9:76:12 | ControlFlowNode for FloatLiteral | test.py:77:10:77:10 | ControlFlowNode for x | -| test.py:87:10:87:15 | ControlFlowNode for SOURCE | test.py:88:10:88:10 | ControlFlowNode for x | -| test.py:93:9:93:16 | ControlFlowNode for List [List element] | test.py:94:10:94:10 | ControlFlowNode for x [List element] | -| test.py:93:10:93:15 | ControlFlowNode for SOURCE | test.py:93:9:93:16 | ControlFlowNode for List [List element] | -| test.py:94:10:94:10 | ControlFlowNode for x [List element] | test.py:94:10:94:13 | ControlFlowNode for Subscript | -| test.py:103:9:103:37 | ControlFlowNode for ListComp [List element] | test.py:104:10:104:10 | ControlFlowNode for x [List element] | -| test.py:103:10:103:15 | ControlFlowNode for SOURCE | test.py:103:9:103:37 | ControlFlowNode for ListComp [List element] | -| test.py:104:10:104:10 | ControlFlowNode for x [List element] | test.py:104:10:104:13 | ControlFlowNode for Subscript | -| test.py:108:9:108:29 | ControlFlowNode for ListComp [List element] | test.py:109:10:109:10 | ControlFlowNode for x [List element] | -| test.py:108:10:108:10 | ControlFlowNode for y | test.py:108:9:108:29 | ControlFlowNode for ListComp [List element] | -| test.py:108:16:108:16 | SSA variable y | test.py:108:10:108:10 | ControlFlowNode for y | -| test.py:108:21:108:28 | ControlFlowNode for List [List element] | test.py:108:16:108:16 | SSA variable y | -| test.py:108:22:108:27 | ControlFlowNode for SOURCE | test.py:108:21:108:28 | ControlFlowNode for List [List element] | -| test.py:109:10:109:10 | ControlFlowNode for x [List element] | test.py:109:10:109:13 | ControlFlowNode for Subscript | -| test.py:113:9:113:16 | ControlFlowNode for List [List element] | test.py:114:21:114:21 | ControlFlowNode for l [List element] | -| test.py:113:10:113:15 | ControlFlowNode for SOURCE | test.py:113:9:113:16 | ControlFlowNode for List [List element] | -| test.py:114:9:114:22 | ControlFlowNode for ListComp [List element] | test.py:115:10:115:10 | ControlFlowNode for x [List element] | -| test.py:114:10:114:10 | ControlFlowNode for y | test.py:114:9:114:22 | ControlFlowNode for ListComp [List element] | -| test.py:114:16:114:16 | SSA variable y | test.py:114:10:114:10 | ControlFlowNode for y | -| test.py:114:21:114:21 | ControlFlowNode for l [List element] | test.py:114:16:114:16 | SSA variable y | -| test.py:115:10:115:10 | ControlFlowNode for x [List element] | test.py:115:10:115:13 | ControlFlowNode for Subscript | -| test.py:125:9:125:16 | ControlFlowNode for Set [Set element] | test.py:126:10:126:10 | ControlFlowNode for x [Set element] | -| test.py:125:10:125:15 | ControlFlowNode for SOURCE | test.py:125:9:125:16 | ControlFlowNode for Set [Set element] | -| test.py:126:10:126:10 | ControlFlowNode for x [Set element] | test.py:126:10:126:16 | ControlFlowNode for Attribute() | -| test.py:130:9:130:37 | ControlFlowNode for SetComp [Set element] | test.py:131:10:131:10 | ControlFlowNode for x [Set element] | -| test.py:130:10:130:15 | ControlFlowNode for SOURCE | test.py:130:9:130:37 | ControlFlowNode for SetComp [Set element] | -| test.py:131:10:131:10 | ControlFlowNode for x [Set element] | test.py:131:10:131:16 | ControlFlowNode for Attribute() | -| test.py:135:9:135:29 | ControlFlowNode for SetComp [Set element] | test.py:136:10:136:10 | ControlFlowNode for x [Set element] | -| test.py:135:10:135:10 | ControlFlowNode for y | test.py:135:9:135:29 | ControlFlowNode for SetComp [Set element] | -| test.py:135:16:135:16 | SSA variable y | test.py:135:10:135:10 | ControlFlowNode for y | -| test.py:135:21:135:28 | ControlFlowNode for List [List element] | test.py:135:16:135:16 | SSA variable y | -| test.py:135:22:135:27 | ControlFlowNode for SOURCE | test.py:135:21:135:28 | ControlFlowNode for List [List element] | -| test.py:136:10:136:10 | ControlFlowNode for x [Set element] | test.py:136:10:136:16 | ControlFlowNode for Attribute() | -| test.py:140:9:140:16 | ControlFlowNode for Set [Set element] | test.py:141:21:141:21 | ControlFlowNode for l [Set element] | -| test.py:140:10:140:15 | ControlFlowNode for SOURCE | test.py:140:9:140:16 | ControlFlowNode for Set [Set element] | -| test.py:141:9:141:22 | ControlFlowNode for SetComp [Set element] | test.py:142:10:142:10 | ControlFlowNode for x [Set element] | -| test.py:141:10:141:10 | ControlFlowNode for y | test.py:141:9:141:22 | ControlFlowNode for SetComp [Set element] | -| test.py:141:16:141:16 | SSA variable y | test.py:141:10:141:10 | ControlFlowNode for y | -| test.py:141:21:141:21 | ControlFlowNode for l [Set element] | test.py:141:16:141:16 | SSA variable y | -| test.py:142:10:142:10 | ControlFlowNode for x [Set element] | test.py:142:10:142:16 | ControlFlowNode for Attribute() | -| test.py:152:9:152:21 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:153:10:153:10 | ControlFlowNode for x [Dictionary element at key s] | -| test.py:152:15:152:20 | ControlFlowNode for SOURCE | test.py:152:9:152:21 | ControlFlowNode for Dict [Dictionary element at key s] | -| test.py:153:10:153:10 | ControlFlowNode for x [Dictionary element at key s] | test.py:153:10:153:15 | ControlFlowNode for Subscript | -| test.py:157:9:157:21 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:158:10:158:10 | ControlFlowNode for x [Dictionary element at key s] | -| test.py:157:15:157:20 | ControlFlowNode for SOURCE | test.py:157:9:157:21 | ControlFlowNode for Dict [Dictionary element at key s] | -| test.py:158:10:158:10 | ControlFlowNode for x [Dictionary element at key s] | test.py:158:10:158:19 | ControlFlowNode for Attribute() | -| test.py:183:9:183:42 | ControlFlowNode for ListComp [List element] | test.py:184:10:184:10 | ControlFlowNode for x [List element] | -| test.py:183:10:183:10 | ControlFlowNode for y | test.py:183:9:183:42 | ControlFlowNode for ListComp [List element] | -| test.py:183:16:183:16 | SSA variable z [List element] | test.py:183:41:183:41 | ControlFlowNode for z [List element] | -| test.py:183:21:183:30 | ControlFlowNode for List [List element, List element] | test.py:183:16:183:16 | SSA variable z [List element] | -| test.py:183:22:183:29 | ControlFlowNode for List [List element] | test.py:183:21:183:30 | ControlFlowNode for List [List element, List element] | -| test.py:183:23:183:28 | ControlFlowNode for SOURCE | test.py:183:22:183:29 | ControlFlowNode for List [List element] | -| test.py:183:36:183:36 | SSA variable y | test.py:183:10:183:10 | ControlFlowNode for y | -| test.py:183:41:183:41 | ControlFlowNode for z [List element] | test.py:183:36:183:36 | SSA variable y | -| test.py:184:10:184:10 | ControlFlowNode for x [List element] | test.py:184:10:184:13 | ControlFlowNode for Subscript | -| test.py:188:9:188:68 | ControlFlowNode for ListComp [List element] | test.py:189:10:189:10 | ControlFlowNode for x [List element] | -| test.py:188:10:188:10 | ControlFlowNode for y | test.py:188:9:188:68 | ControlFlowNode for ListComp [List element] | -| test.py:188:16:188:16 | SSA variable v [List element, List element, List element] | test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, List element] | -| test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, List element, List element] | test.py:188:16:188:16 | SSA variable v [List element, List element, List element] | -| test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, List element] | test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, List element, List element] | -| test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] | test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, List element] | -| test.py:188:24:188:31 | ControlFlowNode for List [List element] | test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] | -| test.py:188:25:188:30 | ControlFlowNode for SOURCE | test.py:188:24:188:31 | ControlFlowNode for List [List element] | -| test.py:188:40:188:40 | SSA variable u [List element, List element] | test.py:188:56:188:56 | ControlFlowNode for u [List element, List element] | -| test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, List element] | test.py:188:40:188:40 | SSA variable u [List element, List element] | -| test.py:188:51:188:51 | SSA variable z [List element] | test.py:188:67:188:67 | ControlFlowNode for z [List element] | -| test.py:188:56:188:56 | ControlFlowNode for u [List element, List element] | test.py:188:51:188:51 | SSA variable z [List element] | -| test.py:188:62:188:62 | SSA variable y | test.py:188:10:188:10 | ControlFlowNode for y | -| test.py:188:67:188:67 | ControlFlowNode for z [List element] | test.py:188:62:188:62 | SSA variable y | -| test.py:189:10:189:10 | ControlFlowNode for x [List element] | test.py:189:10:189:13 | ControlFlowNode for Subscript | -| test.py:199:9:199:42 | ControlFlowNode for ListComp [List element] | test.py:200:10:200:10 | ControlFlowNode for x [List element] | -| test.py:199:10:199:10 | ControlFlowNode for y | test.py:199:9:199:42 | ControlFlowNode for ListComp [List element] | -| test.py:199:16:199:16 | SSA variable y | test.py:199:10:199:10 | ControlFlowNode for y | -| test.py:199:22:199:22 | ControlFlowNode for z | test.py:199:22:199:40 | ControlFlowNode for GeneratorExp [List element] | -| test.py:199:22:199:40 | ControlFlowNode for GeneratorExp [List element] | test.py:199:16:199:16 | SSA variable y | -| test.py:199:28:199:28 | SSA variable z | test.py:199:22:199:22 | ControlFlowNode for z | -| test.py:199:33:199:40 | ControlFlowNode for List [List element] | test.py:199:28:199:28 | SSA variable z | -| test.py:199:34:199:39 | ControlFlowNode for SOURCE | test.py:199:33:199:40 | ControlFlowNode for List [List element] | -| test.py:200:10:200:10 | ControlFlowNode for x [List element] | test.py:200:10:200:13 | ControlFlowNode for Subscript | -| test.py:205:9:205:47 | ControlFlowNode for ListComp [List element] | test.py:206:10:206:10 | ControlFlowNode for x [List element] | -| test.py:205:10:205:10 | ControlFlowNode for a | test.py:205:9:205:47 | ControlFlowNode for ListComp [List element] | -| test.py:205:17:205:17 | SSA variable a | test.py:205:10:205:10 | ControlFlowNode for a | -| test.py:205:17:205:20 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:205:17:205:17 | SSA variable a | -| test.py:205:17:205:20 | IterableSequence [Tuple element at index 0] | test.py:205:17:205:20 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:205:26:205:46 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:205:17:205:20 | IterableSequence [Tuple element at index 0] | -| test.py:205:28:205:33 | ControlFlowNode for SOURCE | test.py:205:28:205:44 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:205:28:205:44 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:205:26:205:46 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:206:10:206:10 | ControlFlowNode for x [List element] | test.py:206:10:206:13 | ControlFlowNode for Subscript | -| test.py:210:9:210:51 | ControlFlowNode for ListComp [List element] | test.py:211:10:211:10 | ControlFlowNode for x [List element] | -| test.py:210:10:210:10 | ControlFlowNode for a [List element] | test.py:210:10:210:13 | ControlFlowNode for Subscript | -| test.py:210:10:210:13 | ControlFlowNode for Subscript | test.py:210:9:210:51 | ControlFlowNode for ListComp [List element] | -| test.py:210:20:210:21 | IterableElement | test.py:210:20:210:21 | SSA variable a [List element] | -| test.py:210:20:210:21 | SSA variable a [List element] | test.py:210:10:210:10 | ControlFlowNode for a [List element] | -| test.py:210:20:210:24 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:210:20:210:21 | IterableElement | -| test.py:210:20:210:24 | IterableSequence [Tuple element at index 0] | test.py:210:20:210:24 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:210:30:210:50 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:210:20:210:24 | IterableSequence [Tuple element at index 0] | -| test.py:210:32:210:37 | ControlFlowNode for SOURCE | test.py:210:32:210:48 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:210:32:210:48 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:210:30:210:50 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:211:10:211:10 | ControlFlowNode for x [List element] | test.py:211:10:211:13 | ControlFlowNode for Subscript | -| test.py:349:11:349:16 | ControlFlowNode for SOURCE | test.py:349:11:349:17 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:349:11:349:17 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:349:10:349:21 | ControlFlowNode for Subscript | -| test.py:353:10:353:17 | ControlFlowNode for List [List element] | test.py:353:10:353:20 | ControlFlowNode for Subscript | -| test.py:353:11:353:16 | ControlFlowNode for SOURCE | test.py:353:10:353:17 | ControlFlowNode for List [List element] | -| test.py:357:10:357:22 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:357:10:357:27 | ControlFlowNode for Subscript | -| test.py:357:16:357:21 | ControlFlowNode for SOURCE | test.py:357:10:357:22 | ControlFlowNode for Dict [Dictionary element at key s] | -| test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b | -| test.py:380:28:380:33 | ControlFlowNode for SOURCE | test.py:375:15:375:15 | ControlFlowNode for b | -| test.py:380:28:380:33 | ControlFlowNode for SOURCE | test.py:380:10:380:34 | ControlFlowNode for second() | -| test.py:388:30:388:35 | ControlFlowNode for SOURCE | test.py:375:15:375:15 | ControlFlowNode for b | -| test.py:388:30:388:35 | ControlFlowNode for SOURCE | test.py:388:10:388:36 | ControlFlowNode for second() | -| test.py:396:10:396:43 | KwUnpacked b | test.py:375:15:375:15 | ControlFlowNode for b | -| test.py:396:10:396:43 | KwUnpacked b | test.py:396:10:396:43 | ControlFlowNode for second() | -| test.py:396:30:396:42 | ControlFlowNode for Dict [Dictionary element at key b] | test.py:396:10:396:43 | KwUnpacked b | -| test.py:396:36:396:41 | ControlFlowNode for SOURCE | test.py:396:30:396:42 | ControlFlowNode for Dict [Dictionary element at key b] | -| test.py:399:21:399:21 | ControlFlowNode for b [Tuple element at index 0] | test.py:400:12:400:12 | ControlFlowNode for b [Tuple element at index 0] | -| test.py:400:12:400:12 | ControlFlowNode for b [Tuple element at index 0] | test.py:400:12:400:15 | ControlFlowNode for Subscript | -| test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:399:21:399:21 | ControlFlowNode for b [Tuple element at index 0] | -| test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() | -| test.py:404:33:404:38 | ControlFlowNode for SOURCE | test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | -| test.py:407:26:407:26 | ControlFlowNode for b [Dictionary element at key b] | test.py:408:12:408:12 | ControlFlowNode for b [Dictionary element at key b] | -| test.py:408:12:408:12 | ControlFlowNode for b [Dictionary element at key b] | test.py:408:12:408:17 | ControlFlowNode for Subscript | -| test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:407:26:407:26 | ControlFlowNode for b [Dictionary element at key b] | -| test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | -| test.py:412:39:412:44 | ControlFlowNode for SOURCE | test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | -| test.py:429:15:429:20 | ControlFlowNode for SOURCE | test.py:429:10:429:20 | ControlFlowNode for BoolExpr | -| test.py:434:16:434:21 | ControlFlowNode for SOURCE | test.py:434:10:434:21 | ControlFlowNode for BoolExpr | -| test.py:445:10:445:15 | ControlFlowNode for SOURCE | test.py:445:10:445:38 | ControlFlowNode for IfExp | -| test.py:453:34:453:39 | ControlFlowNode for SOURCE | test.py:453:10:453:39 | ControlFlowNode for IfExp | -| test.py:474:11:474:11 | ControlFlowNode for x | test.py:475:16:475:16 | ControlFlowNode for x | -| test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:474:11:474:11 | ControlFlowNode for x | -| test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:477:10:477:18 | ControlFlowNode for f() | -| test.py:481:19:481:19 | ControlFlowNode for b | test.py:482:16:482:16 | ControlFlowNode for b | -| test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:481:19:481:19 | ControlFlowNode for b | -| test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:484:10:484:34 | ControlFlowNode for second() | -| test.py:495:19:495:19 | ControlFlowNode for b | test.py:496:16:496:16 | ControlFlowNode for b | -| test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:495:19:495:19 | ControlFlowNode for b | -| test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:498:10:498:36 | ControlFlowNode for second() | -| test.py:509:19:509:19 | ControlFlowNode for b | test.py:510:16:510:16 | ControlFlowNode for b | -| test.py:512:10:512:43 | KwUnpacked b | test.py:509:19:509:19 | ControlFlowNode for b | -| test.py:512:10:512:43 | KwUnpacked b | test.py:512:10:512:43 | ControlFlowNode for second() | -| test.py:512:30:512:42 | ControlFlowNode for Dict [Dictionary element at key b] | test.py:512:10:512:43 | KwUnpacked b | -| test.py:512:36:512:41 | ControlFlowNode for SOURCE | test.py:512:30:512:42 | ControlFlowNode for Dict [Dictionary element at key b] | -| test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] | test.py:516:33:516:33 | ControlFlowNode for b [Tuple element at index 0] | -| test.py:516:33:516:33 | ControlFlowNode for b [Tuple element at index 0] | test.py:516:33:516:36 | ControlFlowNode for Subscript | -| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] | -| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | -| test.py:517:33:517:38 | ControlFlowNode for SOURCE | test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | -| test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] | test.py:521:38:521:38 | ControlFlowNode for b [Dictionary element at key b] | -| test.py:521:38:521:38 | ControlFlowNode for b [Dictionary element at key b] | test.py:521:38:521:43 | ControlFlowNode for Subscript | -| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] | -| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | -| test.py:522:39:522:44 | ControlFlowNode for SOURCE | test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | -| test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:536:10:536:10 | ControlFlowNode for a | -| test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:541:10:541:10 | ControlFlowNode for b | -| test.py:546:10:546:15 | ControlFlowNode for SOURCE | test.py:546:10:546:26 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:546:10:546:26 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:547:5:547:8 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:547:5:547:5 | SSA variable a | test.py:548:10:548:10 | ControlFlowNode for a | -| test.py:547:5:547:8 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:547:5:547:5 | SSA variable a | -| test.py:554:10:554:15 | ControlFlowNode for SOURCE | test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | -| test.py:554:19:554:35 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | -| test.py:554:30:554:35 | ControlFlowNode for SOURCE | test.py:554:19:554:35 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:555:5:555:5 | SSA variable a | test.py:556:10:556:10 | ControlFlowNode for a | -| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:555:5:555:5 | SSA variable a | -| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | test.py:555:9:555:12 | IterableSequence [Tuple element at index 1] | -| test.py:555:9:555:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:555:12:555:12 | SSA variable c | -| test.py:555:9:555:12 | IterableSequence [Tuple element at index 1] | test.py:555:9:555:12 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:555:12:555:12 | SSA variable c | test.py:558:10:558:10 | ControlFlowNode for c | -| test.py:563:9:563:33 | ControlFlowNode for List [List element, List element, List element, List element] | test.py:564:5:564:14 | IterableSequence [List element, List element, List element, List element] | -| test.py:563:10:563:21 | ControlFlowNode for List [List element, List element, List element] | test.py:563:9:563:33 | ControlFlowNode for List [List element, List element, List element, List element] | -| test.py:563:11:563:20 | ControlFlowNode for List [List element, List element] | test.py:563:10:563:21 | ControlFlowNode for List [List element, List element, List element] | -| test.py:563:12:563:19 | ControlFlowNode for List [List element] | test.py:563:11:563:20 | ControlFlowNode for List [List element, List element] | -| test.py:563:13:563:18 | ControlFlowNode for SOURCE | test.py:563:12:563:19 | ControlFlowNode for List [List element] | -| test.py:564:5:564:11 | ControlFlowNode for List [Tuple element at index 0, List element, List element] | test.py:564:6:564:10 | IterableSequence [List element, List element] | -| test.py:564:5:564:11 | IterableElement [List element, List element] | test.py:564:5:564:11 | ControlFlowNode for List [Tuple element at index 0, List element, List element] | -| test.py:564:5:564:11 | IterableSequence [List element, List element, List element] | test.py:564:5:564:11 | IterableElement [List element, List element] | -| test.py:564:5:564:14 | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | test.py:564:5:564:11 | IterableSequence [List element, List element, List element] | -| test.py:564:5:564:14 | IterableElement [List element, List element, List element] | test.py:564:5:564:14 | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | -| test.py:564:5:564:14 | IterableSequence [List element, List element, List element, List element] | test.py:564:5:564:14 | IterableElement [List element, List element, List element] | -| test.py:564:6:564:10 | ControlFlowNode for List [Tuple element at index 0, List element] | test.py:564:7:564:9 | IterableSequence [List element] | -| test.py:564:6:564:10 | IterableElement [List element] | test.py:564:6:564:10 | ControlFlowNode for List [Tuple element at index 0, List element] | -| test.py:564:6:564:10 | IterableSequence [List element, List element] | test.py:564:6:564:10 | IterableElement [List element] | -| test.py:564:7:564:9 | ControlFlowNode for List [Tuple element at index 0] | test.py:564:8:564:8 | SSA variable a | -| test.py:564:7:564:9 | IterableElement | test.py:564:7:564:9 | ControlFlowNode for List [Tuple element at index 0] | -| test.py:564:7:564:9 | IterableSequence [List element] | test.py:564:7:564:9 | IterableElement | -| test.py:564:8:564:8 | SSA variable a | test.py:565:10:565:10 | ControlFlowNode for a | -| test.py:571:10:571:15 | ControlFlowNode for SOURCE | test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:571:18:571:23 | ControlFlowNode for SOURCE | test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:572:5:572:5 | SSA variable a | test.py:573:10:573:10 | ControlFlowNode for a | -| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:572:5:572:5 | SSA variable a | -| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:572:8:572:9 | IterableElement | -| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:572:12:572:12 | SSA variable c | -| test.py:572:8:572:9 | IterableElement | test.py:572:8:572:9 | SSA variable b [List element] | -| test.py:572:8:572:9 | SSA variable b [List element] | test.py:575:10:575:10 | ControlFlowNode for b [List element] | -| test.py:572:12:572:12 | SSA variable c | test.py:576:12:576:12 | ControlFlowNode for c | -| test.py:575:10:575:10 | ControlFlowNode for b [List element] | test.py:575:10:575:13 | ControlFlowNode for Subscript | -| test.py:581:10:581:15 | ControlFlowNode for SOURCE | test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:581:18:581:23 | ControlFlowNode for SOURCE | test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:582:5:582:5 | SSA variable a | test.py:583:10:583:10 | ControlFlowNode for a | -| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:582:5:582:5 | SSA variable a | -| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:582:12:582:12 | SSA variable c | -| test.py:582:12:582:12 | SSA variable c | test.py:585:10:585:10 | ControlFlowNode for c | -| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | test.py:593:6:593:23 | IterableSequence [List element, List element] | -| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | test.py:601:5:601:24 | IterableSequence [List element, List element] | -| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | test.py:609:6:609:23 | IterableSequence [List element, List element] | -| test.py:590:11:590:37 | ControlFlowNode for List [List element] | test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | -| test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:590:11:590:37 | ControlFlowNode for List [List element] | -| test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:590:11:590:37 | ControlFlowNode for List [List element] | -| test.py:590:40:590:47 | ControlFlowNode for List [List element] | test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | -| test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:590:40:590:47 | ControlFlowNode for List [List element] | -| test.py:593:6:593:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | test.py:593:7:593:16 | IterableSequence [List element] | -| test.py:593:6:593:23 | IterableElement [List element] | test.py:593:6:593:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | -| test.py:593:6:593:23 | IterableSequence [List element, List element] | test.py:593:6:593:23 | IterableElement [List element] | -| test.py:593:7:593:8 | SSA variable a1 | test.py:594:10:594:11 | ControlFlowNode for a1 | -| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:593:7:593:8 | SSA variable a1 | -| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:593:11:593:12 | SSA variable a2 | -| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:593:15:593:16 | SSA variable a3 | -| test.py:593:7:593:16 | IterableElement | test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:593:7:593:16 | IterableElement | test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:593:7:593:16 | IterableElement | test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:593:7:593:16 | IterableSequence [List element] | test.py:593:7:593:16 | IterableElement | -| test.py:593:11:593:12 | SSA variable a2 | test.py:595:12:595:13 | ControlFlowNode for a2 | -| test.py:593:15:593:16 | SSA variable a3 | test.py:596:10:596:11 | ControlFlowNode for a3 | -| test.py:601:5:601:24 | ControlFlowNode for List [Tuple element at index 0, List element] | test.py:601:7:601:16 | IterableSequence [List element] | -| test.py:601:5:601:24 | IterableElement [List element] | test.py:601:5:601:24 | ControlFlowNode for List [Tuple element at index 0, List element] | -| test.py:601:5:601:24 | IterableSequence [List element, List element] | test.py:601:5:601:24 | IterableElement [List element] | -| test.py:601:7:601:8 | SSA variable a1 | test.py:602:10:602:11 | ControlFlowNode for a1 | -| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:601:7:601:8 | SSA variable a1 | -| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:601:11:601:12 | SSA variable a2 | -| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:601:15:601:16 | SSA variable a3 | -| test.py:601:7:601:16 | IterableElement | test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:601:7:601:16 | IterableElement | test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:601:7:601:16 | IterableElement | test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:601:7:601:16 | IterableSequence [List element] | test.py:601:7:601:16 | IterableElement | -| test.py:601:11:601:12 | SSA variable a2 | test.py:603:12:603:13 | ControlFlowNode for a2 | -| test.py:601:15:601:16 | SSA variable a3 | test.py:604:10:604:11 | ControlFlowNode for a3 | -| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 0] | test.py:609:7:609:8 | SSA variable a1 | -| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 1] | test.py:609:11:609:12 | SSA variable a2 | -| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 2] | test.py:609:15:609:16 | SSA variable a3 | -| test.py:609:6:609:17 | IterableElement | test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 0] | -| test.py:609:6:609:17 | IterableElement | test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 1] | -| test.py:609:6:609:17 | IterableElement | test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 2] | -| test.py:609:6:609:17 | IterableSequence [List element] | test.py:609:6:609:17 | IterableElement | -| test.py:609:6:609:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | test.py:609:6:609:17 | IterableSequence [List element] | -| test.py:609:6:609:23 | IterableElement [List element] | test.py:609:6:609:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | -| test.py:609:6:609:23 | IterableSequence [List element, List element] | test.py:609:6:609:23 | IterableElement [List element] | -| test.py:609:7:609:8 | SSA variable a1 | test.py:610:10:610:11 | ControlFlowNode for a1 | -| test.py:609:11:609:12 | SSA variable a2 | test.py:611:12:611:13 | ControlFlowNode for a2 | -| test.py:609:15:609:16 | SSA variable a3 | test.py:612:10:612:11 | ControlFlowNode for a3 | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | -| test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | -| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | -| test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | test.py:621:6:621:14 | IterableSequence [Tuple element at index 0] | -| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | test.py:621:6:621:14 | IterableSequence [Tuple element at index 2] | -| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 0] | test.py:621:7:621:8 | SSA variable a1 | -| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 2] | test.py:621:11:621:13 | IterableElement | -| test.py:621:6:621:14 | IterableSequence [Tuple element at index 0] | test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 0] | -| test.py:621:6:621:14 | IterableSequence [Tuple element at index 2] | test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 2] | -| test.py:621:7:621:8 | SSA variable a1 | test.py:622:10:622:11 | ControlFlowNode for a1 | -| test.py:621:11:621:13 | IterableElement | test.py:621:11:621:13 | SSA variable a2 [List element] | -| test.py:621:11:621:13 | SSA variable a2 [List element] | test.py:624:12:624:13 | ControlFlowNode for a2 [List element] | -| test.py:621:11:621:13 | SSA variable a2 [List element] | test.py:625:10:625:11 | ControlFlowNode for a2 [List element] | -| test.py:624:12:624:13 | ControlFlowNode for a2 [List element] | test.py:624:12:624:16 | ControlFlowNode for Subscript | -| test.py:625:10:625:11 | ControlFlowNode for a2 [List element] | test.py:625:10:625:14 | ControlFlowNode for Subscript | -| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:630:7:630:13 | IterableSequence [Tuple element at index 0] | -| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:630:7:630:13 | IterableSequence [Tuple element at index 2] | -| test.py:630:7:630:8 | SSA variable a1 | test.py:631:10:631:11 | ControlFlowNode for a1 | -| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:630:7:630:8 | SSA variable a1 | -| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:630:11:630:13 | IterableElement | -| test.py:630:7:630:13 | IterableSequence [Tuple element at index 0] | test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:630:7:630:13 | IterableSequence [Tuple element at index 2] | test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:630:11:630:13 | IterableElement | test.py:630:11:630:13 | SSA variable a2 [List element] | -| test.py:630:11:630:13 | SSA variable a2 [List element] | test.py:633:12:633:13 | ControlFlowNode for a2 [List element] | -| test.py:630:11:630:13 | SSA variable a2 [List element] | test.py:634:10:634:11 | ControlFlowNode for a2 [List element] | -| test.py:633:12:633:13 | ControlFlowNode for a2 [List element] | test.py:633:12:633:16 | ControlFlowNode for Subscript | -| test.py:634:10:634:11 | ControlFlowNode for a2 [List element] | test.py:634:10:634:14 | ControlFlowNode for Subscript | -| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | test.py:639:7:639:13 | IterableSequence [Tuple element at index 0] | -| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | test.py:639:7:639:13 | IterableSequence [Tuple element at index 2] | -| test.py:639:7:639:8 | SSA variable a1 | test.py:640:10:640:11 | ControlFlowNode for a1 | -| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:639:7:639:8 | SSA variable a1 | -| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:639:11:639:13 | IterableElement | -| test.py:639:7:639:13 | IterableSequence [Tuple element at index 0] | test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:639:7:639:13 | IterableSequence [Tuple element at index 2] | test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:639:11:639:13 | IterableElement | test.py:639:11:639:13 | SSA variable a2 [List element] | -| test.py:639:11:639:13 | SSA variable a2 [List element] | test.py:642:12:642:13 | ControlFlowNode for a2 [List element] | -| test.py:639:11:639:13 | SSA variable a2 [List element] | test.py:643:10:643:11 | ControlFlowNode for a2 [List element] | -| test.py:642:12:642:13 | ControlFlowNode for a2 [List element] | test.py:642:12:642:16 | ControlFlowNode for Subscript | -| test.py:643:10:643:11 | ControlFlowNode for a2 [List element] | test.py:643:10:643:14 | ControlFlowNode for Subscript | -| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 0] | test.py:648:7:648:8 | SSA variable a1 | -| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 2] | test.py:648:11:648:13 | IterableElement | -| test.py:648:6:648:14 | IterableSequence [Tuple element at index 0] | test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 0] | -| test.py:648:6:648:14 | IterableSequence [Tuple element at index 2] | test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 2] | -| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:648:6:648:14 | IterableSequence [Tuple element at index 0] | -| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:648:6:648:14 | IterableSequence [Tuple element at index 2] | -| test.py:648:7:648:8 | SSA variable a1 | test.py:649:10:649:11 | ControlFlowNode for a1 | -| test.py:648:11:648:13 | IterableElement | test.py:648:11:648:13 | SSA variable a2 [List element] | -| test.py:648:11:648:13 | SSA variable a2 [List element] | test.py:651:12:651:13 | ControlFlowNode for a2 [List element] | -| test.py:648:11:648:13 | SSA variable a2 [List element] | test.py:652:10:652:11 | ControlFlowNode for a2 [List element] | -| test.py:651:12:651:13 | ControlFlowNode for a2 [List element] | test.py:651:12:651:16 | ControlFlowNode for Subscript | -| test.py:652:10:652:11 | ControlFlowNode for a2 [List element] | test.py:652:10:652:14 | ControlFlowNode for Subscript | -| test.py:659:19:659:24 | ControlFlowNode for SOURCE | test.py:660:10:660:10 | ControlFlowNode for a | -| test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:668:16:668:17 | ControlFlowNode for tl [List element, Tuple element at index 0] | -| test.py:667:12:667:17 | ControlFlowNode for SOURCE | test.py:667:12:667:28 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:667:12:667:28 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:667:33:667:38 | ControlFlowNode for SOURCE | test.py:667:33:667:49 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:667:33:667:49 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:668:9:668:9 | SSA variable x | test.py:669:14:669:14 | ControlFlowNode for x | -| test.py:668:9:668:11 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:668:9:668:9 | SSA variable x | -| test.py:668:9:668:11 | IterableSequence [Tuple element at index 0] | test.py:668:9:668:11 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:668:16:668:17 | ControlFlowNode for tl [List element, Tuple element at index 0] | test.py:668:9:668:11 | IterableSequence [Tuple element at index 0] | -| test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:676:17:676:18 | ControlFlowNode for tl [List element, Tuple element at index 0] | -| test.py:675:12:675:17 | ControlFlowNode for SOURCE | test.py:675:12:675:28 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:675:12:675:28 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:675:33:675:38 | ControlFlowNode for SOURCE | test.py:675:33:675:49 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:675:33:675:49 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:676:9:676:10 | IterableElement | test.py:676:9:676:10 | SSA variable x [List element] | -| test.py:676:9:676:10 | SSA variable x [List element] | test.py:678:14:678:14 | ControlFlowNode for x [List element] | -| test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:676:9:676:10 | IterableElement | -| test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:676:12:676:12 | SSA variable y | -| test.py:676:9:676:12 | IterableSequence [Tuple element at index 0] | test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:676:12:676:12 | SSA variable y | test.py:679:16:679:16 | ControlFlowNode for y | -| test.py:676:17:676:18 | ControlFlowNode for tl [List element, Tuple element at index 0] | test.py:676:9:676:12 | IterableSequence [Tuple element at index 0] | -| test.py:678:14:678:14 | ControlFlowNode for x [List element] | test.py:678:14:678:17 | ControlFlowNode for Subscript | -| test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:685:19:685:20 | ControlFlowNode for tl [List element, Tuple element at index 0] | -| test.py:684:12:684:17 | ControlFlowNode for SOURCE | test.py:684:12:684:28 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:684:12:684:28 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:684:33:684:38 | ControlFlowNode for SOURCE | test.py:684:33:684:49 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:684:33:684:49 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:685:9:685:9 | SSA variable x | test.py:686:14:686:14 | ControlFlowNode for x | -| test.py:685:9:685:14 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:685:9:685:9 | SSA variable x | -| test.py:685:9:685:14 | IterableSequence [Tuple element at index 0] | test.py:685:9:685:14 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:685:19:685:20 | ControlFlowNode for tl [List element, Tuple element at index 0] | test.py:685:9:685:14 | IterableSequence [Tuple element at index 0] | -| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 0] | test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 0] | -| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 1] | test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 1] | -| test.py:691:7:691:9 | SSA variable arg | test.py:692:10:692:12 | ControlFlowNode for arg | -| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 0] | test.py:691:7:691:9 | SSA variable arg | -| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 1] | test.py:691:7:691:9 | SSA variable arg | -| test.py:697:7:697:12 | ControlFlowNode for SOURCE | test.py:698:51:698:51 | ControlFlowNode for s | -| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 0] | -| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 1] | -| test.py:698:43:698:48 | ControlFlowNode for SOURCE | test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | -| test.py:698:51:698:51 | ControlFlowNode for s | test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | -| test.py:769:16:769:21 | ControlFlowNode for SOURCE | test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() | -| test.py:807:35:807:35 | ControlFlowNode for x | test.py:808:10:808:10 | ControlFlowNode for x | -| test.py:807:37:807:42 | ControlFlowNode for SOURCE | test.py:807:35:807:35 | ControlFlowNode for x | -| test.py:807:48:807:48 | ControlFlowNode for y | test.py:809:10:809:10 | ControlFlowNode for y | -| test.py:807:50:807:55 | ControlFlowNode for SOURCE | test.py:807:48:807:48 | ControlFlowNode for y | -| test.py:807:61:807:61 | ControlFlowNode for z | test.py:810:10:810:10 | ControlFlowNode for z | -| test.py:807:63:807:68 | ControlFlowNode for SOURCE | test.py:807:61:807:61 | ControlFlowNode for z | -nodes -| datamodel.py:35:7:35:7 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| datamodel.py:36:10:36:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| datamodel.py:38:6:38:17 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | -| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| datamodel.py:44:22:44:22 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| datamodel.py:46:16:46:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| datamodel.py:49:26:49:26 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| datamodel.py:50:16:50:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| datamodel.py:53:22:53:22 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| datamodel.py:54:16:54:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| datamodel.py:88:6:88:27 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| datamodel.py:88:21:88:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| datamodel.py:89:6:89:27 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| datamodel.py:89:21:89:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| datamodel.py:161:5:161:8 | [post store] ControlFlowNode for self [Attribute b] | semmle.label | [post store] ControlFlowNode for self [Attribute b] | -| datamodel.py:161:14:161:19 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| datamodel.py:164:14:164:25 | ControlFlowNode for Customized() [Attribute b] | semmle.label | ControlFlowNode for Customized() [Attribute b] | -| datamodel.py:168:6:168:15 | ControlFlowNode for customized [Attribute b] | semmle.label | ControlFlowNode for customized [Attribute b] | -| datamodel.py:168:6:168:17 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| test.py:42:10:42:26 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:42:21:42:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:43:9:43:9 | ControlFlowNode for x [Tuple element at index 1] | semmle.label | ControlFlowNode for x [Tuple element at index 1] | -| test.py:43:9:43:12 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:44:10:44:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:55:9:55:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:56:10:56:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:61:9:61:16 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | -| test.py:62:10:62:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:66:9:66:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | -| test.py:67:10:67:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:71:9:71:10 | ControlFlowNode for IntegerLiteral | semmle.label | ControlFlowNode for IntegerLiteral | -| test.py:72:10:72:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:76:9:76:12 | ControlFlowNode for FloatLiteral | semmle.label | ControlFlowNode for FloatLiteral | -| test.py:77:10:77:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:87:10:87:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:88:10:88:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:93:9:93:16 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:93:10:93:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:94:10:94:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:94:10:94:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:103:9:103:37 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:103:10:103:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:104:10:104:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:104:10:104:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:108:9:108:29 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:108:10:108:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:108:16:108:16 | SSA variable y | semmle.label | SSA variable y | -| test.py:108:21:108:28 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:108:22:108:27 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:109:10:109:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:109:10:109:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:113:9:113:16 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:113:10:113:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:114:9:114:22 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:114:10:114:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:114:16:114:16 | SSA variable y | semmle.label | SSA variable y | -| test.py:114:21:114:21 | ControlFlowNode for l [List element] | semmle.label | ControlFlowNode for l [List element] | -| test.py:115:10:115:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:115:10:115:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:125:9:125:16 | ControlFlowNode for Set [Set element] | semmle.label | ControlFlowNode for Set [Set element] | -| test.py:125:10:125:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:126:10:126:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] | -| test.py:126:10:126:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| test.py:130:9:130:37 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] | -| test.py:130:10:130:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:131:10:131:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] | -| test.py:131:10:131:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| test.py:135:9:135:29 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] | -| test.py:135:10:135:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:135:16:135:16 | SSA variable y | semmle.label | SSA variable y | -| test.py:135:21:135:28 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:135:22:135:27 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:136:10:136:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] | -| test.py:136:10:136:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| test.py:140:9:140:16 | ControlFlowNode for Set [Set element] | semmle.label | ControlFlowNode for Set [Set element] | -| test.py:140:10:140:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:141:9:141:22 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] | -| test.py:141:10:141:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:141:16:141:16 | SSA variable y | semmle.label | SSA variable y | -| test.py:141:21:141:21 | ControlFlowNode for l [Set element] | semmle.label | ControlFlowNode for l [Set element] | -| test.py:142:10:142:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] | -| test.py:142:10:142:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| test.py:152:9:152:21 | ControlFlowNode for Dict [Dictionary element at key s] | semmle.label | ControlFlowNode for Dict [Dictionary element at key s] | -| test.py:152:15:152:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:153:10:153:10 | ControlFlowNode for x [Dictionary element at key s] | semmle.label | ControlFlowNode for x [Dictionary element at key s] | -| test.py:153:10:153:15 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:157:9:157:21 | ControlFlowNode for Dict [Dictionary element at key s] | semmle.label | ControlFlowNode for Dict [Dictionary element at key s] | -| test.py:157:15:157:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:158:10:158:10 | ControlFlowNode for x [Dictionary element at key s] | semmle.label | ControlFlowNode for x [Dictionary element at key s] | -| test.py:158:10:158:19 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| test.py:183:9:183:42 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:183:10:183:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:183:16:183:16 | SSA variable z [List element] | semmle.label | SSA variable z [List element] | -| test.py:183:21:183:30 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] | -| test.py:183:22:183:29 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:183:23:183:28 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:183:36:183:36 | SSA variable y | semmle.label | SSA variable y | -| test.py:183:41:183:41 | ControlFlowNode for z [List element] | semmle.label | ControlFlowNode for z [List element] | -| test.py:184:10:184:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:184:10:184:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:188:9:188:68 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:188:10:188:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:188:16:188:16 | SSA variable v [List element, List element, List element] | semmle.label | SSA variable v [List element, List element, List element] | -| test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element, List element] | -| test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element] | -| test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] | -| test.py:188:24:188:31 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:188:25:188:30 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:188:40:188:40 | SSA variable u [List element, List element] | semmle.label | SSA variable u [List element, List element] | -| test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, List element] | semmle.label | ControlFlowNode for v [List element, List element, List element] | -| test.py:188:51:188:51 | SSA variable z [List element] | semmle.label | SSA variable z [List element] | -| test.py:188:56:188:56 | ControlFlowNode for u [List element, List element] | semmle.label | ControlFlowNode for u [List element, List element] | -| test.py:188:62:188:62 | SSA variable y | semmle.label | SSA variable y | -| test.py:188:67:188:67 | ControlFlowNode for z [List element] | semmle.label | ControlFlowNode for z [List element] | -| test.py:189:10:189:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:189:10:189:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:199:9:199:42 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:199:10:199:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:199:16:199:16 | SSA variable y | semmle.label | SSA variable y | -| test.py:199:22:199:22 | ControlFlowNode for z | semmle.label | ControlFlowNode for z | -| test.py:199:22:199:40 | ControlFlowNode for GeneratorExp [List element] | semmle.label | ControlFlowNode for GeneratorExp [List element] | -| test.py:199:28:199:28 | SSA variable z | semmle.label | SSA variable z | -| test.py:199:33:199:40 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:199:34:199:39 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:200:10:200:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:200:10:200:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:205:9:205:47 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:205:10:205:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:205:17:205:17 | SSA variable a | semmle.label | SSA variable a | -| test.py:205:17:205:20 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:205:17:205:20 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:205:26:205:46 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:205:28:205:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:205:28:205:44 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:206:10:206:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:206:10:206:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:210:9:210:51 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:210:10:210:10 | ControlFlowNode for a [List element] | semmle.label | ControlFlowNode for a [List element] | -| test.py:210:10:210:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:210:20:210:21 | IterableElement | semmle.label | IterableElement | -| test.py:210:20:210:21 | SSA variable a [List element] | semmle.label | SSA variable a [List element] | -| test.py:210:20:210:24 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:210:20:210:24 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:210:30:210:50 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:210:32:210:37 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:210:32:210:48 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:211:10:211:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:211:10:211:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:349:10:349:21 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:349:11:349:16 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:349:11:349:17 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:353:10:353:17 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:353:10:353:20 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:353:11:353:16 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:357:10:357:22 | ControlFlowNode for Dict [Dictionary element at key s] | semmle.label | ControlFlowNode for Dict [Dictionary element at key s] | -| test.py:357:10:357:27 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:357:16:357:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:375:15:375:15 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:376:12:376:12 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:380:10:380:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:380:28:380:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:388:10:388:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:388:30:388:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:396:10:396:43 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:396:10:396:43 | KwUnpacked b | semmle.label | KwUnpacked b | -| test.py:396:30:396:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] | -| test.py:396:36:396:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:399:21:399:21 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] | -| test.py:400:12:400:12 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] | -| test.py:400:12:400:15 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() | -| test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | semmle.label | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | -| test.py:404:33:404:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:407:26:407:26 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] | -| test.py:408:12:408:12 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] | -| test.py:408:12:408:17 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() | -| test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | semmle.label | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | -| test.py:412:39:412:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:429:10:429:20 | ControlFlowNode for BoolExpr | semmle.label | ControlFlowNode for BoolExpr | -| test.py:429:15:429:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:434:10:434:21 | ControlFlowNode for BoolExpr | semmle.label | ControlFlowNode for BoolExpr | -| test.py:434:16:434:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:445:10:445:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:445:10:445:38 | ControlFlowNode for IfExp | semmle.label | ControlFlowNode for IfExp | -| test.py:453:10:453:39 | ControlFlowNode for IfExp | semmle.label | ControlFlowNode for IfExp | -| test.py:453:34:453:39 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:474:11:474:11 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:475:16:475:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:477:10:477:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | -| test.py:477:12:477:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:481:19:481:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:482:16:482:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:484:10:484:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:484:28:484:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:495:19:495:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:496:16:496:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:498:10:498:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:498:30:498:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:509:19:509:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:510:16:510:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:512:10:512:43 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:512:10:512:43 | KwUnpacked b | semmle.label | KwUnpacked b | -| test.py:512:30:512:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] | -| test.py:512:36:512:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] | -| test.py:516:33:516:33 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] | -| test.py:516:33:516:36 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() | -| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | semmle.label | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | -| test.py:517:33:517:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] | -| test.py:521:38:521:38 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] | -| test.py:521:38:521:43 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() | -| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | semmle.label | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | -| test.py:522:39:522:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:534:9:534:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:536:10:536:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:541:10:541:10 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:546:10:546:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:546:10:546:26 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:547:5:547:5 | SSA variable a | semmle.label | SSA variable a | -| test.py:547:5:547:8 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:548:10:548:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:554:10:554:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | -| test.py:554:19:554:35 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:554:30:554:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:555:5:555:5 | SSA variable a | semmle.label | SSA variable a | -| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | -| test.py:555:9:555:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:555:9:555:12 | IterableSequence [Tuple element at index 1] | semmle.label | IterableSequence [Tuple element at index 1] | -| test.py:555:12:555:12 | SSA variable c | semmle.label | SSA variable c | -| test.py:556:10:556:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:558:10:558:10 | ControlFlowNode for c | semmle.label | ControlFlowNode for c | -| test.py:563:9:563:33 | ControlFlowNode for List [List element, List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element, List element] | -| test.py:563:10:563:21 | ControlFlowNode for List [List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element] | -| test.py:563:11:563:20 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] | -| test.py:563:12:563:19 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:563:13:563:18 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:564:5:564:11 | ControlFlowNode for List [Tuple element at index 0, List element, List element] | semmle.label | ControlFlowNode for List [Tuple element at index 0, List element, List element] | -| test.py:564:5:564:11 | IterableElement [List element, List element] | semmle.label | IterableElement [List element, List element] | -| test.py:564:5:564:11 | IterableSequence [List element, List element, List element] | semmle.label | IterableSequence [List element, List element, List element] | -| test.py:564:5:564:14 | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | -| test.py:564:5:564:14 | IterableElement [List element, List element, List element] | semmle.label | IterableElement [List element, List element, List element] | -| test.py:564:5:564:14 | IterableSequence [List element, List element, List element, List element] | semmle.label | IterableSequence [List element, List element, List element, List element] | -| test.py:564:6:564:10 | ControlFlowNode for List [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for List [Tuple element at index 0, List element] | -| test.py:564:6:564:10 | IterableElement [List element] | semmle.label | IterableElement [List element] | -| test.py:564:6:564:10 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] | -| test.py:564:7:564:9 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] | -| test.py:564:7:564:9 | IterableElement | semmle.label | IterableElement | -| test.py:564:7:564:9 | IterableSequence [List element] | semmle.label | IterableSequence [List element] | -| test.py:564:8:564:8 | SSA variable a | semmle.label | SSA variable a | -| test.py:565:10:565:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:571:10:571:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:571:18:571:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:572:5:572:5 | SSA variable a | semmle.label | SSA variable a | -| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:572:8:572:9 | IterableElement | semmle.label | IterableElement | -| test.py:572:8:572:9 | SSA variable b [List element] | semmle.label | SSA variable b [List element] | -| test.py:572:12:572:12 | SSA variable c | semmle.label | SSA variable c | -| test.py:573:10:573:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:575:10:575:10 | ControlFlowNode for b [List element] | semmle.label | ControlFlowNode for b [List element] | -| test.py:575:10:575:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:576:12:576:12 | ControlFlowNode for c | semmle.label | ControlFlowNode for c | -| test.py:581:10:581:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:581:18:581:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:582:5:582:5 | SSA variable a | semmle.label | SSA variable a | -| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:582:12:582:12 | SSA variable c | semmle.label | SSA variable c | -| test.py:583:10:583:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:585:10:585:10 | ControlFlowNode for c | semmle.label | ControlFlowNode for c | -| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] | -| test.py:590:11:590:37 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:590:12:590:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:590:31:590:36 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:590:40:590:47 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:590:41:590:46 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:593:6:593:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, List element] | -| test.py:593:6:593:23 | IterableElement [List element] | semmle.label | IterableElement [List element] | -| test.py:593:6:593:23 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] | -| test.py:593:7:593:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:593:7:593:16 | IterableElement | semmle.label | IterableElement | -| test.py:593:7:593:16 | IterableSequence [List element] | semmle.label | IterableSequence [List element] | -| test.py:593:11:593:12 | SSA variable a2 | semmle.label | SSA variable a2 | -| test.py:593:15:593:16 | SSA variable a3 | semmle.label | SSA variable a3 | -| test.py:594:10:594:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:595:12:595:13 | ControlFlowNode for a2 | semmle.label | ControlFlowNode for a2 | -| test.py:596:10:596:11 | ControlFlowNode for a3 | semmle.label | ControlFlowNode for a3 | -| test.py:601:5:601:24 | ControlFlowNode for List [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for List [Tuple element at index 0, List element] | -| test.py:601:5:601:24 | IterableElement [List element] | semmle.label | IterableElement [List element] | -| test.py:601:5:601:24 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] | -| test.py:601:7:601:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:601:7:601:16 | IterableElement | semmle.label | IterableElement | -| test.py:601:7:601:16 | IterableSequence [List element] | semmle.label | IterableSequence [List element] | -| test.py:601:11:601:12 | SSA variable a2 | semmle.label | SSA variable a2 | -| test.py:601:15:601:16 | SSA variable a3 | semmle.label | SSA variable a3 | -| test.py:602:10:602:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:603:12:603:13 | ControlFlowNode for a2 | semmle.label | ControlFlowNode for a2 | -| test.py:604:10:604:11 | ControlFlowNode for a3 | semmle.label | ControlFlowNode for a3 | -| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] | -| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 1] | semmle.label | ControlFlowNode for List [Tuple element at index 1] | -| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 2] | -| test.py:609:6:609:17 | IterableElement | semmle.label | IterableElement | -| test.py:609:6:609:17 | IterableSequence [List element] | semmle.label | IterableSequence [List element] | -| test.py:609:6:609:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, List element] | -| test.py:609:6:609:23 | IterableElement [List element] | semmle.label | IterableElement [List element] | -| test.py:609:6:609:23 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] | -| test.py:609:7:609:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:609:11:609:12 | SSA variable a2 | semmle.label | SSA variable a2 | -| test.py:609:15:609:16 | SSA variable a3 | semmle.label | SSA variable a3 | -| test.py:610:10:610:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:611:12:611:13 | ControlFlowNode for a2 | semmle.label | ControlFlowNode for a2 | -| test.py:612:10:612:11 | ControlFlowNode for a3 | semmle.label | ControlFlowNode for a3 | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | -| test.py:618:12:618:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:618:31:618:36 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | -| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | -| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] | -| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 2] | -| test.py:621:6:621:14 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:621:6:621:14 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] | -| test.py:621:7:621:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:621:11:621:13 | IterableElement | semmle.label | IterableElement | -| test.py:621:11:621:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] | -| test.py:622:10:622:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:624:12:624:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:624:12:624:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:625:10:625:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:625:10:625:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | -| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | -| test.py:630:7:630:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:630:7:630:13 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:630:7:630:13 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] | -| test.py:630:11:630:13 | IterableElement | semmle.label | IterableElement | -| test.py:630:11:630:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] | -| test.py:631:10:631:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:633:12:633:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:633:12:633:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:634:10:634:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:634:10:634:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | -| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | -| test.py:639:7:639:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:639:7:639:13 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:639:7:639:13 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] | -| test.py:639:11:639:13 | IterableElement | semmle.label | IterableElement | -| test.py:639:11:639:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] | -| test.py:640:10:640:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:642:12:642:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:642:12:642:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:643:10:643:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:643:10:643:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] | -| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 2] | -| test.py:648:6:648:14 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:648:6:648:14 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] | -| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | -| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | -| test.py:648:7:648:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:648:11:648:13 | IterableElement | semmle.label | IterableElement | -| test.py:648:11:648:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] | -| test.py:649:10:649:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:651:12:651:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:651:12:651:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:652:10:652:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:652:10:652:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:659:19:659:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:660:10:660:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:667:12:667:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:667:12:667:28 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:667:33:667:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:667:33:667:49 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:668:9:668:9 | SSA variable x | semmle.label | SSA variable x | -| test.py:668:9:668:11 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:668:9:668:11 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:668:16:668:17 | ControlFlowNode for tl [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for tl [List element, Tuple element at index 0] | -| test.py:669:14:669:14 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:675:12:675:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:675:12:675:28 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:675:33:675:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:675:33:675:49 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:676:9:676:10 | IterableElement | semmle.label | IterableElement | -| test.py:676:9:676:10 | SSA variable x [List element] | semmle.label | SSA variable x [List element] | -| test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:676:9:676:12 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:676:12:676:12 | SSA variable y | semmle.label | SSA variable y | -| test.py:676:17:676:18 | ControlFlowNode for tl [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for tl [List element, Tuple element at index 0] | -| test.py:678:14:678:14 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:678:14:678:17 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:679:16:679:16 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:684:12:684:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:684:12:684:28 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:684:33:684:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:684:33:684:49 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:685:9:685:9 | SSA variable x | semmle.label | SSA variable x | -| test.py:685:9:685:14 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:685:9:685:14 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:685:19:685:20 | ControlFlowNode for tl [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for tl [List element, Tuple element at index 0] | -| test.py:686:14:686:14 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 0] | semmle.label | ControlFlowNode for args [Tuple element at index 0] | -| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 1] | semmle.label | ControlFlowNode for args [Tuple element at index 1] | -| test.py:691:7:691:9 | SSA variable arg | semmle.label | SSA variable arg | -| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 0] | semmle.label | ControlFlowNode for args [Tuple element at index 0] | -| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 1] | semmle.label | ControlFlowNode for args [Tuple element at index 1] | -| test.py:692:10:692:12 | ControlFlowNode for arg | semmle.label | ControlFlowNode for arg | -| test.py:697:7:697:12 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | semmle.label | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | -| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | semmle.label | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | -| test.py:698:43:698:48 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:698:51:698:51 | ControlFlowNode for s | semmle.label | ControlFlowNode for s | -| test.py:769:16:769:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() | semmle.label | ControlFlowNode for return_from_inner_scope() | -| test.py:807:35:807:35 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:807:37:807:42 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:807:48:807:48 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:807:50:807:55 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:807:61:807:61 | ControlFlowNode for z | semmle.label | ControlFlowNode for z | -| test.py:807:63:807:68 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:808:10:808:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:809:10:809:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:810:10:810:10 | ControlFlowNode for z | semmle.label | ControlFlowNode for z | -subpaths -| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:35:7:35:7 | ControlFlowNode for a | datamodel.py:36:10:36:10 | ControlFlowNode for a | datamodel.py:38:6:38:17 | ControlFlowNode for f() | -| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x | datamodel.py:46:16:46:16 | ControlFlowNode for x | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | -| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x | datamodel.py:46:16:46:16 | ControlFlowNode for x | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | -| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x | datamodel.py:50:16:50:16 | ControlFlowNode for x | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | -| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x | datamodel.py:50:16:50:16 | ControlFlowNode for x | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | -| datamodel.py:88:21:88:26 | ControlFlowNode for SOURCE | datamodel.py:53:22:53:22 | ControlFlowNode for x | datamodel.py:54:16:54:16 | ControlFlowNode for x | datamodel.py:88:6:88:27 | ControlFlowNode for Attribute() | -| datamodel.py:89:21:89:26 | ControlFlowNode for SOURCE | datamodel.py:53:22:53:22 | ControlFlowNode for x | datamodel.py:54:16:54:16 | ControlFlowNode for x | datamodel.py:89:6:89:27 | ControlFlowNode for Attribute() | -| test.py:380:28:380:33 | ControlFlowNode for SOURCE | test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b | test.py:380:10:380:34 | ControlFlowNode for second() | -| test.py:388:30:388:35 | ControlFlowNode for SOURCE | test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b | test.py:388:10:388:36 | ControlFlowNode for second() | -| test.py:396:10:396:43 | KwUnpacked b | test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b | test.py:396:10:396:43 | ControlFlowNode for second() | -| test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:399:21:399:21 | ControlFlowNode for b [Tuple element at index 0] | test.py:400:12:400:15 | ControlFlowNode for Subscript | test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() | -| test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:407:26:407:26 | ControlFlowNode for b [Dictionary element at key b] | test.py:408:12:408:17 | ControlFlowNode for Subscript | test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | -| test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:474:11:474:11 | ControlFlowNode for x | test.py:475:16:475:16 | ControlFlowNode for x | test.py:477:10:477:18 | ControlFlowNode for f() | -| test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:481:19:481:19 | ControlFlowNode for b | test.py:482:16:482:16 | ControlFlowNode for b | test.py:484:10:484:34 | ControlFlowNode for second() | -| test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:495:19:495:19 | ControlFlowNode for b | test.py:496:16:496:16 | ControlFlowNode for b | test.py:498:10:498:36 | ControlFlowNode for second() | -| test.py:512:10:512:43 | KwUnpacked b | test.py:509:19:509:19 | ControlFlowNode for b | test.py:510:16:510:16 | ControlFlowNode for b | test.py:512:10:512:43 | ControlFlowNode for second() | -| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] | test.py:516:33:516:36 | ControlFlowNode for Subscript | test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | -| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] | test.py:521:38:521:43 | ControlFlowNode for Subscript | test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | -#select -| datamodel.py:38:6:38:17 | ControlFlowNode for f() | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:38:6:38:17 | ControlFlowNode for f() | Flow found | -| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | Flow found | -| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | Flow found | -| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | Flow found | -| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | Flow found | -| datamodel.py:88:6:88:27 | ControlFlowNode for Attribute() | datamodel.py:88:21:88:26 | ControlFlowNode for SOURCE | datamodel.py:88:6:88:27 | ControlFlowNode for Attribute() | Flow found | -| datamodel.py:89:6:89:27 | ControlFlowNode for Attribute() | datamodel.py:89:21:89:26 | ControlFlowNode for SOURCE | datamodel.py:89:6:89:27 | ControlFlowNode for Attribute() | Flow found | -| datamodel.py:168:6:168:17 | ControlFlowNode for Attribute | datamodel.py:161:14:161:19 | ControlFlowNode for SOURCE | datamodel.py:168:6:168:17 | ControlFlowNode for Attribute | Flow found | -| test.py:44:10:44:10 | ControlFlowNode for y | test.py:42:21:42:26 | ControlFlowNode for SOURCE | test.py:44:10:44:10 | ControlFlowNode for y | Flow found | -| test.py:56:10:56:10 | ControlFlowNode for x | test.py:55:9:55:14 | ControlFlowNode for SOURCE | test.py:56:10:56:10 | ControlFlowNode for x | Flow found | -| test.py:62:10:62:10 | ControlFlowNode for x | test.py:61:9:61:16 | ControlFlowNode for Str | test.py:62:10:62:10 | ControlFlowNode for x | Flow found | -| test.py:67:10:67:10 | ControlFlowNode for x | test.py:66:9:66:17 | ControlFlowNode for Str | test.py:67:10:67:10 | ControlFlowNode for x | Flow found | -| test.py:72:10:72:10 | ControlFlowNode for x | test.py:71:9:71:10 | ControlFlowNode for IntegerLiteral | test.py:72:10:72:10 | ControlFlowNode for x | Flow found | -| test.py:77:10:77:10 | ControlFlowNode for x | test.py:76:9:76:12 | ControlFlowNode for FloatLiteral | test.py:77:10:77:10 | ControlFlowNode for x | Flow found | -| test.py:88:10:88:10 | ControlFlowNode for x | test.py:87:10:87:15 | ControlFlowNode for SOURCE | test.py:88:10:88:10 | ControlFlowNode for x | Flow found | -| test.py:94:10:94:13 | ControlFlowNode for Subscript | test.py:93:10:93:15 | ControlFlowNode for SOURCE | test.py:94:10:94:13 | ControlFlowNode for Subscript | Flow found | -| test.py:104:10:104:13 | ControlFlowNode for Subscript | test.py:103:10:103:15 | ControlFlowNode for SOURCE | test.py:104:10:104:13 | ControlFlowNode for Subscript | Flow found | -| test.py:109:10:109:13 | ControlFlowNode for Subscript | test.py:108:22:108:27 | ControlFlowNode for SOURCE | test.py:109:10:109:13 | ControlFlowNode for Subscript | Flow found | -| test.py:115:10:115:13 | ControlFlowNode for Subscript | test.py:113:10:113:15 | ControlFlowNode for SOURCE | test.py:115:10:115:13 | ControlFlowNode for Subscript | Flow found | -| test.py:126:10:126:16 | ControlFlowNode for Attribute() | test.py:125:10:125:15 | ControlFlowNode for SOURCE | test.py:126:10:126:16 | ControlFlowNode for Attribute() | Flow found | -| test.py:131:10:131:16 | ControlFlowNode for Attribute() | test.py:130:10:130:15 | ControlFlowNode for SOURCE | test.py:131:10:131:16 | ControlFlowNode for Attribute() | Flow found | -| test.py:136:10:136:16 | ControlFlowNode for Attribute() | test.py:135:22:135:27 | ControlFlowNode for SOURCE | test.py:136:10:136:16 | ControlFlowNode for Attribute() | Flow found | -| test.py:142:10:142:16 | ControlFlowNode for Attribute() | test.py:140:10:140:15 | ControlFlowNode for SOURCE | test.py:142:10:142:16 | ControlFlowNode for Attribute() | Flow found | -| test.py:153:10:153:15 | ControlFlowNode for Subscript | test.py:152:15:152:20 | ControlFlowNode for SOURCE | test.py:153:10:153:15 | ControlFlowNode for Subscript | Flow found | -| test.py:158:10:158:19 | ControlFlowNode for Attribute() | test.py:157:15:157:20 | ControlFlowNode for SOURCE | test.py:158:10:158:19 | ControlFlowNode for Attribute() | Flow found | -| test.py:184:10:184:13 | ControlFlowNode for Subscript | test.py:183:23:183:28 | ControlFlowNode for SOURCE | test.py:184:10:184:13 | ControlFlowNode for Subscript | Flow found | -| test.py:189:10:189:13 | ControlFlowNode for Subscript | test.py:188:25:188:30 | ControlFlowNode for SOURCE | test.py:189:10:189:13 | ControlFlowNode for Subscript | Flow found | -| test.py:200:10:200:13 | ControlFlowNode for Subscript | test.py:199:34:199:39 | ControlFlowNode for SOURCE | test.py:200:10:200:13 | ControlFlowNode for Subscript | Flow found | -| test.py:206:10:206:13 | ControlFlowNode for Subscript | test.py:205:28:205:33 | ControlFlowNode for SOURCE | test.py:206:10:206:13 | ControlFlowNode for Subscript | Flow found | -| test.py:211:10:211:13 | ControlFlowNode for Subscript | test.py:210:32:210:37 | ControlFlowNode for SOURCE | test.py:211:10:211:13 | ControlFlowNode for Subscript | Flow found | -| test.py:349:10:349:21 | ControlFlowNode for Subscript | test.py:349:11:349:16 | ControlFlowNode for SOURCE | test.py:349:10:349:21 | ControlFlowNode for Subscript | Flow found | -| test.py:353:10:353:20 | ControlFlowNode for Subscript | test.py:353:11:353:16 | ControlFlowNode for SOURCE | test.py:353:10:353:20 | ControlFlowNode for Subscript | Flow found | -| test.py:357:10:357:27 | ControlFlowNode for Subscript | test.py:357:16:357:21 | ControlFlowNode for SOURCE | test.py:357:10:357:27 | ControlFlowNode for Subscript | Flow found | -| test.py:380:10:380:34 | ControlFlowNode for second() | test.py:380:28:380:33 | ControlFlowNode for SOURCE | test.py:380:10:380:34 | ControlFlowNode for second() | Flow found | -| test.py:388:10:388:36 | ControlFlowNode for second() | test.py:388:30:388:35 | ControlFlowNode for SOURCE | test.py:388:10:388:36 | ControlFlowNode for second() | Flow found | -| test.py:396:10:396:43 | ControlFlowNode for second() | test.py:396:36:396:41 | ControlFlowNode for SOURCE | test.py:396:10:396:43 | ControlFlowNode for second() | Flow found | -| test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() | test.py:404:33:404:38 | ControlFlowNode for SOURCE | test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() | Flow found | -| test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | test.py:412:39:412:44 | ControlFlowNode for SOURCE | test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | Flow found | -| test.py:429:10:429:20 | ControlFlowNode for BoolExpr | test.py:429:15:429:20 | ControlFlowNode for SOURCE | test.py:429:10:429:20 | ControlFlowNode for BoolExpr | Flow found | -| test.py:434:10:434:21 | ControlFlowNode for BoolExpr | test.py:434:16:434:21 | ControlFlowNode for SOURCE | test.py:434:10:434:21 | ControlFlowNode for BoolExpr | Flow found | -| test.py:445:10:445:38 | ControlFlowNode for IfExp | test.py:445:10:445:15 | ControlFlowNode for SOURCE | test.py:445:10:445:38 | ControlFlowNode for IfExp | Flow found | -| test.py:453:10:453:39 | ControlFlowNode for IfExp | test.py:453:34:453:39 | ControlFlowNode for SOURCE | test.py:453:10:453:39 | ControlFlowNode for IfExp | Flow found | -| test.py:477:10:477:18 | ControlFlowNode for f() | test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:477:10:477:18 | ControlFlowNode for f() | Flow found | -| test.py:484:10:484:34 | ControlFlowNode for second() | test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:484:10:484:34 | ControlFlowNode for second() | Flow found | -| test.py:498:10:498:36 | ControlFlowNode for second() | test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:498:10:498:36 | ControlFlowNode for second() | Flow found | -| test.py:512:10:512:43 | ControlFlowNode for second() | test.py:512:36:512:41 | ControlFlowNode for SOURCE | test.py:512:10:512:43 | ControlFlowNode for second() | Flow found | -| test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | test.py:517:33:517:38 | ControlFlowNode for SOURCE | test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | Flow found | -| test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | test.py:522:39:522:44 | ControlFlowNode for SOURCE | test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | Flow found | -| test.py:536:10:536:10 | ControlFlowNode for a | test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:536:10:536:10 | ControlFlowNode for a | Flow found | -| test.py:541:10:541:10 | ControlFlowNode for b | test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:541:10:541:10 | ControlFlowNode for b | Flow found | -| test.py:548:10:548:10 | ControlFlowNode for a | test.py:546:10:546:15 | ControlFlowNode for SOURCE | test.py:548:10:548:10 | ControlFlowNode for a | Flow found | -| test.py:556:10:556:10 | ControlFlowNode for a | test.py:554:10:554:15 | ControlFlowNode for SOURCE | test.py:556:10:556:10 | ControlFlowNode for a | Flow found | -| test.py:558:10:558:10 | ControlFlowNode for c | test.py:554:30:554:35 | ControlFlowNode for SOURCE | test.py:558:10:558:10 | ControlFlowNode for c | Flow found | -| test.py:565:10:565:10 | ControlFlowNode for a | test.py:563:13:563:18 | ControlFlowNode for SOURCE | test.py:565:10:565:10 | ControlFlowNode for a | Flow found | -| test.py:573:10:573:10 | ControlFlowNode for a | test.py:571:10:571:15 | ControlFlowNode for SOURCE | test.py:573:10:573:10 | ControlFlowNode for a | Flow found | -| test.py:575:10:575:13 | ControlFlowNode for Subscript | test.py:571:18:571:23 | ControlFlowNode for SOURCE | test.py:575:10:575:13 | ControlFlowNode for Subscript | Flow found | -| test.py:576:12:576:12 | ControlFlowNode for c | test.py:571:18:571:23 | ControlFlowNode for SOURCE | test.py:576:12:576:12 | ControlFlowNode for c | Flow found | -| test.py:583:10:583:10 | ControlFlowNode for a | test.py:581:10:581:15 | ControlFlowNode for SOURCE | test.py:583:10:583:10 | ControlFlowNode for a | Flow found | -| test.py:585:10:585:10 | ControlFlowNode for c | test.py:581:18:581:23 | ControlFlowNode for SOURCE | test.py:585:10:585:10 | ControlFlowNode for c | Flow found | -| test.py:594:10:594:11 | ControlFlowNode for a1 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:594:10:594:11 | ControlFlowNode for a1 | Flow found | -| test.py:594:10:594:11 | ControlFlowNode for a1 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:594:10:594:11 | ControlFlowNode for a1 | Flow found | -| test.py:594:10:594:11 | ControlFlowNode for a1 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:594:10:594:11 | ControlFlowNode for a1 | Flow found | -| test.py:595:12:595:13 | ControlFlowNode for a2 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:595:12:595:13 | ControlFlowNode for a2 | Flow found | -| test.py:595:12:595:13 | ControlFlowNode for a2 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:595:12:595:13 | ControlFlowNode for a2 | Flow found | -| test.py:595:12:595:13 | ControlFlowNode for a2 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:595:12:595:13 | ControlFlowNode for a2 | Flow found | -| test.py:596:10:596:11 | ControlFlowNode for a3 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:596:10:596:11 | ControlFlowNode for a3 | Flow found | -| test.py:596:10:596:11 | ControlFlowNode for a3 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:596:10:596:11 | ControlFlowNode for a3 | Flow found | -| test.py:596:10:596:11 | ControlFlowNode for a3 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:596:10:596:11 | ControlFlowNode for a3 | Flow found | -| test.py:602:10:602:11 | ControlFlowNode for a1 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:602:10:602:11 | ControlFlowNode for a1 | Flow found | -| test.py:602:10:602:11 | ControlFlowNode for a1 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:602:10:602:11 | ControlFlowNode for a1 | Flow found | -| test.py:602:10:602:11 | ControlFlowNode for a1 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:602:10:602:11 | ControlFlowNode for a1 | Flow found | -| test.py:603:12:603:13 | ControlFlowNode for a2 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:603:12:603:13 | ControlFlowNode for a2 | Flow found | -| test.py:603:12:603:13 | ControlFlowNode for a2 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:603:12:603:13 | ControlFlowNode for a2 | Flow found | -| test.py:603:12:603:13 | ControlFlowNode for a2 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:603:12:603:13 | ControlFlowNode for a2 | Flow found | -| test.py:604:10:604:11 | ControlFlowNode for a3 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:604:10:604:11 | ControlFlowNode for a3 | Flow found | -| test.py:604:10:604:11 | ControlFlowNode for a3 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:604:10:604:11 | ControlFlowNode for a3 | Flow found | -| test.py:604:10:604:11 | ControlFlowNode for a3 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:604:10:604:11 | ControlFlowNode for a3 | Flow found | -| test.py:610:10:610:11 | ControlFlowNode for a1 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:610:10:610:11 | ControlFlowNode for a1 | Flow found | -| test.py:610:10:610:11 | ControlFlowNode for a1 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:610:10:610:11 | ControlFlowNode for a1 | Flow found | -| test.py:610:10:610:11 | ControlFlowNode for a1 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:610:10:610:11 | ControlFlowNode for a1 | Flow found | -| test.py:611:12:611:13 | ControlFlowNode for a2 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:611:12:611:13 | ControlFlowNode for a2 | Flow found | -| test.py:611:12:611:13 | ControlFlowNode for a2 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:611:12:611:13 | ControlFlowNode for a2 | Flow found | -| test.py:611:12:611:13 | ControlFlowNode for a2 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:611:12:611:13 | ControlFlowNode for a2 | Flow found | -| test.py:612:10:612:11 | ControlFlowNode for a3 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:612:10:612:11 | ControlFlowNode for a3 | Flow found | -| test.py:612:10:612:11 | ControlFlowNode for a3 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:612:10:612:11 | ControlFlowNode for a3 | Flow found | -| test.py:612:10:612:11 | ControlFlowNode for a3 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:612:10:612:11 | ControlFlowNode for a3 | Flow found | -| test.py:622:10:622:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:622:10:622:11 | ControlFlowNode for a1 | Flow found | -| test.py:624:12:624:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:624:12:624:16 | ControlFlowNode for Subscript | Flow found | -| test.py:625:10:625:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:625:10:625:14 | ControlFlowNode for Subscript | Flow found | -| test.py:631:10:631:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:631:10:631:11 | ControlFlowNode for a1 | Flow found | -| test.py:633:12:633:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:633:12:633:16 | ControlFlowNode for Subscript | Flow found | -| test.py:634:10:634:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:634:10:634:14 | ControlFlowNode for Subscript | Flow found | -| test.py:640:10:640:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:640:10:640:11 | ControlFlowNode for a1 | Flow found | -| test.py:642:12:642:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:642:12:642:16 | ControlFlowNode for Subscript | Flow found | -| test.py:643:10:643:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:643:10:643:14 | ControlFlowNode for Subscript | Flow found | -| test.py:649:10:649:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:649:10:649:11 | ControlFlowNode for a1 | Flow found | -| test.py:651:12:651:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:651:12:651:16 | ControlFlowNode for Subscript | Flow found | -| test.py:652:10:652:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:652:10:652:14 | ControlFlowNode for Subscript | Flow found | -| test.py:660:10:660:10 | ControlFlowNode for a | test.py:659:19:659:24 | ControlFlowNode for SOURCE | test.py:660:10:660:10 | ControlFlowNode for a | Flow found | -| test.py:669:14:669:14 | ControlFlowNode for x | test.py:667:12:667:17 | ControlFlowNode for SOURCE | test.py:669:14:669:14 | ControlFlowNode for x | Flow found | -| test.py:669:14:669:14 | ControlFlowNode for x | test.py:667:33:667:38 | ControlFlowNode for SOURCE | test.py:669:14:669:14 | ControlFlowNode for x | Flow found | -| test.py:678:14:678:17 | ControlFlowNode for Subscript | test.py:675:12:675:17 | ControlFlowNode for SOURCE | test.py:678:14:678:17 | ControlFlowNode for Subscript | Flow found | -| test.py:678:14:678:17 | ControlFlowNode for Subscript | test.py:675:33:675:38 | ControlFlowNode for SOURCE | test.py:678:14:678:17 | ControlFlowNode for Subscript | Flow found | -| test.py:679:16:679:16 | ControlFlowNode for y | test.py:675:12:675:17 | ControlFlowNode for SOURCE | test.py:679:16:679:16 | ControlFlowNode for y | Flow found | -| test.py:679:16:679:16 | ControlFlowNode for y | test.py:675:33:675:38 | ControlFlowNode for SOURCE | test.py:679:16:679:16 | ControlFlowNode for y | Flow found | -| test.py:686:14:686:14 | ControlFlowNode for x | test.py:684:12:684:17 | ControlFlowNode for SOURCE | test.py:686:14:686:14 | ControlFlowNode for x | Flow found | -| test.py:686:14:686:14 | ControlFlowNode for x | test.py:684:33:684:38 | ControlFlowNode for SOURCE | test.py:686:14:686:14 | ControlFlowNode for x | Flow found | -| test.py:692:10:692:12 | ControlFlowNode for arg | test.py:697:7:697:12 | ControlFlowNode for SOURCE | test.py:692:10:692:12 | ControlFlowNode for arg | Flow found | -| test.py:692:10:692:12 | ControlFlowNode for arg | test.py:698:43:698:48 | ControlFlowNode for SOURCE | test.py:692:10:692:12 | ControlFlowNode for arg | Flow found | -| test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() | test.py:769:16:769:21 | ControlFlowNode for SOURCE | test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() | Flow found | -| test.py:808:10:808:10 | ControlFlowNode for x | test.py:807:37:807:42 | ControlFlowNode for SOURCE | test.py:808:10:808:10 | ControlFlowNode for x | Flow found | -| test.py:809:10:809:10 | ControlFlowNode for y | test.py:807:50:807:55 | ControlFlowNode for SOURCE | test.py:809:10:809:10 | ControlFlowNode for y | Flow found | -| test.py:810:10:810:10 | ControlFlowNode for z | test.py:807:63:807:68 | ControlFlowNode for SOURCE | test.py:810:10:810:10 | ControlFlowNode for z | Flow found | diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.ql b/python/ql/test/experimental/dataflow/coverage/dataflow.ql deleted file mode 100644 index 868f24a598f..00000000000 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.ql +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @kind path-problem - */ - -import python -import experimental.dataflow.testConfig -import DataFlow::PathGraph - -from TestConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink -where config.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "Flow found" From 0f34752f8f25821fe61b52a292256ba5fb04a307 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 1 Jul 2022 11:56:50 +0200 Subject: [PATCH 0028/1420] Python: Delete `classesCallGraph.ql` I don't see the value from this, so just going to outright delete it. (it actually stayed alive for quite some time in the original git history, but never seemed to be that useful.) --- .../coverage/classesCallGraph.expected | 80 ------------------- .../dataflow/coverage/classesCallGraph.ql | 37 --------- 2 files changed, 117 deletions(-) delete mode 100644 python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected delete mode 100644 python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected deleted file mode 100644 index f297c4be6e3..00000000000 --- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected +++ /dev/null @@ -1,80 +0,0 @@ -| classes.py:14:17:14:60 | ControlFlowNode for Attribute() | classes.py:14:17:14:60 | ControlFlowNode for Attribute() | -| classes.py:45:16:45:35 | ControlFlowNode for Attribute() | classes.py:45:16:45:35 | ControlFlowNode for Attribute() | -| classes.py:60:17:60:27 | [pre objCreate] ControlFlowNode for With_init() | classes.py:54:18:54:21 | ControlFlowNode for self | -| classes.py:242:9:242:24 | ControlFlowNode for set() | classes.py:242:9:242:24 | ControlFlowNode for set() | -| classes.py:247:9:247:30 | ControlFlowNode for frozenset() | classes.py:247:9:247:30 | ControlFlowNode for frozenset() | -| classes.py:252:9:252:28 | ControlFlowNode for dict() | classes.py:252:9:252:28 | ControlFlowNode for dict() | -| classes.py:559:16:559:17 | ControlFlowNode for Str | classes.py:565:5:565:22 | ControlFlowNode for Subscript | -| classes.py:565:5:565:16 | ControlFlowNode for with_getitem | classes.py:555:21:555:24 | ControlFlowNode for self | -| classes.py:565:18:565:21 | ControlFlowNode for arg2 | classes.py:555:27:555:29 | ControlFlowNode for key | -| classes.py:581:5:581:16 | ControlFlowNode for with_setitem | classes.py:570:21:570:24 | ControlFlowNode for self | -| classes.py:581:18:581:21 | ControlFlowNode for arg2 | classes.py:570:27:570:29 | ControlFlowNode for key | -| classes.py:581:26:581:29 | ControlFlowNode for arg3 | classes.py:570:32:570:36 | ControlFlowNode for value | -| classes.py:595:9:595:20 | ControlFlowNode for with_delitem | classes.py:586:21:586:24 | ControlFlowNode for self | -| classes.py:595:22:595:25 | ControlFlowNode for arg2 | classes.py:586:27:586:29 | ControlFlowNode for key | -| classes.py:618:16:618:28 | ControlFlowNode for Attribute() | classes.py:618:16:618:28 | ControlFlowNode for Attribute() | -| classes.py:659:15:659:18 | ControlFlowNode for self | classes.py:667:5:667:19 | ControlFlowNode for BinaryExpr | -| classes.py:661:16:661:19 | ControlFlowNode for self | classes.py:667:5:667:19 | ControlFlowNode for BinaryExpr | -| classes.py:667:5:667:12 | ControlFlowNode for with_add | classes.py:657:17:657:20 | ControlFlowNode for self | -| classes.py:667:5:667:12 | ControlFlowNode for with_add | classes.py:667:5:667:19 | ControlFlowNode for BinaryExpr | -| classes.py:667:16:667:19 | ControlFlowNode for arg2 | classes.py:657:23:657:27 | ControlFlowNode for other | -| classes.py:674:15:674:18 | ControlFlowNode for self | classes.py:682:5:682:19 | ControlFlowNode for BinaryExpr | -| classes.py:676:16:676:19 | ControlFlowNode for self | classes.py:682:5:682:19 | ControlFlowNode for BinaryExpr | -| classes.py:682:5:682:12 | ControlFlowNode for with_sub | classes.py:672:17:672:20 | ControlFlowNode for self | -| classes.py:682:5:682:12 | ControlFlowNode for with_sub | classes.py:682:5:682:19 | ControlFlowNode for BinaryExpr | -| classes.py:682:16:682:19 | ControlFlowNode for arg2 | classes.py:672:23:672:27 | ControlFlowNode for other | -| classes.py:689:15:689:18 | ControlFlowNode for self | classes.py:697:5:697:19 | ControlFlowNode for BinaryExpr | -| classes.py:691:16:691:19 | ControlFlowNode for self | classes.py:697:5:697:19 | ControlFlowNode for BinaryExpr | -| classes.py:697:5:697:12 | ControlFlowNode for with_mul | classes.py:687:17:687:20 | ControlFlowNode for self | -| classes.py:697:5:697:12 | ControlFlowNode for with_mul | classes.py:697:5:697:19 | ControlFlowNode for BinaryExpr | -| classes.py:697:16:697:19 | ControlFlowNode for arg2 | classes.py:687:23:687:27 | ControlFlowNode for other | -| classes.py:704:15:704:18 | ControlFlowNode for self | classes.py:712:5:712:22 | ControlFlowNode for BinaryExpr | -| classes.py:706:16:706:19 | ControlFlowNode for self | classes.py:712:5:712:22 | ControlFlowNode for BinaryExpr | -| classes.py:712:5:712:15 | ControlFlowNode for with_matmul | classes.py:702:20:702:23 | ControlFlowNode for self | -| classes.py:712:5:712:15 | ControlFlowNode for with_matmul | classes.py:712:5:712:22 | ControlFlowNode for BinaryExpr | -| classes.py:712:19:712:22 | ControlFlowNode for arg2 | classes.py:702:26:702:30 | ControlFlowNode for other | -| classes.py:719:15:719:18 | ControlFlowNode for self | classes.py:727:5:727:23 | ControlFlowNode for BinaryExpr | -| classes.py:721:16:721:19 | ControlFlowNode for self | classes.py:727:5:727:23 | ControlFlowNode for BinaryExpr | -| classes.py:727:5:727:16 | ControlFlowNode for with_truediv | classes.py:717:21:717:24 | ControlFlowNode for self | -| classes.py:727:5:727:16 | ControlFlowNode for with_truediv | classes.py:727:5:727:23 | ControlFlowNode for BinaryExpr | -| classes.py:727:20:727:23 | ControlFlowNode for arg2 | classes.py:717:27:717:31 | ControlFlowNode for other | -| classes.py:734:15:734:18 | ControlFlowNode for self | classes.py:742:5:742:25 | ControlFlowNode for BinaryExpr | -| classes.py:736:16:736:19 | ControlFlowNode for self | classes.py:742:5:742:25 | ControlFlowNode for BinaryExpr | -| classes.py:742:5:742:17 | ControlFlowNode for with_floordiv | classes.py:732:22:732:25 | ControlFlowNode for self | -| classes.py:742:5:742:17 | ControlFlowNode for with_floordiv | classes.py:742:5:742:25 | ControlFlowNode for BinaryExpr | -| classes.py:742:22:742:25 | ControlFlowNode for arg2 | classes.py:732:28:732:32 | ControlFlowNode for other | -| classes.py:749:15:749:18 | ControlFlowNode for self | classes.py:757:5:757:19 | ControlFlowNode for BinaryExpr | -| classes.py:751:16:751:19 | ControlFlowNode for self | classes.py:757:5:757:19 | ControlFlowNode for BinaryExpr | -| classes.py:757:5:757:12 | ControlFlowNode for with_mod | classes.py:747:17:747:20 | ControlFlowNode for self | -| classes.py:757:5:757:12 | ControlFlowNode for with_mod | classes.py:757:5:757:19 | ControlFlowNode for BinaryExpr | -| classes.py:757:16:757:19 | ControlFlowNode for arg2 | classes.py:747:23:747:27 | ControlFlowNode for other | -| classes.py:779:15:779:18 | ControlFlowNode for self | classes.py:793:5:793:20 | ControlFlowNode for BinaryExpr | -| classes.py:781:16:781:19 | ControlFlowNode for self | classes.py:793:5:793:20 | ControlFlowNode for BinaryExpr | -| classes.py:793:5:793:12 | ControlFlowNode for with_pow | classes.py:777:17:777:20 | ControlFlowNode for self | -| classes.py:793:5:793:12 | ControlFlowNode for with_pow | classes.py:793:5:793:20 | ControlFlowNode for BinaryExpr | -| classes.py:793:17:793:20 | ControlFlowNode for arg2 | classes.py:777:23:777:27 | ControlFlowNode for other | -| classes.py:800:15:800:18 | ControlFlowNode for self | classes.py:808:5:808:23 | ControlFlowNode for BinaryExpr | -| classes.py:802:16:802:19 | ControlFlowNode for self | classes.py:808:5:808:23 | ControlFlowNode for BinaryExpr | -| classes.py:808:5:808:15 | ControlFlowNode for with_lshift | classes.py:798:20:798:23 | ControlFlowNode for self | -| classes.py:808:5:808:15 | ControlFlowNode for with_lshift | classes.py:808:5:808:23 | ControlFlowNode for BinaryExpr | -| classes.py:808:20:808:23 | ControlFlowNode for arg2 | classes.py:798:26:798:30 | ControlFlowNode for other | -| classes.py:815:15:815:18 | ControlFlowNode for self | classes.py:823:5:823:23 | ControlFlowNode for BinaryExpr | -| classes.py:817:16:817:19 | ControlFlowNode for self | classes.py:823:5:823:23 | ControlFlowNode for BinaryExpr | -| classes.py:823:5:823:15 | ControlFlowNode for with_rshift | classes.py:813:20:813:23 | ControlFlowNode for self | -| classes.py:823:5:823:15 | ControlFlowNode for with_rshift | classes.py:823:5:823:23 | ControlFlowNode for BinaryExpr | -| classes.py:823:20:823:23 | ControlFlowNode for arg2 | classes.py:813:26:813:30 | ControlFlowNode for other | -| classes.py:830:15:830:18 | ControlFlowNode for self | classes.py:838:5:838:19 | ControlFlowNode for BinaryExpr | -| classes.py:832:16:832:19 | ControlFlowNode for self | classes.py:838:5:838:19 | ControlFlowNode for BinaryExpr | -| classes.py:838:5:838:12 | ControlFlowNode for with_and | classes.py:828:17:828:20 | ControlFlowNode for self | -| classes.py:838:5:838:12 | ControlFlowNode for with_and | classes.py:838:5:838:19 | ControlFlowNode for BinaryExpr | -| classes.py:838:16:838:19 | ControlFlowNode for arg2 | classes.py:828:23:828:27 | ControlFlowNode for other | -| classes.py:845:15:845:18 | ControlFlowNode for self | classes.py:853:5:853:19 | ControlFlowNode for BinaryExpr | -| classes.py:847:16:847:19 | ControlFlowNode for self | classes.py:853:5:853:19 | ControlFlowNode for BinaryExpr | -| classes.py:853:5:853:12 | ControlFlowNode for with_xor | classes.py:843:17:843:20 | ControlFlowNode for self | -| classes.py:853:5:853:12 | ControlFlowNode for with_xor | classes.py:853:5:853:19 | ControlFlowNode for BinaryExpr | -| classes.py:853:16:853:19 | ControlFlowNode for arg2 | classes.py:843:23:843:27 | ControlFlowNode for other | -| classes.py:860:15:860:18 | ControlFlowNode for self | classes.py:868:5:868:18 | ControlFlowNode for BinaryExpr | -| classes.py:862:16:862:19 | ControlFlowNode for self | classes.py:868:5:868:18 | ControlFlowNode for BinaryExpr | -| classes.py:868:5:868:11 | ControlFlowNode for with_or | classes.py:858:16:858:19 | ControlFlowNode for self | -| classes.py:868:5:868:11 | ControlFlowNode for with_or | classes.py:868:5:868:18 | ControlFlowNode for BinaryExpr | -| classes.py:868:15:868:18 | ControlFlowNode for arg2 | classes.py:858:22:858:26 | ControlFlowNode for other | diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql deleted file mode 100644 index c1b66d0f323..00000000000 --- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql +++ /dev/null @@ -1,37 +0,0 @@ -import semmle.python.dataflow.new.DataFlow -private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate - -/** - * A configuration to find the call graph edges. - */ -class CallGraphConfig extends DataFlow::Configuration { - CallGraphConfig() { this = "CallGraphConfig" } - - override predicate isSource(DataFlow::Node node) { - node instanceof DataFlowPrivate::ReturnNode - or - // These sources should allow for the non-standard call syntax - node instanceof DataFlow::ArgumentNode - } - - override predicate isSink(DataFlow::Node node) { - node instanceof DataFlowPrivate::OutNode - or - node instanceof DataFlow::ParameterNode and - // exclude parameters to the SINK-functions - not exists(DataFlowPrivate::DataFlowCallable c | - c.getParameter(_) = node.asCfgNode() and - c.getName().matches("SINK_") - ) - } -} - -from DataFlow::Node source, DataFlow::Node sink -where - source.getLocation().getFile().getBaseName() = "classes.py" and - sink.getLocation().getFile().getBaseName() = "classes.py" and - exists(CallGraphConfig cfg | cfg.hasFlow(source, sink)) -select source, sink -// Ideally, we would just have 1-step paths either from argument to parameter -// or from return to call. This gives a bit more, so should be rewritten. -// We should also consider splitting this into two, one for each direction. From c1b256159817c5d7e400a942577d328a099df620 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 22 Jun 2022 15:06:21 +0200 Subject: [PATCH 0029/1420] Python: Extend fieldflow tests with bound method call --- .../experimental/dataflow/fieldflow/test.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/python/ql/test/experimental/dataflow/fieldflow/test.py b/python/ql/test/experimental/dataflow/fieldflow/test.py index d8d4b5f6fe0..c7a2bc50a14 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/test.py +++ b/python/ql/test/experimental/dataflow/fieldflow/test.py @@ -160,6 +160,40 @@ def test_nested_obj_method(): a.getObj().foo = x SINK(a.obj.foo) # $ flow="SOURCE, l:-3 -> a.obj.foo" +# ------------------------------------------------------------------------------ +# Bound Method calls +# ------------------------------------------------------------------------------ + +class Foo: + def __init__(self, x): + self.x = x + + def update_x(self, x): + self.x = x + +@expects(7) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) +def test_bound_method_call(): + # direct assignment + foo = Foo(None) + SINK_F(foo.x) + foo.x = SOURCE + SINK(foo.x) # $ flow="SOURCE, l:-1 -> foo.x" + foo.x = None + SINK_F(foo.x) + + # assignment through function + foo = Foo(SOURCE) + SINK(foo.x) # $ flow="SOURCE, l:-1 -> foo.x" + foo.update_x(None) + SINK_F(foo.x) # $ flow="SOURCE, l:-3 -> foo.x" + + # assignment through bound-method calls + foo = Foo(SOURCE) + ux = foo.update_x + SINK(foo.x) # $ flow="SOURCE, l:-2 -> foo.x" + ux(None) + SINK_F(foo.x) # $ SPURIOUS: flow="SOURCE, l:-4 -> foo.x" + # ------------------------------------------------------------------------------ # Global scope # ------------------------------------------------------------------------------ From 6577281bed5a7c9bc4696f01f07362b5fe4c8ede Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 28 Jul 2022 10:57:08 +0200 Subject: [PATCH 0030/1420] Python: Add crosstalk fieldflow test --- .../experimental/dataflow/fieldflow/test.py | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/python/ql/test/experimental/dataflow/fieldflow/test.py b/python/ql/test/experimental/dataflow/fieldflow/test.py index c7a2bc50a14..70db8554241 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/test.py +++ b/python/ql/test/experimental/dataflow/fieldflow/test.py @@ -194,6 +194,128 @@ def test_bound_method_call(): ux(None) SINK_F(foo.x) # $ SPURIOUS: flow="SOURCE, l:-4 -> foo.x" + +# ------------------------------------------------------------------------------ +# Crosstalk test -- using different function based on conditional +# ------------------------------------------------------------------------------ + +class CrosstalkTestX: + def __init__(self): + self.x = None + self.y = None + + def setx(self, value): + self.x = value + + def setvalue(self, value): + self.x = value + + +class CrosstalkTestY: + def __init__(self): + self.x = None + self.y = None + + def sety(self ,value): + self.y = value + + def setvalue(self, value): + self.y = value + + +@expects(8) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) +def test_no_crosstalk_reference(cond=True): + objx = CrosstalkTestX() + SINK_F(objx.x) + SINK_F(objx.y) + + objy = CrosstalkTestY() + SINK_F(objy.x) + SINK_F(objy.y) + + if cond: + objx.setvalue(SOURCE) + else: + objy.setvalue(SOURCE) + + SINK(objx.x) # $ flow="SOURCE, l:-4 -> objx.x" + SINK_F(objx.y) + SINK_F(objy.x) + SINK_F(objy.y) # $ flow="SOURCE, l:-5 -> objy.y" + + +@expects(8) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) +def test_potential_crosstalk_different_name(cond=True): + objx = CrosstalkTestX() + SINK_F(objx.x) + SINK_F(objx.y) + + objy = CrosstalkTestY() + SINK_F(objy.x) + SINK_F(objy.y) + + if cond: + func = objx.setx + else: + func = objy.sety + + func(SOURCE) + + SINK(objx.x) # $ MISSING: flow="SOURCE, l:-2 -> objx.x" + SINK_F(objx.y) + SINK_F(objy.x) + SINK_F(objy.y) # $ MISSING: flow="SOURCE, l:-5 -> objy.y" + + +@expects(8) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) +def test_potential_crosstalk_same_name(cond=True): + objx = CrosstalkTestX() + SINK_F(objx.x) + SINK_F(objx.y) + + objy = CrosstalkTestY() + SINK_F(objy.x) + SINK_F(objy.y) + + if cond: + func = objx.setvalue + else: + func = objy.setvalue + + func(SOURCE) + + SINK(objx.x) # $ MISSING: flow="SOURCE, l:-2 -> objx.x" + SINK_F(objx.y) + SINK_F(objy.x) + SINK_F(objy.y) # $ MISSING: flow="SOURCE, l:-5 -> objy.y" + + +@expects(10) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) +def test_potential_crosstalk_same_name_object_reference(cond=True): + objx = CrosstalkTestX() + SINK_F(objx.x) + SINK_F(objx.y) + + objy = CrosstalkTestY() + SINK_F(objy.x) + SINK_F(objy.y) + + if cond: + obj = objx + else: + obj = objy + + obj.setvalue(SOURCE) + + SINK(objx.x) # $ MISSING: flow="SOURCE, l:-2 -> objx.x" + SINK_F(objx.y) + SINK_F(objy.x) + SINK_F(objy.y) # $ MISSING: flow="SOURCE, l:-5 -> objy.y" + + SINK(obj.x) # $ flow="SOURCE, l:-7 -> obj.x" + SINK_F(obj.y) # $ flow="SOURCE, l:-8 -> obj.y" + + # ------------------------------------------------------------------------------ # Global scope # ------------------------------------------------------------------------------ From e8fdff7a3bea1c12d87ae5e33958f8aaf71ef534 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 16 Aug 2022 14:48:41 +0200 Subject: [PATCH 0031/1420] Python: Expand ExternalAPIs test We never had a showcase of how keyword arguments were handled --- .../ExternalAPIsUsedWithUntrustedData.expected | 2 +- .../UntrustedDataToExternalAPI.expected | 7 +++++++ .../Security/CWE-020-ExternalAPIs/test.py | 12 ++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/ExternalAPIsUsedWithUntrustedData.expected b/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/ExternalAPIsUsedWithUntrustedData.expected index c070169615c..7438c415858 100644 --- a/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/ExternalAPIsUsedWithUntrustedData.expected +++ b/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/ExternalAPIsUsedWithUntrustedData.expected @@ -1 +1 @@ -| hmac.new [param 1] | 1 | 1 | +| hmac.new [param 1] | 2 | 1 | diff --git a/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/UntrustedDataToExternalAPI.expected b/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/UntrustedDataToExternalAPI.expected index c64a6943813..e024ef20cba 100644 --- a/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/UntrustedDataToExternalAPI.expected +++ b/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/UntrustedDataToExternalAPI.expected @@ -1,9 +1,12 @@ edges | test.py:0:0:0:0 | ModuleVariableNode for test.request | test.py:13:16:13:22 | ControlFlowNode for request | +| test.py:0:0:0:0 | ModuleVariableNode for test.request | test.py:23:16:23:22 | ControlFlowNode for request | | test.py:5:26:5:32 | ControlFlowNode for ImportMember | test.py:5:26:5:32 | GSSA Variable request | | test.py:5:26:5:32 | GSSA Variable request | test.py:0:0:0:0 | ModuleVariableNode for test.request | | test.py:13:16:13:22 | ControlFlowNode for request | test.py:13:16:13:27 | ControlFlowNode for Attribute | | test.py:13:16:13:27 | ControlFlowNode for Attribute | test.py:15:36:15:39 | ControlFlowNode for data | +| test.py:23:16:23:22 | ControlFlowNode for request | test.py:23:16:23:27 | ControlFlowNode for Attribute | +| test.py:23:16:23:27 | ControlFlowNode for Attribute | test.py:25:44:25:47 | ControlFlowNode for data | nodes | test.py:0:0:0:0 | ModuleVariableNode for test.request | semmle.label | ModuleVariableNode for test.request | | test.py:5:26:5:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | @@ -11,6 +14,10 @@ nodes | test.py:13:16:13:22 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | test.py:13:16:13:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | test.py:15:36:15:39 | ControlFlowNode for data | semmle.label | ControlFlowNode for data | +| test.py:23:16:23:22 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test.py:23:16:23:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:25:44:25:47 | ControlFlowNode for data | semmle.label | ControlFlowNode for data | subpaths #select | test.py:15:36:15:39 | ControlFlowNode for data | test.py:5:26:5:32 | ControlFlowNode for ImportMember | test.py:15:36:15:39 | ControlFlowNode for data | Call to hmac.new [param 1] with untrusted data from $@. | test.py:5:26:5:32 | ControlFlowNode for ImportMember | ControlFlowNode for ImportMember | +| test.py:25:44:25:47 | ControlFlowNode for data | test.py:5:26:5:32 | ControlFlowNode for ImportMember | test.py:25:44:25:47 | ControlFlowNode for data | Call to hmac.new [param 1] with untrusted data from $@. | test.py:5:26:5:32 | ControlFlowNode for ImportMember | ControlFlowNode for ImportMember | diff --git a/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/test.py b/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/test.py index b88748fbb29..ca4191ded85 100644 --- a/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/test.py +++ b/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/test.py @@ -18,11 +18,22 @@ def hmac_example(): return "ok" +@app.route("/hmac-example2") +def hmac_example2(): + data_raw = request.args.get("data").encode('utf-8') + data = base64.decodebytes(data_raw) + my_hmac = hmac.new(key=SECRET_KEY, msg=data, digestmod=hashlib.sha256) + digest = my_hmac.digest() + print(digest) + return "ok" + + @app.route("/unknown-lib-1") def unknown_lib_1(): from unknown.lib import func data = request.args.get("data") func(data) # TODO: currently not recognized + func(kw=data) # TODO: currently not recognized @app.route("/unknown-lib-2") @@ -30,6 +41,7 @@ def unknown_lib_2(): import unknown.lib data = request.args.get("data") unknown.lib.func(data) # TODO: currently not recognized + unknown.lib.func(kw=data) # TODO: currently not recognized if __name__ == "__main__": From 08bc14f59806387599cfe38463367a2761f13cf1 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Tue, 1 Nov 2022 22:50:13 +0100 Subject: [PATCH 0032/1420] add failing test --- .../CWE-400/ReDoS/PolynomialBackTracking.expected | 2 ++ .../Security/CWE-400/ReDoS/PolynomialReDoS.expected | 9 +++++++++ .../test/query-tests/Security/CWE-400/ReDoS/lib/lib.js | 9 ++++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialBackTracking.expected b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialBackTracking.expected index e1ad3adb91c..6ed2af353f5 100644 --- a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialBackTracking.expected +++ b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialBackTracking.expected @@ -33,6 +33,8 @@ | lib/lib.js:8:3:8:4 | f* | Strings with many repetitions of 'f' can start matching anywhere after the start of the preceeding f*g | | lib/lib.js:28:3:28:4 | f* | Strings with many repetitions of 'f' can start matching anywhere after the start of the preceeding f*g | | lib/lib.js:36:3:36:4 | f* | Strings with many repetitions of 'f' can start matching anywhere after the start of the preceeding f*g | +| lib/lib.js:42:29:42:30 | f* | Strings with many repetitions of 'f' can start matching anywhere after the start of the preceeding f*g | +| lib/lib.js:45:29:45:30 | f* | Strings with many repetitions of 'f' can start matching anywhere after the start of the preceeding f*g | | lib/moduleLib/moduleLib.js:2:3:2:4 | a* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding a*b | | lib/otherLib/js/src/index.js:2:3:2:4 | a* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding a*b | | lib/snapdragon.js:7:28:7:29 | a* | Strings starting with 'a' and with many repetitions of 'a' can start matching anywhere after the start of the preceeding aa*$ | diff --git a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialReDoS.expected b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialReDoS.expected index 3df7db24964..c4076c927cd 100644 --- a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialReDoS.expected +++ b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialReDoS.expected @@ -28,6 +28,10 @@ nodes | lib/lib.js:35:28:35:31 | name | | lib/lib.js:36:13:36:16 | name | | lib/lib.js:36:13:36:16 | name | +| lib/lib.js:41:32:41:35 | name | +| lib/lib.js:41:32:41:35 | name | +| lib/lib.js:42:17:42:20 | name | +| lib/lib.js:42:17:42:20 | name | | lib/moduleLib/moduleLib.js:1:28:1:31 | name | | lib/moduleLib/moduleLib.js:1:28:1:31 | name | | lib/moduleLib/moduleLib.js:2:13:2:16 | name | @@ -249,6 +253,10 @@ edges | lib/lib.js:35:1:37:1 | 'arguments' object of function usedWithArguments | lib/lib.js:35:28:35:31 | name | | lib/lib.js:35:28:35:31 | name | lib/lib.js:36:13:36:16 | name | | lib/lib.js:35:28:35:31 | name | lib/lib.js:36:13:36:16 | name | +| lib/lib.js:41:32:41:35 | name | lib/lib.js:42:17:42:20 | name | +| lib/lib.js:41:32:41:35 | name | lib/lib.js:42:17:42:20 | name | +| lib/lib.js:41:32:41:35 | name | lib/lib.js:42:17:42:20 | name | +| lib/lib.js:41:32:41:35 | name | lib/lib.js:42:17:42:20 | name | | lib/moduleLib/moduleLib.js:1:28:1:31 | name | lib/moduleLib/moduleLib.js:2:13:2:16 | name | | lib/moduleLib/moduleLib.js:1:28:1:31 | name | lib/moduleLib/moduleLib.js:2:13:2:16 | name | | lib/moduleLib/moduleLib.js:1:28:1:31 | name | lib/moduleLib/moduleLib.js:2:13:2:16 | name | @@ -440,6 +448,7 @@ edges | lib/lib.js:4:2:4:18 | regexp.test(name) | lib/lib.js:3:28:3:31 | name | lib/lib.js:4:14:4:17 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/lib.js:1:15:1:16 | a* | regular expression | lib/lib.js:3:28:3:31 | name | library input | | lib/lib.js:8:2:8:17 | /f*g/.test(name) | lib/lib.js:7:19:7:22 | name | lib/lib.js:8:13:8:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'f'. | lib/lib.js:8:3:8:4 | f* | regular expression | lib/lib.js:7:19:7:22 | name | library input | | lib/lib.js:36:2:36:17 | /f*g/.test(name) | lib/lib.js:32:32:32:40 | arguments | lib/lib.js:36:13:36:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'f'. | lib/lib.js:36:3:36:4 | f* | regular expression | lib/lib.js:32:32:32:40 | arguments | library input | +| lib/lib.js:42:17:42:33 | name.match(/f*g/) | lib/lib.js:41:32:41:35 | name | lib/lib.js:42:17:42:20 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'f'. | lib/lib.js:42:29:42:30 | f* | regular expression | lib/lib.js:41:32:41:35 | name | library input | | lib/moduleLib/moduleLib.js:2:2:2:17 | /a*b/.test(name) | lib/moduleLib/moduleLib.js:1:28:1:31 | name | lib/moduleLib/moduleLib.js:2:13:2:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/moduleLib/moduleLib.js:2:3:2:4 | a* | regular expression | lib/moduleLib/moduleLib.js:1:28:1:31 | name | library input | | lib/otherLib/js/src/index.js:2:2:2:17 | /a*b/.test(name) | lib/otherLib/js/src/index.js:1:28:1:31 | name | lib/otherLib/js/src/index.js:2:13:2:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/otherLib/js/src/index.js:2:3:2:4 | a* | regular expression | lib/otherLib/js/src/index.js:1:28:1:31 | name | library input | | lib/snapdragon.js:7:15:7:32 | this.match(/aa*$/) | lib/snapdragon.js:3:34:3:38 | input | lib/snapdragon.js:7:15:7:18 | this | This $@ that depends on $@ may run slow on strings starting with 'a' and with many repetitions of 'a'. | lib/snapdragon.js:7:28:7:29 | a* | regular expression | lib/snapdragon.js:3:34:3:38 | input | library input | diff --git a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/lib/lib.js b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/lib/lib.js index 5c892f328a3..87b7e8292d2 100644 --- a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/lib/lib.js +++ b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/lib/lib.js @@ -36,4 +36,11 @@ function usedWithArguments(name) { /f*g/.test(name); // NOT OK } -module.exports.snapdragon = require("./snapdragon") \ No newline at end of file +module.exports.snapdragon = require("./snapdragon") + +module.exports.foo = function (name) { + var data1 = name.match(/f*g/); // NOT OK + + name = name.substr(1); + var data2 = name.match(/f*g/); // NOT OK - but not flagged +} \ No newline at end of file From 851d53d56b69254efcb2c1898b0e47d9fe53daa8 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Tue, 1 Nov 2022 22:51:07 +0100 Subject: [PATCH 0033/1420] don't sanitize calls through substring calls that just remove the start --- .../regexp/PolynomialReDoSCustomizations.qll | 3 ++- .../Security/CWE-400/ReDoS/PolynomialReDoS.expected | 12 ++++++++++++ .../query-tests/Security/CWE-400/ReDoS/lib/lib.js | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/security/regexp/PolynomialReDoSCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/regexp/PolynomialReDoSCustomizations.qll index 87f9437196f..508eaf40e2c 100644 --- a/javascript/ql/lib/semmle/javascript/security/regexp/PolynomialReDoSCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/regexp/PolynomialReDoSCustomizations.qll @@ -90,7 +90,8 @@ module PolynomialReDoS { isCharClassLike(root) ) or - this.(DataFlow::MethodCallNode).getMethodName() = StringOps::substringMethodName() + this.(DataFlow::MethodCallNode).getMethodName() = StringOps::substringMethodName() and + not this.(DataFlow::MethodCallNode).getNumArgument() = 1 // with one argument it just slices off the beginning } } diff --git a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialReDoS.expected b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialReDoS.expected index c4076c927cd..04bf2cbad36 100644 --- a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialReDoS.expected +++ b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialReDoS.expected @@ -32,6 +32,11 @@ nodes | lib/lib.js:41:32:41:35 | name | | lib/lib.js:42:17:42:20 | name | | lib/lib.js:42:17:42:20 | name | +| lib/lib.js:44:5:44:25 | name | +| lib/lib.js:44:12:44:15 | name | +| lib/lib.js:44:12:44:25 | name.substr(1) | +| lib/lib.js:45:17:45:20 | name | +| lib/lib.js:45:17:45:20 | name | | lib/moduleLib/moduleLib.js:1:28:1:31 | name | | lib/moduleLib/moduleLib.js:1:28:1:31 | name | | lib/moduleLib/moduleLib.js:2:13:2:16 | name | @@ -257,6 +262,12 @@ edges | lib/lib.js:41:32:41:35 | name | lib/lib.js:42:17:42:20 | name | | lib/lib.js:41:32:41:35 | name | lib/lib.js:42:17:42:20 | name | | lib/lib.js:41:32:41:35 | name | lib/lib.js:42:17:42:20 | name | +| lib/lib.js:41:32:41:35 | name | lib/lib.js:44:12:44:15 | name | +| lib/lib.js:41:32:41:35 | name | lib/lib.js:44:12:44:15 | name | +| lib/lib.js:44:5:44:25 | name | lib/lib.js:45:17:45:20 | name | +| lib/lib.js:44:5:44:25 | name | lib/lib.js:45:17:45:20 | name | +| lib/lib.js:44:12:44:15 | name | lib/lib.js:44:12:44:25 | name.substr(1) | +| lib/lib.js:44:12:44:25 | name.substr(1) | lib/lib.js:44:5:44:25 | name | | lib/moduleLib/moduleLib.js:1:28:1:31 | name | lib/moduleLib/moduleLib.js:2:13:2:16 | name | | lib/moduleLib/moduleLib.js:1:28:1:31 | name | lib/moduleLib/moduleLib.js:2:13:2:16 | name | | lib/moduleLib/moduleLib.js:1:28:1:31 | name | lib/moduleLib/moduleLib.js:2:13:2:16 | name | @@ -449,6 +460,7 @@ edges | lib/lib.js:8:2:8:17 | /f*g/.test(name) | lib/lib.js:7:19:7:22 | name | lib/lib.js:8:13:8:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'f'. | lib/lib.js:8:3:8:4 | f* | regular expression | lib/lib.js:7:19:7:22 | name | library input | | lib/lib.js:36:2:36:17 | /f*g/.test(name) | lib/lib.js:32:32:32:40 | arguments | lib/lib.js:36:13:36:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'f'. | lib/lib.js:36:3:36:4 | f* | regular expression | lib/lib.js:32:32:32:40 | arguments | library input | | lib/lib.js:42:17:42:33 | name.match(/f*g/) | lib/lib.js:41:32:41:35 | name | lib/lib.js:42:17:42:20 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'f'. | lib/lib.js:42:29:42:30 | f* | regular expression | lib/lib.js:41:32:41:35 | name | library input | +| lib/lib.js:45:17:45:33 | name.match(/f*g/) | lib/lib.js:41:32:41:35 | name | lib/lib.js:45:17:45:20 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'f'. | lib/lib.js:45:29:45:30 | f* | regular expression | lib/lib.js:41:32:41:35 | name | library input | | lib/moduleLib/moduleLib.js:2:2:2:17 | /a*b/.test(name) | lib/moduleLib/moduleLib.js:1:28:1:31 | name | lib/moduleLib/moduleLib.js:2:13:2:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/moduleLib/moduleLib.js:2:3:2:4 | a* | regular expression | lib/moduleLib/moduleLib.js:1:28:1:31 | name | library input | | lib/otherLib/js/src/index.js:2:2:2:17 | /a*b/.test(name) | lib/otherLib/js/src/index.js:1:28:1:31 | name | lib/otherLib/js/src/index.js:2:13:2:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/otherLib/js/src/index.js:2:3:2:4 | a* | regular expression | lib/otherLib/js/src/index.js:1:28:1:31 | name | library input | | lib/snapdragon.js:7:15:7:32 | this.match(/aa*$/) | lib/snapdragon.js:3:34:3:38 | input | lib/snapdragon.js:7:15:7:18 | this | This $@ that depends on $@ may run slow on strings starting with 'a' and with many repetitions of 'a'. | lib/snapdragon.js:7:28:7:29 | a* | regular expression | lib/snapdragon.js:3:34:3:38 | input | library input | diff --git a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/lib/lib.js b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/lib/lib.js index 87b7e8292d2..73700dfbc6b 100644 --- a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/lib/lib.js +++ b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/lib/lib.js @@ -42,5 +42,5 @@ module.exports.foo = function (name) { var data1 = name.match(/f*g/); // NOT OK name = name.substr(1); - var data2 = name.match(/f*g/); // NOT OK - but not flagged + var data2 = name.match(/f*g/); // NOT OK } \ No newline at end of file From 33cca29a8e8cfb7c22381caef10cf0a9149f091f Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Wed, 2 Nov 2022 11:23:01 +0100 Subject: [PATCH 0034/1420] drop down to the CFG instead of the AST to better support de-sugaring --- .../ruby/frameworks/StringFormatters.qll | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll b/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll index da9ce58345e..479507dd0e6 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/StringFormatters.qll @@ -87,15 +87,15 @@ class StringPercentCall extends PrintfStyleCall { override DataFlow::Node getFormatString() { result = this.getReceiver() } override DataFlow::Node getFormatArgument(int n) { - exists(Ast::Call call | call = this.asExpr().getExpr() | - exists(Ast::ArrayLiteral arrLit | arrLit = call.getArgument(0) | - result.asExpr().getExpr() = arrLit.getElement(n) - ) - or - exists(Ast::HashLiteral hashLit | hashLit = call.getArgument(0) | - n = -2 and // -2 is indicates that the index does not make sense in this context - result.asExpr().getExpr() = hashLit.getAnElement() - ) + exists(CfgNodes::ExprNodes::ArrayLiteralCfgNode arrLit | arrLit = this.getArgument(0).asExpr() | + result.asExpr() = arrLit.getArgument(n) + ) + or + exists(CfgNodes::ExprNodes::HashLiteralCfgNode hashLit | + hashLit = this.getArgument(0).asExpr() + | + n = -2 and // -2 is indicates that the index does not make sense in this context + result.asExpr() = hashLit.getAKeyValuePair().getValue() ) } From 1e3adcd14e172aff3a0a03c89181ca31ca091080 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 2 Nov 2022 11:37:37 +0100 Subject: [PATCH 0035/1420] Revert "Revert "SSA: Turn consistency predicates into `query` predicates"" --- .../ql/consistency-queries/SsaConsistency.ql | 12 ++--------- ruby/ql/consistency-queries/SsaConsistency.ql | 12 ++--------- shared/ssa/codeql/ssa/Ssa.qll | 20 +++++++++++-------- 3 files changed, 16 insertions(+), 28 deletions(-) diff --git a/csharp/ql/consistency-queries/SsaConsistency.ql b/csharp/ql/consistency-queries/SsaConsistency.ql index 71f88bf2ab0..bd666a71e49 100644 --- a/csharp/ql/consistency-queries/SsaConsistency.ql +++ b/csharp/ql/consistency-queries/SsaConsistency.ql @@ -1,8 +1,8 @@ import csharp -import semmle.code.csharp.dataflow.internal.SsaImpl::Consistency as Consistency +import semmle.code.csharp.dataflow.internal.SsaImpl::Consistency import Ssa -class MyRelevantDefinition extends Consistency::RelevantDefinition, Ssa::Definition { +class MyRelevantDefinition extends RelevantDefinition, Ssa::Definition { override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { @@ -10,14 +10,6 @@ class MyRelevantDefinition extends Consistency::RelevantDefinition, Ssa::Definit } } -query predicate nonUniqueDef = Consistency::nonUniqueDef/4; - -query predicate readWithoutDef = Consistency::readWithoutDef/3; - -query predicate deadDef = Consistency::deadDef/2; - -query predicate notDominatedByDef = Consistency::notDominatedByDef/4; - query predicate localDeclWithSsaDef(LocalVariableDeclExpr d) { // Local variables in C# must be initialized before every use, so uninitialized // local variables should not have an SSA definition, as that would imply that diff --git a/ruby/ql/consistency-queries/SsaConsistency.ql b/ruby/ql/consistency-queries/SsaConsistency.ql index 54c1b149ab2..7ba9262baa4 100644 --- a/ruby/ql/consistency-queries/SsaConsistency.ql +++ b/ruby/ql/consistency-queries/SsaConsistency.ql @@ -1,18 +1,10 @@ import codeql.ruby.dataflow.SSA -import codeql.ruby.dataflow.internal.SsaImpl::Consistency as Consistency +import codeql.ruby.dataflow.internal.SsaImpl::Consistency -class MyRelevantDefinition extends Consistency::RelevantDefinition, Ssa::Definition { +class MyRelevantDefinition extends RelevantDefinition, Ssa::Definition { override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) } } - -query predicate nonUniqueDef = Consistency::nonUniqueDef/4; - -query predicate readWithoutDef = Consistency::readWithoutDef/3; - -query predicate deadDef = Consistency::deadDef/2; - -query predicate notDominatedByDef = Consistency::notDominatedByDef/4; diff --git a/shared/ssa/codeql/ssa/Ssa.qll b/shared/ssa/codeql/ssa/Ssa.qll index 886e4128e26..19f31f7c8bb 100644 --- a/shared/ssa/codeql/ssa/Ssa.qll +++ b/shared/ssa/codeql/ssa/Ssa.qll @@ -9,7 +9,10 @@ signature module InputSig { * A basic block, that is, a maximal straight-line sequence of control flow nodes * without branches or joins. */ - class BasicBlock; + class BasicBlock { + /** Gets a textual representation of this basic block. */ + string toString(); + } /** * Gets the basic block that immediately dominates basic block `bb`, if any. @@ -43,7 +46,10 @@ signature module InputSig { class ExitBasicBlock extends BasicBlock; /** A variable that can be SSA converted. */ - class SourceVariable; + class SourceVariable { + /** Gets a textual representation of this variable. */ + string toString(); + } /** * Holds if the `i`th node of basic block `bb` is a (potential) write to source @@ -846,8 +852,6 @@ module Make { } /** Provides a set of consistency queries. */ - // TODO: Make these `query` predicates once class signatures are supported - // (`SourceVariable` and `BasicBlock` must have `toString`) module Consistency { /** A definition that is relevant for the consistency queries. */ abstract class RelevantDefinition extends Definition { @@ -858,19 +862,19 @@ module Make { } /** Holds if a read can be reached from multiple definitions. */ - predicate nonUniqueDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) { + query predicate nonUniqueDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) { ssaDefReachesRead(v, def, bb, i) and not exists(unique(Definition def0 | ssaDefReachesRead(v, def0, bb, i))) } /** Holds if a read cannot be reached from a definition. */ - predicate readWithoutDef(SourceVariable v, BasicBlock bb, int i) { + query predicate readWithoutDef(SourceVariable v, BasicBlock bb, int i) { variableRead(bb, i, v, _) and not ssaDefReachesRead(v, _, bb, i) } /** Holds if a definition cannot reach a read. */ - predicate deadDef(RelevantDefinition def, SourceVariable v) { + query predicate deadDef(RelevantDefinition def, SourceVariable v) { v = def.getSourceVariable() and not ssaDefReachesRead(_, def, _, _) and not phiHasInputFromBlock(_, def, _) and @@ -878,7 +882,7 @@ module Make { } /** Holds if a read is not dominated by a definition. */ - predicate notDominatedByDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) { + query predicate notDominatedByDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) { exists(BasicBlock bbDef, int iDef | def.definesAt(v, bbDef, iDef) | ssaDefReachesReadWithinBlock(v, def, bb, i) and (bb != bbDef or i < iDef) From 5d288d321af2bf9aa00118f3dcaf59f29edd84a3 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 2 Nov 2022 11:10:04 -0400 Subject: [PATCH 0036/1420] Use latest released bundle for QL-for-QL --- .github/workflows/ql-for-ql-build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ql-for-ql-build.yml b/.github/workflows/ql-for-ql-build.yml index 53a4157973d..e3cacf73d2e 100644 --- a/.github/workflows/ql-for-ql-build.yml +++ b/.github/workflows/ql-for-ql-build.yml @@ -27,6 +27,7 @@ jobs: uses: github/codeql-action/init@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca with: languages: javascript # does not matter + tools: latest - name: Get CodeQL version id: get-codeql-version run: | @@ -138,6 +139,7 @@ jobs: languages: ql db-location: ${{ runner.temp }}/db config-file: ./ql-for-ql-config.yml + tools: latest - name: Move pack cache run: | cp -r ${PACK}/.cache ql/ql/src/.cache From bb0b0801dde5fa69c12aac8c3d36094e7432552d Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 2 Nov 2022 11:38:11 -0400 Subject: [PATCH 0037/1420] Try again --- .github/actions/find-latest-bundle/action.yml | 20 +++++++++++++++++++ .github/workflows/ql-for-ql-build.yml | 5 ++++- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 .github/actions/find-latest-bundle/action.yml diff --git a/.github/actions/find-latest-bundle/action.yml b/.github/actions/find-latest-bundle/action.yml new file mode 100644 index 00000000000..3f4e089d476 --- /dev/null +++ b/.github/actions/find-latest-bundle/action.yml @@ -0,0 +1,20 @@ +name: Find Latest CodeQL Bundle +description: Finds the URL of the latest released version of the CodeQL bundle. +outputs: + url: + description: The download URL of the latest CodeQL bundle release + value: ${{ steps.find-latest.outputs.url }} +runs: + using: composite + steps: + - name: Find Latest Release + id: find-latest + shell: pwsh + run: | + $Latest = gh release list --exclude-drafts --limit 1000 | + ForEach-Object { $C = $_ -split "`t"; return @{ type = $C[1]; tag = $C[2]; } } | + Where-Object { $_.type -eq 'Latest' } + + $Tag = $Latest.tag + Write-Output "Latest bundle tag is '${Tag}'." + "url=https://github.com/github/codeql-action/releases/download/${Tag}/codeql-bundle-linux64.tar.gz" >> $env:GITHUB_OUTPUT diff --git a/.github/workflows/ql-for-ql-build.yml b/.github/workflows/ql-for-ql-build.yml index e3cacf73d2e..0af98b21e09 100644 --- a/.github/workflows/ql-for-ql-build.yml +++ b/.github/workflows/ql-for-ql-build.yml @@ -22,12 +22,15 @@ jobs: steps: ### Build the queries ### - uses: actions/checkout@v3 + - name: Find latest bundle + id: find-latest-bundle + uses: ./.github/actions/find-latest-bundle - name: Find codeql id: find-codeql uses: github/codeql-action/init@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca with: languages: javascript # does not matter - tools: latest + tools: ${{ steps.find-latest-bundle.outputs.url }} - name: Get CodeQL version id: get-codeql-version run: | From 631b8fed308c23d8673a93819193053e80d42646 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 2 Nov 2022 11:40:01 -0400 Subject: [PATCH 0038/1420] Add token --- .github/actions/find-latest-bundle/action.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/actions/find-latest-bundle/action.yml b/.github/actions/find-latest-bundle/action.yml index 3f4e089d476..669cdfb518c 100644 --- a/.github/actions/find-latest-bundle/action.yml +++ b/.github/actions/find-latest-bundle/action.yml @@ -18,3 +18,5 @@ runs: $Tag = $Latest.tag Write-Output "Latest bundle tag is '${Tag}'." "url=https://github.com/github/codeql-action/releases/download/${Tag}/codeql-bundle-linux64.tar.gz" >> $env:GITHUB_OUTPUT + env: + GITHUB_TOKEN: ${{ github.token }} From 906f2f5e0f3b51b864885e14c802928726bd611f Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 2 Nov 2022 11:42:39 -0400 Subject: [PATCH 0039/1420] Add repo --- .github/actions/find-latest-bundle/action.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/actions/find-latest-bundle/action.yml b/.github/actions/find-latest-bundle/action.yml index 669cdfb518c..59e16a6d3cb 100644 --- a/.github/actions/find-latest-bundle/action.yml +++ b/.github/actions/find-latest-bundle/action.yml @@ -11,11 +11,15 @@ runs: id: find-latest shell: pwsh run: | - $Latest = gh release list --exclude-drafts --limit 1000 | + $Latest = gh release list --repo github/codeql-action --exclude-drafts --limit 1000 | ForEach-Object { $C = $_ -split "`t"; return @{ type = $C[1]; tag = $C[2]; } } | Where-Object { $_.type -eq 'Latest' } $Tag = $Latest.tag + if ($Tag -eq '') { + throw 'Failed to find latest bundle release.' + } + Write-Output "Latest bundle tag is '${Tag}'." "url=https://github.com/github/codeql-action/releases/download/${Tag}/codeql-bundle-linux64.tar.gz" >> $env:GITHUB_OUTPUT env: From 3507cdc79666d10a3e196aa07bb0ca452ead3464 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 2 Nov 2022 12:17:24 -0400 Subject: [PATCH 0040/1420] Stop using `latest` for second init --- .github/workflows/ql-for-ql-build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ql-for-ql-build.yml b/.github/workflows/ql-for-ql-build.yml index 0af98b21e09..86b1aa85162 100644 --- a/.github/workflows/ql-for-ql-build.yml +++ b/.github/workflows/ql-for-ql-build.yml @@ -142,7 +142,6 @@ jobs: languages: ql db-location: ${{ runner.temp }}/db config-file: ./ql-for-ql-config.yml - tools: latest - name: Move pack cache run: | cp -r ${PACK}/.cache ql/ql/src/.cache From 966be2727eba3c546914f9b59025130b21e65f52 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 2 Nov 2022 12:22:39 -0400 Subject: [PATCH 0041/1420] Use correct bundle version for second init --- .github/workflows/ql-for-ql-build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ql-for-ql-build.yml b/.github/workflows/ql-for-ql-build.yml index 86b1aa85162..36ca512012f 100644 --- a/.github/workflows/ql-for-ql-build.yml +++ b/.github/workflows/ql-for-ql-build.yml @@ -142,6 +142,7 @@ jobs: languages: ql db-location: ${{ runner.temp }}/db config-file: ./ql-for-ql-config.yml + tools: ${{ steps.find-latest-bundle.outputs.url }} - name: Move pack cache run: | cp -r ${PACK}/.cache ql/ql/src/.cache From 5c905c42b268ed3f499033eb9605f7090c3081e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 26 Oct 2022 20:23:58 +0200 Subject: [PATCH 0042/1420] Swift: Initial UnsafeJsEval query --- .../queries/Security/CWE-094/UnsafeJsEval.ql | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.ql diff --git a/swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.ql b/swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.ql new file mode 100644 index 00000000000..ced918a64d4 --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.ql @@ -0,0 +1,119 @@ +/** + * @name JavaScript Injection + * @description Evaluating JavaScript code containing a substring from a remote source may lead to remote code execution. + * @kind path-problem + * @problem.severity warning + * @security-severity 6.1 + * @precision high + * @id swift/unsafe-js-eval + * @tags security + * external/cwe/cwe-094 + * external/cwe/cwe-095 + * external/cwe/cwe-749 + */ + +import swift +import codeql.swift.dataflow.DataFlow +import codeql.swift.dataflow.TaintTracking +import codeql.swift.dataflow.FlowSources +import DataFlow::PathGraph + +/** + * A source of untrusted, user-controlled data. + * TODO: Extend to more (non-remote) sources in the future. + */ +class Source = RemoteFlowSource; + +/** + * A sink that evaluates a string of JavaScript code. + */ +abstract class Sink extends DataFlow::Node { } + +class WKWebView extends Sink { + WKWebView() { + any(CallExpr ce | + ce.getStaticTarget() = + getMethodWithQualifiedName("WKWebView", + [ + "evaluateJavaScript(_:completionHandler:)", + "evaluateJavaScript(_:in:in:completionHandler:)", + "evaluateJavaScript(_:in:contentWorld:)", + "callAsyncJavaScript(_:arguments:in:in:completionHandler:)", + "callAsyncJavaScript(_:arguments:in:contentWorld:)" + ]) + ).getArgument(0).getExpr() = this.asExpr() + } +} + +class WKUserContentController extends Sink { + WKUserContentController() { + any(CallExpr ce | + ce.getStaticTarget() = + getMethodWithQualifiedName("WKUserContentController", "addUserScript(_:)") + ).getArgument(0).getExpr() = this.asExpr() + } +} + +class UIWebView extends Sink { + UIWebView() { + any(CallExpr ce | + ce.getStaticTarget() = + getMethodWithQualifiedName(["UIWebView", "WebView"], "stringByEvaluatingJavaScript(from:)") + ).getArgument(0).getExpr() = this.asExpr() + } +} + +class JSContext extends Sink { + JSContext() { + any(CallExpr ce | + ce.getStaticTarget() = + getMethodWithQualifiedName("JSContext", + ["evaluateScript(_:)", "evaluateScript(_:withSourceURL:)"]) + ).getArgument(0).getExpr() = this.asExpr() + } +} + +class JSEvaluateScript extends Sink { + JSEvaluateScript() { + any(CallExpr ce | + ce.getStaticTarget() = getFunctionWithQualifiedName("JSEvaluateScript(_:_:_:_:_:_:)") + ).getArgument(1).getExpr() = this.asExpr() + } +} + +// TODO: Consider moving the following to the library, e.g. +// - Decl.hasQualifiedName(moduleName?, declaringDeclName?, declName) +// - parentDecl = memberDecl.getDeclaringDecl() <=> parentDecl.getAMember() = memberDecl +IterableDeclContext getDeclaringDeclOf(Decl member) { result.getAMember() = member } + +MethodDecl getMethodWithQualifiedName(string className, string methodName) { + result.getName() = methodName and + getDeclaringDeclOf(result).(NominalTypeDecl).getName() = className +} + +AbstractFunctionDecl getFunctionWithQualifiedName(string funcName) { + result.getName() = funcName and + not result.hasSelfParam() +} + +/** + * A taint configuration from taint sources to sinks for this query. + */ +class UnsafeJsEvalConfig extends TaintTracking::Configuration { + UnsafeJsEvalConfig() { this = "UnsafeJsEvalConfig" } + + override predicate isSource(DataFlow::Node node) { node instanceof Source } + + override predicate isSink(DataFlow::Node node) { node instanceof Sink } + + override predicate isSanitizer(DataFlow::Node node) { + none() // TODO: A conversion to a primitive type or an enum + } +} + +from + UnsafeJsEvalConfig config, DataFlow::PathNode sourceNode, DataFlow::PathNode sinkNode, Sink sink +where + config.hasFlowPath(sourceNode, sinkNode) and + sink = sinkNode.getNode() +select sink, sourceNode, sinkNode, "Evaluation of uncontrolled JavaScript from a remote source." From 7b599f5fefecf3e4798c4ed2211a8d2c7d692c49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Thu, 27 Oct 2022 11:42:22 +0200 Subject: [PATCH 0043/1420] Swift: Add async varant of WKWebView evaluateJavaScript(_:) See concurrency note here: https://developer.apple.com/documentation/webkit/wkwebview/1415017-evaluatejavascript See also https://developer.apple.com/documentation/swift/calling-objective-c-apis-asynchronously --- swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.ql b/swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.ql index ced918a64d4..53891e2c03d 100644 --- a/swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.ql +++ b/swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.ql @@ -35,7 +35,7 @@ class WKWebView extends Sink { ce.getStaticTarget() = getMethodWithQualifiedName("WKWebView", [ - "evaluateJavaScript(_:completionHandler:)", + "evaluateJavaScript(_:)", "evaluateJavaScript(_:completionHandler:)", "evaluateJavaScript(_:in:in:completionHandler:)", "evaluateJavaScript(_:in:contentWorld:)", "callAsyncJavaScript(_:arguments:in:in:completionHandler:)", From 28b7f0884f6d88f3e7f81d7f9a531ed4b22c0b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 2 Nov 2022 12:49:59 +0100 Subject: [PATCH 0044/1420] Swift: UnsafeJsEval test finally compiles --- .../Security/CWE-094/UnsafeJsEval.expected | 0 .../Security/CWE-094/UnsafeJsEval.qlref | 1 + .../Security/CWE-094/UnsafeJsEval.swift | 299 ++++++++++++++++++ 3 files changed, 300 insertions(+) create mode 100644 swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.expected create mode 100644 swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.qlref create mode 100644 swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.swift diff --git a/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.expected b/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.qlref b/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.qlref new file mode 100644 index 00000000000..b8c11cee30d --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.qlref @@ -0,0 +1 @@ +queries/Security/CWE-094/UnsafeJsEval.ql diff --git a/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.swift b/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.swift new file mode 100644 index 00000000000..51914bd97e1 --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.swift @@ -0,0 +1,299 @@ + +// --- stubs --- + +class NSObject {} + +@MainActor class UIResponder : NSObject {} +@MainActor class UIView : UIResponder {} + +@MainActor class NSResponder : NSObject {} +class NSView : NSResponder {} + +class WKFrameInfo : NSObject {} +class WKContentWorld : NSObject { + class var defaultClient: WKContentWorld { WKContentWorld() } +} + +class WKWebView : UIView { + + func evaluateJavaScript( + _ javaScriptString: String + ) async throws -> Any { "" } + + func evaluateJavaScript( + _ javaScriptString: String, + completionHandler: ((Any?, Error?) -> Void)? = nil + ) { + completionHandler?(nil, nil) + } + + @MainActor func evaluateJavaScript( + _ javaScript: String, + in frame: WKFrameInfo? = nil, + in contentWorld: WKContentWorld, + completionHandler: ((Result) -> Void)? = nil + ) { + completionHandler?(.success("")) + } + + @MainActor func evaluateJavaScript( + _ javaScript: String, + in frame: WKFrameInfo? = nil, + contentWorld: WKContentWorld + ) async throws -> Any? { nil } + + @MainActor func callAsyncJavaScript( + _ functionBody: String, + arguments: [String : Any] = [:], + in frame: WKFrameInfo? = nil, + in contentWorld: WKContentWorld, + completionHandler: ((Result) -> Void)? = nil + ) { + completionHandler?(.success("")) + } + + @MainActor func callAsyncJavaScript( + _ functionBody: String, + arguments: [String : Any] = [:], + in frame: WKFrameInfo? = nil, + contentWorld: WKContentWorld + ) async throws -> Any? { nil } +} + +enum WKUserScriptInjectionTime : Int, @unchecked Sendable { + case atDocumentStart, atDocumentEnd +} + +class WKUserScript : NSObject { + init( + source: String, + injectionTime: WKUserScriptInjectionTime, + forMainFrameOnly: Bool + ) {} + + init( + source: String, + injectionTime: WKUserScriptInjectionTime, + forMainFrameOnly: Bool, + in contentWorld: WKContentWorld + ) {} +} + +class WKUserContentController : NSObject { + func addUserScript(_ userScript: WKUserScript) {} +} + +class UIWebView : UIView { + // deprecated + func stringByEvaluatingJavaScript(from script: String) -> String? { nil } +} + +class WebView : NSView { + // deprecated + func stringByEvaluatingJavaScript(from script: String!) -> String! { "" } +} + +class JSValue : NSObject {} + +class JSContext { + func evaluateScript(_ script: String!) -> JSValue! { return JSValue() } + func evaluateScript( + _ script: String!, + withSourceURL sourceURL: URL! + ) -> JSValue! { return JSValue() } +} + +typealias JSContextRef = OpaquePointer +typealias JSStringRef = OpaquePointer +typealias JSObjectRef = OpaquePointer +typealias JSValueRef = OpaquePointer +typealias JSChar = UInt16 + +func JSStringCreateWithCharacters( + _ chars: UnsafePointer!, + _ numChars: Int +) -> JSStringRef! { + return chars.withMemoryRebound(to: CChar.self, capacity: numChars) { + cchars in OpaquePointer(cchars) + } +} +func JSStringCreateWithUTF8CString(_ string: UnsafePointer!) -> JSStringRef! { + return OpaquePointer(string) +} +func JSStringRetain(_ string: JSStringRef!) -> JSStringRef! { return string } +func JSStringRelease(_ string: JSStringRef!) { } + +func JSEvaluateScript( + _ ctx: JSContextRef!, + _ script: JSStringRef!, + _ thisObject: JSObjectRef!, + _ sourceURL: JSStringRef!, + _ startingLineNumber: Int32, + _ exception: UnsafeMutablePointer! +) -> JSValueRef! { return OpaquePointer(bitPattern: 0) } + +@frozen +public struct Data: Collection { + public typealias Index = Int + public typealias Element = UInt8 + public subscript(x: Index) -> Element { 0 } + public var startIndex: Index { 0 } + public var endIndex: Index { 0 } + public func index(after i: Index) -> Index { i + 1 } + init(_ elements: S) {} +} + +struct URL { + init?(string: String) {} + init?(string: String, relativeTo: URL?) {} +} + +extension String { + init(contentsOf: URL) throws { + let data = "" + // ... + self.init(data) + } +} + +// --- tests --- + +func getRemoteData() -> String { + let url = URL(string: "http://example.com/") + do { + return try String(contentsOf: url!) + } catch { + return "" + } +} + +func testUsage(_ sink: @escaping (String) async throws -> ()) { + Task { + let localString = "console.log('localString')" + let localStringFragment = "'localStringFragment'" + let remoteString = getRemoteData() + + try! await sink(localString) // GOOD: the HTML data is local + try! await sink(getRemoteData()) // BAD: HTML contains remote input, may access local secrets + try! await sink(remoteString) // BAD + + try! await sink("console.log(" + localStringFragment + ")") // GOOD: the HTML data is local + try! await sink("console.log(" + remoteString + ")") // BAD + + let localData = Data(localString.utf8) + let remoteData = Data(remoteString.utf8) + + try! await sink(String(decoding: localData, as: UTF8.self)) // GOOD: the data is local + try! await sink(String(decoding: remoteData, as: UTF8.self)) // BAD: the data is remote + + try! await sink("console.log(" + String(Int(localStringFragment) ?? 0) + ")") // GOOD: Primitive conversion + try! await sink("console.log(" + String(Int(remoteString) ?? 0) + ")") // GOOD: Primitive conversion + + try! await sink("console.log(" + (localStringFragment.count != 0 ? "1" : "0") + ")") // GOOD: Primitive conversion + try! await sink("console.log(" + (remoteString.count != 0 ? "1" : "0") + ")") // GOOD: Primitive conversion + } +} + +func testUIWebView() { + let webview = UIWebView() + + testUsage { string in + _ = await webview.stringByEvaluatingJavaScript(from: string) + } +} + +func testWebView() { + let webview = WebView() + + testUsage { string in + _ = await webview.stringByEvaluatingJavaScript(from: string) + } +} + +func testWKWebView() { + let webview = WKWebView() + + testUsage { string in + _ = try await webview.evaluateJavaScript(string) + } + testUsage { string in + await webview.evaluateJavaScript(string) { _, _ in } + } + testUsage { string in + await webview.evaluateJavaScript(string, in: nil, in: WKContentWorld.defaultClient) { _ in } + } + testUsage { string in + _ = try await webview.evaluateJavaScript(string, contentWorld: .defaultClient) + } + testUsage { string in + await webview.callAsyncJavaScript(string, in: nil, in: .defaultClient) { _ in () } + } + testUsage { string in + _ = try await webview.callAsyncJavaScript(string, contentWorld: WKContentWorld.defaultClient) + } +} + +func testWKUserContentController() { + let ctrl = WKUserContentController() + + testUsage { string in + ctrl.addUserScript(WKUserScript(source: string, injectionTime: .atDocumentStart, forMainFrameOnly: false)) + } + testUsage { string in + ctrl.addUserScript(WKUserScript(source: string, injectionTime: .atDocumentEnd, forMainFrameOnly: true, in: .defaultClient)) + } +} + +func testJSContext() { + let ctx = JSContext() + + testUsage { string in + _ = ctx.evaluateScript(string) + } + testUsage { string in + _ = ctx.evaluateScript(string, withSourceURL: URL(string: "https://example.com")) + } +} + +func testJSEvaluateScript() { + testUsage { string in + string.utf16.withContiguousStorageIfAvailable { stringBytes in + let jsstr = JSStringRetain(JSStringCreateWithCharacters(stringBytes.baseAddress, string.count)) + defer { JSStringRelease(jsstr) } + _ = JSEvaluateScript( + /*ctx:*/ OpaquePointer(bitPattern: 0), + /*script:*/ jsstr, + /*thisObject:*/ OpaquePointer(bitPattern: 0), + /*sourceURL:*/ OpaquePointer(bitPattern: 0), + /*startingLineNumber:*/ 0, + /*exception:*/ UnsafeMutablePointer(bitPattern: 0) + ) + } + } + testUsage { string in + string.utf8CString.withUnsafeBufferPointer { stringBytes in + let jsstr = JSStringRetain(JSStringCreateWithUTF8CString(stringBytes.baseAddress)) + defer { JSStringRelease(jsstr) } + _ = JSEvaluateScript( + /*ctx:*/ OpaquePointer(bitPattern: 0), + /*script:*/ jsstr, + /*thisObject:*/ OpaquePointer(bitPattern: 0), + /*sourceURL:*/ OpaquePointer(bitPattern: 0), + /*startingLineNumber:*/ 0, + /*exception:*/ UnsafeMutablePointer(bitPattern: 0) + ) + } + } +} + +func testQHelpExamples() { + +} + +testUIWebView() +testWebView() +testWKWebView() +testWKUserContentController() +testJSContext() +testJSEvaluateScript() +testQHelpExamples() From 3d24e0a2ebb187ad93cf04e0ef74297e717a92db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Thu, 3 Nov 2022 11:12:41 +0100 Subject: [PATCH 0045/1420] Swift: enable VSCode to build extractor via CMake The `-arch=x86_64` from `swift/rules.bzl` turns out to be unnecessary, even on Arm-based Macs. --- .vscode/settings.json | 4 +++- swift/CMakeLists.txt | 4 ++++ swift/rules.bzl | 6 +----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 8b22c91bb77..1050c79b825 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,5 @@ { - "omnisharp.autoStart": false + "omnisharp.autoStart": false, + "cmake.sourceDirectory": "${workspaceFolder}/swift", + "cmake.buildDirectory": "${workspaceFolder}/bazel-cmake-build" } diff --git a/swift/CMakeLists.txt b/swift/CMakeLists.txt index ad431e49a17..fbc55187567 100644 --- a/swift/CMakeLists.txt +++ b/swift/CMakeLists.txt @@ -9,6 +9,10 @@ set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_C_COMPILER clang) set(CMAKE_CXX_COMPILER clang++) +if(APPLE) + set(CMAKE_OSX_ARCHITECTURES x86_64) # temporary until we can build a Universal Binary +endif() + project(codeql) include(../misc/bazel/cmake/setup.cmake) diff --git a/swift/rules.bzl b/swift/rules.bzl index 29a1a704f02..ba9e4b0e8bf 100644 --- a/swift/rules.bzl +++ b/swift/rules.bzl @@ -5,11 +5,7 @@ def _wrap_cc(rule, kwargs): _add_args(kwargs, "copts", [ # Required by LLVM/Swift "-fno-rtti", - ] + select({ - # temporary, before we do universal merging and have an arm prebuilt package, we make arm build x86 - "@platforms//os:macos": ["-arch=x86_64"], - "//conditions:default": [], - })) + ]) _add_args(kwargs, "features", [ # temporary, before we do universal merging "-universal_binaries", From dc6f60a5015bf6b81a8c98c537cf449ea90bc5bf Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Wed, 2 Nov 2022 16:10:29 +0100 Subject: [PATCH 0046/1420] Add new XXE query Only XMLParser sinks for the time being --- .../codeql/swift/dataflow/ExternalFlow.qll | 2 + .../swift/frameworks/StandardLibrary/Data.qll | 6 ++ .../StandardLibrary/InputStream.qll | 8 +++ swift/ql/lib/codeql/swift/security/XXE.qll | 67 +++++++++++++++++++ .../ql/lib/codeql/swift/security/XXEQuery.qll | 27 ++++++++ .../ql/src/queries/Security/CWE-611/XXE.qhelp | 57 ++++++++++++++++ swift/ql/src/queries/Security/CWE-611/XXE.ql | 24 +++++++ .../src/queries/Security/CWE-611/XXEBad.swift | 2 + .../queries/Security/CWE-611/XXEGood.swift | 2 + .../Security/CWE-611/XXETest.expected | 0 .../query-tests/Security/CWE-611/XXETest.ql | 20 ++++++ .../Security/CWE-611/testXXE.swift | 58 ++++++++++++++++ 12 files changed, 273 insertions(+) create mode 100644 swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Data.qll create mode 100644 swift/ql/lib/codeql/swift/frameworks/StandardLibrary/InputStream.qll create mode 100644 swift/ql/lib/codeql/swift/security/XXE.qll create mode 100644 swift/ql/lib/codeql/swift/security/XXEQuery.qll create mode 100644 swift/ql/src/queries/Security/CWE-611/XXE.qhelp create mode 100644 swift/ql/src/queries/Security/CWE-611/XXE.ql create mode 100644 swift/ql/src/queries/Security/CWE-611/XXEBad.swift create mode 100644 swift/ql/src/queries/Security/CWE-611/XXEGood.swift create mode 100644 swift/ql/test/query-tests/Security/CWE-611/XXETest.expected create mode 100644 swift/ql/test/query-tests/Security/CWE-611/XXETest.ql create mode 100644 swift/ql/test/query-tests/Security/CWE-611/testXXE.swift diff --git a/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll b/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll index 28e783c8047..4d563525830 100644 --- a/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll +++ b/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll @@ -79,6 +79,8 @@ private import internal.FlowSummaryImplSpecific */ private module Frameworks { private import codeql.swift.frameworks.StandardLibrary.CustomUrlSchemes + private import codeql.swift.frameworks.StandardLibrary.Data + private import codeql.swift.frameworks.StandardLibrary.InputStream private import codeql.swift.frameworks.StandardLibrary.String private import codeql.swift.frameworks.StandardLibrary.Url private import codeql.swift.frameworks.StandardLibrary.UrlSession diff --git a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Data.qll b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Data.qll new file mode 100644 index 00000000000..740fc8874b6 --- /dev/null +++ b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Data.qll @@ -0,0 +1,6 @@ +import swift +private import codeql.swift.dataflow.ExternalFlow + +private class DataSummaries extends SummaryModelCsv { + override predicate row(string row) { row = ";Data;true;init(_:);;;Argument[0];ReturnValue;taint" } +} diff --git a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/InputStream.qll b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/InputStream.qll new file mode 100644 index 00000000000..60b61bec294 --- /dev/null +++ b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/InputStream.qll @@ -0,0 +1,8 @@ +import swift +private import codeql.swift.dataflow.ExternalFlow + +private class InputStreamSummaries extends SummaryModelCsv { + override predicate row(string row) { + row = ";InputStream;true;init(data:);;;Argument[0];ReturnValue;taint" + } +} diff --git a/swift/ql/lib/codeql/swift/security/XXE.qll b/swift/ql/lib/codeql/swift/security/XXE.qll new file mode 100644 index 00000000000..eecbd147041 --- /dev/null +++ b/swift/ql/lib/codeql/swift/security/XXE.qll @@ -0,0 +1,67 @@ +/** Provides classes and predicates to reason about XML external entities (XXE) vulnerabilities. */ + +import swift +private import codeql.swift.dataflow.DataFlow +private import codeql.swift.dataflow.internal.DataFlowPrivate + +/** A data flow sink for XML external entities (XXE) vulnerabilities. */ +abstract class XxeSink extends DataFlow::Node { } + +/** A sanitizer for XML external entities (XXE) vulnerabilities. */ +abstract class XxeSanitizer extends DataFlow::Node { } + +/** + * A unit class for adding additional taint steps. + * + * Extend this class to add additional taint steps that should apply to paths related to + * XML external entities (XXE) vulnerabilities. + */ +class XxeAdditionalTaintStep extends Unit { + abstract predicate step(DataFlow::Node n1, DataFlow::Node n2); +} + +/** The XML argument of a `XMLParser` vulnerable to XXE. */ +private class DefaultXxeSink extends XxeSink { + DefaultXxeSink() { + this.asExpr() = any(Argument a | a.getApplyExpr() instanceof VulnerableParser).getExpr() + } +} + +/** The construction of a `XMLParser` that enables external entities. */ +private class VulnerableParser extends CallExpr { + VulnerableParser() { + resolvesExternalEntities(this) and this.getFunction() instanceof ConstructorRefCallExpr + } +} + +/** Holds if there is an access of `ref` that sets `shouldResolveExternalEntities` to `true`. */ +private predicate resolvesExternalEntities(XmlParserRef ref) { + exists(XmlParserRef base | + DataFlow::localExprFlow(ref, base) or DataFlow::localExprFlow(base, ref) + | + exists(AssignExpr assign, ShouldResolveExternalEntities s, BooleanLiteralExpr b | + s.getBase() = base and + assign.getDest() = s and + b.getValue() = true and + DataFlow::localExprFlow(b, assign.getSource()) + ) + ) +} + +/** A reference to the field `XMLParser.shouldResolveExternalEntities`. */ +private class ShouldResolveExternalEntities extends MemberRefExpr { + ShouldResolveExternalEntities() { + this.getMember().(FieldDecl).getName() = "shouldResolveExternalEntities" and + this.getBase() instanceof XmlParserRef + } +} + +/** An expression of type `XMLParser`. */ +private class XmlParserRef extends Expr { + XmlParserRef() { this.getType() instanceof XmlParserType } +} + +/** The type `XMLParser`. */ +private class XmlParserType extends NominalType { + XmlParserType() { this.getFullName() = "XMLParser" } +} diff --git a/swift/ql/lib/codeql/swift/security/XXEQuery.qll b/swift/ql/lib/codeql/swift/security/XXEQuery.qll new file mode 100644 index 00000000000..2b5dc5ea3ea --- /dev/null +++ b/swift/ql/lib/codeql/swift/security/XXEQuery.qll @@ -0,0 +1,27 @@ +/** + * Provides a taint-tracking configuration for reasoning about XML external entities + * (XXE) vulnerabilities. + */ + +import swift +import codeql.swift.dataflow.DataFlow +import codeql.swift.dataflow.FlowSources +import codeql.swift.dataflow.TaintTracking +import codeql.swift.security.XXE + +/** + * A taint-tracking configuration for XML external entities (XXE) vulnerabilities. + */ +class XxeConfiguration extends TaintTracking::Configuration { + XxeConfiguration() { this = "XxeConfiguration" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof XxeSink } + + override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof XxeSanitizer } + + override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) { + any(XxeAdditionalTaintStep s).step(n1, n2) + } +} diff --git a/swift/ql/src/queries/Security/CWE-611/XXE.qhelp b/swift/ql/src/queries/Security/CWE-611/XXE.qhelp new file mode 100644 index 00000000000..0aca8ac474b --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-611/XXE.qhelp @@ -0,0 +1,57 @@ + + + + +

+Parsing untrusted XML files with a weakly configured XML parser may lead to an XML External Entity (XXE) attack. This type of attack +uses external entity references to access arbitrary files on a system, carry out denial of service, or server side +request forgery. Even when the result of parsing is not returned to the user, out-of-band +data retrieval techniques may allow attackers to steal sensitive data. Denial of services can also be +carried out in this situation. +

+
+ + +

+The easiest way to prevent XXE attacks is to disable external entity handling when +parsing untrusted data. How this is done depends on the library being used. Note that some +libraries, such as recent versions of XMLParser, disable entity expansion by default, +so unless you have explicitly enabled entity expansion, no further action needs to be taken. +

+
+ + +

+The following example uses the XMLParser class to parse a string data. +If that string is from an untrusted source, this code may be vulnerable to an XXE attack, since +the parser is also setting its shouldResolveExternalEntities option to true: +

+ + +

+To guard against XXE attacks, the shouldResolveExternalEntities option should be +left unset or explicitly set to false. +

+ + +
+ + +
  • +OWASP: +XML External Entity (XXE) Processing. +
  • +
  • +OWASP: +XML External Entity Prevention Cheat Sheet. +
  • +
  • +Timothy Morgen: +XML Schema, DTD, and Entity Attacks. +
  • +
  • +Timur Yunusov, Alexey Osipov: +XML Out-Of-Band Data Retrieval. +
  • +
    +
    diff --git a/swift/ql/src/queries/Security/CWE-611/XXE.ql b/swift/ql/src/queries/Security/CWE-611/XXE.ql new file mode 100644 index 00000000000..288da0aad9d --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-611/XXE.ql @@ -0,0 +1,24 @@ +/** + * @name Resolving XML external entity in user-controlled data + * @description Parsing user-controlled XML documents and allowing expansion of external entity + * references may lead to disclosure of confidential data or denial of service. + * @kind path-problem + * @problem.severity error + * @security-severity 9.1 + * @precision high + * @id swift/xxe + * @tags security + * external/cwe/cwe-611 + * external/cwe/cwe-776 + * external/cwe/cwe-827 + */ + +import swift +import codeql.swift.dataflow.DataFlow +import codeql.swift.security.XXEQuery +import DataFlow::PathGraph + +from DataFlow::PathNode source, DataFlow::PathNode sink +where any(XxeConfiguration c).hasFlowPath(source, sink) +select sink.getNode(), source, sink, "XML parser with enabled external entities depends on $@.", + source.getNode(), "user input" diff --git a/swift/ql/src/queries/Security/CWE-611/XXEBad.swift b/swift/ql/src/queries/Security/CWE-611/XXEBad.swift new file mode 100644 index 00000000000..19ff4a7383d --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-611/XXEBad.swift @@ -0,0 +1,2 @@ +let parser = XMLParser(data: remoteData) // BAD (parser explicitly enables external entities) +parser.shouldResolveExternalEntities = true diff --git a/swift/ql/src/queries/Security/CWE-611/XXEGood.swift b/swift/ql/src/queries/Security/CWE-611/XXEGood.swift new file mode 100644 index 00000000000..5552186c759 --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-611/XXEGood.swift @@ -0,0 +1,2 @@ +let parser = XMLParser(data: remoteData) // GOOD (parser explicitly disables external entities) +parser.shouldResolveExternalEntities = false diff --git a/swift/ql/test/query-tests/Security/CWE-611/XXETest.expected b/swift/ql/test/query-tests/Security/CWE-611/XXETest.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/query-tests/Security/CWE-611/XXETest.ql b/swift/ql/test/query-tests/Security/CWE-611/XXETest.ql new file mode 100644 index 00000000000..817f55678ad --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-611/XXETest.ql @@ -0,0 +1,20 @@ +import swift +import codeql.swift.security.XXEQuery +import TestUtilities.InlineExpectationsTest + +class XxeTest extends InlineExpectationsTest { + XxeTest() { this = "XxeTest" } + + override string getARelevantTag() { result = "hasXXE" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + exists(XxeConfiguration config, DataFlow::Node source, DataFlow::Node sink, Expr sinkExpr | + config.hasFlow(source, sink) and + sinkExpr = sink.asExpr() and + location = sinkExpr.getLocation() and + element = sinkExpr.toString() and + tag = "hasXXE" and + value = source.asExpr().getLocation().getStartLine().toString() + ) + } +} diff --git a/swift/ql/test/query-tests/Security/CWE-611/testXXE.swift b/swift/ql/test/query-tests/Security/CWE-611/testXXE.swift new file mode 100644 index 00000000000..b98385463ab --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-611/testXXE.swift @@ -0,0 +1,58 @@ +// --- stubs --- + +class Data { + init(_ elements: S) {} +} + +struct URL { + init?(string: String) {} +} + +class InputStream { + init(data: Data) {} +} + +extension String { + init(contentsOf: URL) { + let data = "" + self.init(data) + } +} + +class XMLParser { + var shouldResolveExternalEntities: Bool { get { return false } set {} } + init?(contentsOf: URL) {} + init(data: Data) {} + init(stream: InputStream) {} +} + +// --- tests --- + +func testData() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteData = Data(remoteString) + let parser = XMLParser(data: remoteData) // $ hasXXE=32 + parser.shouldResolveExternalEntities = true +} + +func testInputStream() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteData = Data(remoteString) + let remoteStream = InputStream(data: remoteData) + let parser = XMLParser(stream: remoteStream) // $ hasXXE=39 + parser.shouldResolveExternalEntities = true + +} + +func testDataSafe() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteData = Data(remoteString) + let _ = XMLParser(data: remoteData) // NO XXE: parser doesn't enable external entities +} + +func testInputStreamSafe() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteData = Data(remoteString) + let remoteStream = InputStream(data: remoteData) + let _ = XMLParser(stream: remoteStream) // NO XXE: parser doesn't enable external entities +} \ No newline at end of file From f4047e016cb5831769081c6def3a0cd11fad886b Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Wed, 2 Nov 2022 16:21:17 +0100 Subject: [PATCH 0047/1420] Address QL-for-QL alert Use an alert message consistent with the other languages --- swift/ql/src/queries/Security/CWE-611/XXE.ql | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-611/XXE.ql b/swift/ql/src/queries/Security/CWE-611/XXE.ql index 288da0aad9d..04c27949c11 100644 --- a/swift/ql/src/queries/Security/CWE-611/XXE.ql +++ b/swift/ql/src/queries/Security/CWE-611/XXE.ql @@ -20,5 +20,6 @@ import DataFlow::PathGraph from DataFlow::PathNode source, DataFlow::PathNode sink where any(XxeConfiguration c).hasFlowPath(source, sink) -select sink.getNode(), source, sink, "XML parser with enabled external entities depends on $@.", - source.getNode(), "user input" +select sink.getNode(), source, sink, + "XML parsing depends on a $@ without guarding against external entity expansion.", + source.getNode(), "user-provided value" From 0c6957ea785be63235d1cb8f61d86526681ec172 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Wed, 2 Nov 2022 17:05:55 +0100 Subject: [PATCH 0048/1420] Adjust test expectations of a query affected by new summaries --- .../Security/CWE-311/CleartextTransmission.expected | 11 +++++++++++ .../test/query-tests/Security/CWE-311/testSend.swift | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected b/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected index de02d0db461..5580af94d22 100644 --- a/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected +++ b/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected @@ -1,4 +1,8 @@ edges +| testSend.swift:5:5:5:29 | [summary param] 0 in init(_:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(_:) : | +| testSend.swift:33:14:33:32 | call to init(_:) : | testSend.swift:37:19:37:19 | data2 | +| testSend.swift:33:19:33:19 | passwordPlain : | testSend.swift:5:5:5:29 | [summary param] 0 in init(_:) : | +| testSend.swift:33:19:33:19 | passwordPlain : | testSend.swift:33:14:33:32 | call to init(_:) : | | testSend.swift:41:10:41:18 | data : | testSend.swift:41:45:41:45 | data : | | testSend.swift:45:13:45:13 | password : | testSend.swift:52:27:52:27 | str1 | | testSend.swift:46:13:46:13 | password : | testSend.swift:53:27:53:27 | str2 | @@ -8,7 +12,12 @@ edges | testURL.swift:13:54:13:54 | passwd : | testURL.swift:13:22:13:54 | ... .+(_:_:) ... | | testURL.swift:16:55:16:55 | credit_card_no : | testURL.swift:16:22:16:55 | ... .+(_:_:) ... | nodes +| file://:0:0:0:0 | [summary] to write: return (return) in init(_:) : | semmle.label | [summary] to write: return (return) in init(_:) : | +| testSend.swift:5:5:5:29 | [summary param] 0 in init(_:) : | semmle.label | [summary param] 0 in init(_:) : | | testSend.swift:29:19:29:19 | passwordPlain | semmle.label | passwordPlain | +| testSend.swift:33:14:33:32 | call to init(_:) : | semmle.label | call to init(_:) : | +| testSend.swift:33:19:33:19 | passwordPlain : | semmle.label | passwordPlain : | +| testSend.swift:37:19:37:19 | data2 | semmle.label | data2 | | testSend.swift:41:10:41:18 | data : | semmle.label | data : | | testSend.swift:41:45:41:45 | data : | semmle.label | data : | | testSend.swift:45:13:45:13 | password : | semmle.label | password : | @@ -24,9 +33,11 @@ nodes | testURL.swift:16:55:16:55 | credit_card_no : | semmle.label | credit_card_no : | | testURL.swift:20:22:20:22 | passwd | semmle.label | passwd | subpaths +| testSend.swift:33:19:33:19 | passwordPlain : | testSend.swift:5:5:5:29 | [summary param] 0 in init(_:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(_:) : | testSend.swift:33:14:33:32 | call to init(_:) : | | testSend.swift:47:17:47:17 | password : | testSend.swift:41:10:41:18 | data : | testSend.swift:41:45:41:45 | data : | testSend.swift:47:13:47:25 | call to pad(_:) : | #select | testSend.swift:29:19:29:19 | passwordPlain | testSend.swift:29:19:29:19 | passwordPlain | testSend.swift:29:19:29:19 | passwordPlain | This operation transmits 'passwordPlain', which may contain unencrypted sensitive data from $@. | testSend.swift:29:19:29:19 | passwordPlain | passwordPlain | +| testSend.swift:37:19:37:19 | data2 | testSend.swift:33:19:33:19 | passwordPlain : | testSend.swift:37:19:37:19 | data2 | This operation transmits 'data2', which may contain unencrypted sensitive data from $@. | testSend.swift:33:19:33:19 | passwordPlain : | passwordPlain | | testSend.swift:52:27:52:27 | str1 | testSend.swift:45:13:45:13 | password : | testSend.swift:52:27:52:27 | str1 | This operation transmits 'str1', which may contain unencrypted sensitive data from $@. | testSend.swift:45:13:45:13 | password : | password | | testSend.swift:53:27:53:27 | str2 | testSend.swift:46:13:46:13 | password : | testSend.swift:53:27:53:27 | str2 | This operation transmits 'str2', which may contain unencrypted sensitive data from $@. | testSend.swift:46:13:46:13 | password : | password | | testSend.swift:54:27:54:27 | str3 | testSend.swift:47:17:47:17 | password : | testSend.swift:54:27:54:27 | str3 | This operation transmits 'str3', which may contain unencrypted sensitive data from $@. | testSend.swift:47:17:47:17 | password : | password | diff --git a/swift/ql/test/query-tests/Security/CWE-311/testSend.swift b/swift/ql/test/query-tests/Security/CWE-311/testSend.swift index 506b3a921e3..aaf2e3487f2 100644 --- a/swift/ql/test/query-tests/Security/CWE-311/testSend.swift +++ b/swift/ql/test/query-tests/Security/CWE-311/testSend.swift @@ -34,7 +34,7 @@ func test1(passwordPlain : String, passwordHash : String) { let data3 = Data(passwordHash) nw.send(content: data1, completion: .idempotent) // GOOD (not sensitive) - nw.send(content: data2, completion: .idempotent) // BAD [NOT DETECTED] + nw.send(content: data2, completion: .idempotent) // BAD nw.send(content: data3, completion: .idempotent) // GOOD (not sensitive) } From fe138dc0a176bc8535ecb00bd00d5ad4139dd4d2 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Thu, 3 Nov 2022 09:29:57 +0100 Subject: [PATCH 0049/1420] Add explicitly safe test cases --- .../query-tests/Security/CWE-611/testXXE.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/swift/ql/test/query-tests/Security/CWE-611/testXXE.swift b/swift/ql/test/query-tests/Security/CWE-611/testXXE.swift index b98385463ab..63244e082e9 100644 --- a/swift/ql/test/query-tests/Security/CWE-611/testXXE.swift +++ b/swift/ql/test/query-tests/Security/CWE-611/testXXE.swift @@ -50,9 +50,25 @@ func testDataSafe() { let _ = XMLParser(data: remoteData) // NO XXE: parser doesn't enable external entities } +func testDataSafeExplicit() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteData = Data(remoteString) + let parser = XMLParser(data: remoteData) // NO XXE: parser disables external entities + parser.shouldResolveExternalEntities = false + +} + func testInputStreamSafe() { let remoteString = String(contentsOf: URL(string: "http://example.com/")!) let remoteData = Data(remoteString) let remoteStream = InputStream(data: remoteData) let _ = XMLParser(stream: remoteStream) // NO XXE: parser doesn't enable external entities +} + +func testInputStreamSafeExplicit() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteData = Data(remoteString) + let remoteStream = InputStream(data: remoteData) + let parser = XMLParser(stream: remoteStream) // NO XXE: parser disables external entities + parser.shouldResolveExternalEntities = false } \ No newline at end of file From 3e1819f25de28bf4bf44d8ccddd3f44a60206b00 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Thu, 3 Nov 2022 09:49:13 +0100 Subject: [PATCH 0050/1420] Model XMLParser constructor init(contentsOf:) --- swift/ql/lib/codeql/swift/security/XXE.qll | 5 ++++- .../Security/CWE-611/testXXE.swift | 20 ++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/swift/ql/lib/codeql/swift/security/XXE.qll b/swift/ql/lib/codeql/swift/security/XXE.qll index eecbd147041..c3139ed974b 100644 --- a/swift/ql/lib/codeql/swift/security/XXE.qll +++ b/swift/ql/lib/codeql/swift/security/XXE.qll @@ -58,7 +58,10 @@ private class ShouldResolveExternalEntities extends MemberRefExpr { /** An expression of type `XMLParser`. */ private class XmlParserRef extends Expr { - XmlParserRef() { this.getType() instanceof XmlParserType } + XmlParserRef() { + this.getType() instanceof XmlParserType or + this.getType() = any(OptionalType t | t.getBaseType() instanceof XmlParserType) + } } /** The type `XMLParser`. */ diff --git a/swift/ql/test/query-tests/Security/CWE-611/testXXE.swift b/swift/ql/test/query-tests/Security/CWE-611/testXXE.swift index 63244e082e9..75538f014f9 100644 --- a/swift/ql/test/query-tests/Security/CWE-611/testXXE.swift +++ b/swift/ql/test/query-tests/Security/CWE-611/testXXE.swift @@ -41,7 +41,13 @@ func testInputStream() { let remoteStream = InputStream(data: remoteData) let parser = XMLParser(stream: remoteStream) // $ hasXXE=39 parser.shouldResolveExternalEntities = true +} +func testUrl() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteUrl = URL(string: remoteString)! + let parser = XMLParser(contentsOf: remoteUrl) // $ hasXXE=47 + parser?.shouldResolveExternalEntities = true } func testDataSafe() { @@ -55,7 +61,6 @@ func testDataSafeExplicit() { let remoteData = Data(remoteString) let parser = XMLParser(data: remoteData) // NO XXE: parser disables external entities parser.shouldResolveExternalEntities = false - } func testInputStreamSafe() { @@ -71,4 +76,17 @@ func testInputStreamSafeExplicit() { let remoteStream = InputStream(data: remoteData) let parser = XMLParser(stream: remoteStream) // NO XXE: parser disables external entities parser.shouldResolveExternalEntities = false +} + +func testUrlSafe() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteUrl = URL(string: remoteString)! + let _ = XMLParser(contentsOf: remoteUrl) // NO XXE: parser doesn't enable external entities +} + +func testUrlSafeExplicit() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteUrl = URL(string: remoteString)! + let parser = XMLParser(contentsOf: remoteUrl) // NO XXE: parser disables external entities + parser?.shouldResolveExternalEntities = false } \ No newline at end of file From da67b1059cca6fa83849252ca8fc2ab56587f8fc Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Thu, 3 Nov 2022 12:38:45 +0100 Subject: [PATCH 0051/1420] Remove (now unnecessary) import --- swift/ql/lib/codeql/swift/security/XXE.qll | 1 - 1 file changed, 1 deletion(-) diff --git a/swift/ql/lib/codeql/swift/security/XXE.qll b/swift/ql/lib/codeql/swift/security/XXE.qll index c3139ed974b..506225f54a1 100644 --- a/swift/ql/lib/codeql/swift/security/XXE.qll +++ b/swift/ql/lib/codeql/swift/security/XXE.qll @@ -2,7 +2,6 @@ import swift private import codeql.swift.dataflow.DataFlow -private import codeql.swift.dataflow.internal.DataFlowPrivate /** A data flow sink for XML external entities (XXE) vulnerabilities. */ abstract class XxeSink extends DataFlow::Node { } From fdd7d76ffd0760315d313cd9570f213abce4e581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Thu, 3 Nov 2022 16:14:43 +0100 Subject: [PATCH 0052/1420] Swift: use FreeFunctionDecl/.has(Qualified)Name Instead of hand-rolled predicates. --- .../queries/Security/CWE-094/UnsafeJsEval.ql | 52 +++++++------------ 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.ql b/swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.ql index 53891e2c03d..fff05671313 100644 --- a/swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.ql +++ b/swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.ql @@ -32,15 +32,16 @@ abstract class Sink extends DataFlow::Node { } class WKWebView extends Sink { WKWebView() { any(CallExpr ce | - ce.getStaticTarget() = - getMethodWithQualifiedName("WKWebView", - [ - "evaluateJavaScript(_:)", "evaluateJavaScript(_:completionHandler:)", - "evaluateJavaScript(_:in:in:completionHandler:)", - "evaluateJavaScript(_:in:contentWorld:)", - "callAsyncJavaScript(_:arguments:in:in:completionHandler:)", - "callAsyncJavaScript(_:arguments:in:contentWorld:)" - ]) + ce.getStaticTarget() + .(MethodDecl) + .hasQualifiedName("WKWebView", + [ + "evaluateJavaScript(_:)", "evaluateJavaScript(_:completionHandler:)", + "evaluateJavaScript(_:in:in:completionHandler:)", + "evaluateJavaScript(_:in:contentWorld:)", + "callAsyncJavaScript(_:arguments:in:in:completionHandler:)", + "callAsyncJavaScript(_:arguments:in:contentWorld:)" + ]) ).getArgument(0).getExpr() = this.asExpr() } } @@ -48,8 +49,9 @@ class WKWebView extends Sink { class WKUserContentController extends Sink { WKUserContentController() { any(CallExpr ce | - ce.getStaticTarget() = - getMethodWithQualifiedName("WKUserContentController", "addUserScript(_:)") + ce.getStaticTarget() + .(MethodDecl) + .hasQualifiedName("WKUserContentController", "addUserScript(_:)") ).getArgument(0).getExpr() = this.asExpr() } } @@ -57,8 +59,9 @@ class WKUserContentController extends Sink { class UIWebView extends Sink { UIWebView() { any(CallExpr ce | - ce.getStaticTarget() = - getMethodWithQualifiedName(["UIWebView", "WebView"], "stringByEvaluatingJavaScript(from:)") + ce.getStaticTarget() + .(MethodDecl) + .hasQualifiedName(["UIWebView", "WebView"], "stringByEvaluatingJavaScript(from:)") ).getArgument(0).getExpr() = this.asExpr() } } @@ -66,9 +69,9 @@ class UIWebView extends Sink { class JSContext extends Sink { JSContext() { any(CallExpr ce | - ce.getStaticTarget() = - getMethodWithQualifiedName("JSContext", - ["evaluateScript(_:)", "evaluateScript(_:withSourceURL:)"]) + ce.getStaticTarget() + .(MethodDecl) + .hasQualifiedName("JSContext", ["evaluateScript(_:)", "evaluateScript(_:withSourceURL:)"]) ).getArgument(0).getExpr() = this.asExpr() } } @@ -76,26 +79,11 @@ class JSContext extends Sink { class JSEvaluateScript extends Sink { JSEvaluateScript() { any(CallExpr ce | - ce.getStaticTarget() = getFunctionWithQualifiedName("JSEvaluateScript(_:_:_:_:_:_:)") + ce.getStaticTarget().(FreeFunctionDecl).hasName("JSEvaluateScript(_:_:_:_:_:_:)") ).getArgument(1).getExpr() = this.asExpr() } } -// TODO: Consider moving the following to the library, e.g. -// - Decl.hasQualifiedName(moduleName?, declaringDeclName?, declName) -// - parentDecl = memberDecl.getDeclaringDecl() <=> parentDecl.getAMember() = memberDecl -IterableDeclContext getDeclaringDeclOf(Decl member) { result.getAMember() = member } - -MethodDecl getMethodWithQualifiedName(string className, string methodName) { - result.getName() = methodName and - getDeclaringDeclOf(result).(NominalTypeDecl).getName() = className -} - -AbstractFunctionDecl getFunctionWithQualifiedName(string funcName) { - result.getName() = funcName and - not result.hasSelfParam() -} - /** * A taint configuration from taint sources to sinks for this query. */ From a7ecdef2a6ea66d2acf42d62b6180c0c6fafbd32 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 3 Nov 2022 11:23:03 +0000 Subject: [PATCH 0053/1420] Swift: Add dataflow tests for tuples. --- .../dataflow/dataflow/LocalFlow.expected | 30 +++++++++++++ .../dataflow/dataflow/test.swift | 43 ++++++++++++++++++- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected index b0e95ee6946..2e945f96294 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected @@ -192,3 +192,33 @@ | test.swift:266:15:266:15 | x | test.swift:267:15:267:15 | x | | test.swift:266:15:266:25 | call to signum() | test.swift:266:15:266:25 | OptionalEvaluationExpr | | test.swift:267:15:267:15 | x | test.swift:268:16:268:16 | x | +| test.swift:277:9:277:9 | WriteDef | test.swift:279:15:279:15 | t1 | +| test.swift:277:14:277:26 | (...) | test.swift:277:9:277:9 | WriteDef | +| test.swift:279:15:279:15 | t1 | test.swift:280:15:280:15 | t1 | +| test.swift:280:15:280:15 | t1 | test.swift:281:15:281:15 | t1 | +| test.swift:281:15:281:15 | t1 | test.swift:283:5:283:5 | t1 | +| test.swift:283:5:283:5 | t1 | test.swift:285:15:285:15 | t1 | +| test.swift:285:15:285:15 | t1 | test.swift:286:15:286:15 | t1 | +| test.swift:286:15:286:15 | t1 | test.swift:287:15:287:15 | t1 | +| test.swift:287:15:287:15 | t1 | test.swift:289:5:289:5 | t1 | +| test.swift:289:5:289:5 | t1 | test.swift:291:15:291:15 | t1 | +| test.swift:291:15:291:15 | t1 | test.swift:292:15:292:15 | t1 | +| test.swift:292:15:292:15 | t1 | test.swift:293:15:293:15 | t1 | +| test.swift:297:9:297:9 | WriteDef | test.swift:298:14:298:14 | t1 | +| test.swift:297:14:297:45 | (...) | test.swift:297:9:297:9 | WriteDef | +| test.swift:298:9:298:9 | WriteDef | test.swift:305:15:305:15 | t2 | +| test.swift:298:14:298:14 | t1 | test.swift:298:9:298:9 | WriteDef | +| test.swift:298:14:298:14 | t1 | test.swift:299:21:299:21 | t1 | +| test.swift:299:9:299:17 | WriteDef | test.swift:309:15:309:15 | a | +| test.swift:299:9:299:17 | WriteDef | test.swift:310:15:310:15 | b | +| test.swift:299:9:299:17 | WriteDef | test.swift:311:15:311:15 | c | +| test.swift:299:21:299:21 | t1 | test.swift:299:9:299:17 | WriteDef | +| test.swift:299:21:299:21 | t1 | test.swift:299:9:299:17 | WriteDef | +| test.swift:299:21:299:21 | t1 | test.swift:299:9:299:17 | WriteDef | +| test.swift:299:21:299:21 | t1 | test.swift:301:15:301:15 | t1 | +| test.swift:301:15:301:15 | t1 | test.swift:302:15:302:15 | t1 | +| test.swift:302:15:302:15 | t1 | test.swift:303:15:303:15 | t1 | +| test.swift:303:15:303:15 | t1 | test.swift:304:15:304:15 | t1 | +| test.swift:305:15:305:15 | t2 | test.swift:306:15:306:15 | t2 | +| test.swift:306:15:306:15 | t2 | test.swift:307:15:307:15 | t2 | +| test.swift:307:15:307:15 | t2 | test.swift:308:15:308:15 | t2 | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/test.swift b/swift/ql/test/library-tests/dataflow/dataflow/test.swift index 95a95145140..e1f4f41c5c2 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/test.swift +++ b/swift/ql/test/library-tests/dataflow/dataflow/test.swift @@ -124,7 +124,7 @@ func forwarder() { return i }) sink(arg: z) // $ flow=122 - + var clean: Int = forward(arg: source(), lambda: { (i: Int) -> Int in return 0 @@ -269,3 +269,44 @@ func test_optionals() { sink(arg: y) // $ MISSING: flow=259 } } + +func sink(arg: (Int, Int)) {} +func sink(arg: (Int, Int, Int)) {} + +func testTuples() { + var t1 = (1, source()) + + sink(arg: t1) + sink(arg: t1.0) + sink(arg: t1.1) // $ MISSING: flow=277 + + t1.1 = 2 + + sink(arg: t1) + sink(arg: t1.0) + sink(arg: t1.1) + + t1.0 = source() + + sink(arg: t1) + sink(arg: t1.0) // $ MISSING: flow=289 + sink(arg: t1.1) +} + +func testTuples2() { + let t1 = (x: source(), y: source(), z: 0) + let t2 = t1 + let (a, b, c) = t1 + + sink(arg: t1) + sink(arg: t1.x) // $ MISSING: flow=297 + sink(arg: t1.y) // $ MISSING: flow=297 + sink(arg: t1.z) + sink(arg: t2) + sink(arg: t2.x) // $ MISSING: flow=297 + sink(arg: t2.y) // $ MISSING: flow=297 + sink(arg: t2.z) + sink(arg: a) // $ MISSING: flow=297 + sink(arg: b) // $ MISSING: flow=297 + sink(arg: c) +} From 472ece45e7a7d70d8eac84107413b76be5b4a0f8 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 3 Nov 2022 15:11:03 +0000 Subject: [PATCH 0054/1420] Swift: Basic content flow through tuples. --- .../dataflow/internal/DataFlowPrivate.qll | 16 ++++++- .../dataflow/internal/DataFlowPublic.qll | 12 +++++ .../dataflow/dataflow/DataFlow.expected | 44 +++++++++++++++++++ .../dataflow/dataflow/test.swift | 14 +++--- 4 files changed, 78 insertions(+), 8 deletions(-) diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index 892eea699aa..554f2eb7454 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -177,7 +177,9 @@ private module Cached { newtype TContentSet = TSingletonContent(Content c) cached - newtype TContent = TFieldContent(FieldDecl f) + newtype TContent = + TFieldContent(FieldDecl f) or + TTupleContent(int index) { exists(any(TupleExpr tn).getElement(index)) } } /** @@ -505,6 +507,12 @@ predicate storeStep(Node node1, ContentSet c, Node node2) { c.isSingleton(any(Content::FieldContent ct | ct.getField() = ref.getMember())) ) or + exists(TupleExpr tuple, int pos | + node1.asExpr() = tuple.getElement(pos) and + node2.asExpr() = tuple and + c.isSingleton(any(Content::TupleContent ct | ct.getIndex() = pos)) + ) + or FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2) } @@ -517,6 +525,12 @@ predicate readStep(Node node1, ContentSet c, Node node2) { node2.asExpr() = ref and c.isSingleton(any(Content::FieldContent ct | ct.getField() = ref.getMember())) ) + or + exists(TupleElementExpr tuple | + node1.asExpr() = tuple.getSubExpr() and + node2.asExpr() = tuple and + c.isSingleton(any(Content::TupleContent ct | ct.getIndex() = tuple.getIndex())) + ) } /** diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll index be569f18314..9257c8ccb05 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll @@ -159,6 +159,18 @@ module Content { override string toString() { result = f.toString() } } + + /** An element of a tuple at a specific index. */ + class TupleContent extends Content, TTupleContent { + private int index; + + TupleContent() { this = TTupleContent(index) } + + /** Gets the index for this tuple element. */ + int getIndex() { result = index } + + override string toString() { result = "Tuple element at index " + index.toString() } + } } /** diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected index 3cf1784e010..8862798d4e4 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected @@ -107,6 +107,23 @@ edges | test.swift:266:15:266:16 | ...? : | file://:0:0:0:0 | [summary param] this in signum() : | | test.swift:266:15:266:16 | ...? : | test.swift:266:15:266:25 | call to signum() : | | test.swift:266:15:266:25 | call to signum() : | test.swift:266:15:266:25 | OptionalEvaluationExpr | +| test.swift:277:14:277:26 | (...) [Tuple element at index 1] : | test.swift:281:15:281:15 | t1 [Tuple element at index 1] : | +| test.swift:277:14:277:26 | (...) [Tuple element at index 1] : | test.swift:287:15:287:15 | t1 [Tuple element at index 1] : | +| test.swift:277:14:277:26 | (...) [Tuple element at index 1] : | test.swift:293:15:293:15 | t1 [Tuple element at index 1] : | +| test.swift:277:18:277:25 | call to source() : | test.swift:277:14:277:26 | (...) [Tuple element at index 1] : | +| test.swift:281:15:281:15 | t1 [Tuple element at index 1] : | test.swift:281:15:281:18 | .1 | +| test.swift:287:15:287:15 | t1 [Tuple element at index 1] : | test.swift:287:15:287:18 | .1 | +| test.swift:293:15:293:15 | t1 [Tuple element at index 1] : | test.swift:293:15:293:18 | .1 | +| test.swift:297:14:297:45 | (...) [Tuple element at index 0] : | test.swift:302:15:302:15 | t1 [Tuple element at index 0] : | +| test.swift:297:14:297:45 | (...) [Tuple element at index 0] : | test.swift:306:15:306:15 | t2 [Tuple element at index 0] : | +| test.swift:297:14:297:45 | (...) [Tuple element at index 1] : | test.swift:303:15:303:15 | t1 [Tuple element at index 1] : | +| test.swift:297:14:297:45 | (...) [Tuple element at index 1] : | test.swift:307:15:307:15 | t2 [Tuple element at index 1] : | +| test.swift:297:18:297:25 | call to source() : | test.swift:297:14:297:45 | (...) [Tuple element at index 0] : | +| test.swift:297:31:297:38 | call to source() : | test.swift:297:14:297:45 | (...) [Tuple element at index 1] : | +| test.swift:302:15:302:15 | t1 [Tuple element at index 0] : | test.swift:302:15:302:18 | .0 | +| test.swift:303:15:303:15 | t1 [Tuple element at index 1] : | test.swift:303:15:303:18 | .1 | +| test.swift:306:15:306:15 | t2 [Tuple element at index 0] : | test.swift:306:15:306:18 | .0 | +| test.swift:307:15:307:15 | t2 [Tuple element at index 1] : | test.swift:307:15:307:18 | .1 | nodes | file://:0:0:0:0 | .a [x] : | semmle.label | .a [x] : | | file://:0:0:0:0 | .x : | semmle.label | .x : | @@ -227,6 +244,26 @@ nodes | test.swift:266:15:266:16 | ...? : | semmle.label | ...? : | | test.swift:266:15:266:25 | OptionalEvaluationExpr | semmle.label | OptionalEvaluationExpr | | test.swift:266:15:266:25 | call to signum() : | semmle.label | call to signum() : | +| test.swift:277:14:277:26 | (...) [Tuple element at index 1] : | semmle.label | (...) [Tuple element at index 1] : | +| test.swift:277:18:277:25 | call to source() : | semmle.label | call to source() : | +| test.swift:281:15:281:15 | t1 [Tuple element at index 1] : | semmle.label | t1 [Tuple element at index 1] : | +| test.swift:281:15:281:18 | .1 | semmle.label | .1 | +| test.swift:287:15:287:15 | t1 [Tuple element at index 1] : | semmle.label | t1 [Tuple element at index 1] : | +| test.swift:287:15:287:18 | .1 | semmle.label | .1 | +| test.swift:293:15:293:15 | t1 [Tuple element at index 1] : | semmle.label | t1 [Tuple element at index 1] : | +| test.swift:293:15:293:18 | .1 | semmle.label | .1 | +| test.swift:297:14:297:45 | (...) [Tuple element at index 0] : | semmle.label | (...) [Tuple element at index 0] : | +| test.swift:297:14:297:45 | (...) [Tuple element at index 1] : | semmle.label | (...) [Tuple element at index 1] : | +| test.swift:297:18:297:25 | call to source() : | semmle.label | call to source() : | +| test.swift:297:31:297:38 | call to source() : | semmle.label | call to source() : | +| test.swift:302:15:302:15 | t1 [Tuple element at index 0] : | semmle.label | t1 [Tuple element at index 0] : | +| test.swift:302:15:302:18 | .0 | semmle.label | .0 | +| test.swift:303:15:303:15 | t1 [Tuple element at index 1] : | semmle.label | t1 [Tuple element at index 1] : | +| test.swift:303:15:303:18 | .1 | semmle.label | .1 | +| test.swift:306:15:306:15 | t2 [Tuple element at index 0] : | semmle.label | t2 [Tuple element at index 0] : | +| test.swift:306:15:306:18 | .0 | semmle.label | .0 | +| test.swift:307:15:307:15 | t2 [Tuple element at index 1] : | semmle.label | t2 [Tuple element at index 1] : | +| test.swift:307:15:307:18 | .1 | semmle.label | .1 | subpaths | test.swift:75:21:75:22 | &... : | test.swift:65:16:65:28 | arg1 : | test.swift:65:1:70:1 | arg2[return] : | test.swift:75:31:75:32 | [post] &... : | | test.swift:114:19:114:19 | arg : | test.swift:109:9:109:14 | arg : | test.swift:110:12:110:12 | arg : | test.swift:114:12:114:22 | call to ... : | @@ -287,3 +324,10 @@ subpaths | test.swift:264:15:264:16 | ...! | test.swift:259:12:259:19 | call to source() : | test.swift:264:15:264:16 | ...! | result | | test.swift:265:15:265:31 | call to signum() | test.swift:265:15:265:22 | call to source() : | test.swift:265:15:265:31 | call to signum() | result | | test.swift:266:15:266:25 | OptionalEvaluationExpr | test.swift:259:12:259:19 | call to source() : | test.swift:266:15:266:25 | OptionalEvaluationExpr | result | +| test.swift:281:15:281:18 | .1 | test.swift:277:18:277:25 | call to source() : | test.swift:281:15:281:18 | .1 | result | +| test.swift:287:15:287:18 | .1 | test.swift:277:18:277:25 | call to source() : | test.swift:287:15:287:18 | .1 | result | +| test.swift:293:15:293:18 | .1 | test.swift:277:18:277:25 | call to source() : | test.swift:293:15:293:18 | .1 | result | +| test.swift:302:15:302:18 | .0 | test.swift:297:18:297:25 | call to source() : | test.swift:302:15:302:18 | .0 | result | +| test.swift:303:15:303:18 | .1 | test.swift:297:31:297:38 | call to source() : | test.swift:303:15:303:18 | .1 | result | +| test.swift:306:15:306:18 | .0 | test.swift:297:18:297:25 | call to source() : | test.swift:306:15:306:18 | .0 | result | +| test.swift:307:15:307:18 | .1 | test.swift:297:31:297:38 | call to source() : | test.swift:307:15:307:18 | .1 | result | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/test.swift b/swift/ql/test/library-tests/dataflow/dataflow/test.swift index e1f4f41c5c2..15635efab78 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/test.swift +++ b/swift/ql/test/library-tests/dataflow/dataflow/test.swift @@ -278,19 +278,19 @@ func testTuples() { sink(arg: t1) sink(arg: t1.0) - sink(arg: t1.1) // $ MISSING: flow=277 + sink(arg: t1.1) // $ flow=277 t1.1 = 2 sink(arg: t1) sink(arg: t1.0) - sink(arg: t1.1) + sink(arg: t1.1) // $ SPURIOUS: flow=277 t1.0 = source() sink(arg: t1) sink(arg: t1.0) // $ MISSING: flow=289 - sink(arg: t1.1) + sink(arg: t1.1) // $ SPURIOUS: flow=277 } func testTuples2() { @@ -299,12 +299,12 @@ func testTuples2() { let (a, b, c) = t1 sink(arg: t1) - sink(arg: t1.x) // $ MISSING: flow=297 - sink(arg: t1.y) // $ MISSING: flow=297 + sink(arg: t1.x) // $ flow=297 + sink(arg: t1.y) // $ flow=297 sink(arg: t1.z) sink(arg: t2) - sink(arg: t2.x) // $ MISSING: flow=297 - sink(arg: t2.y) // $ MISSING: flow=297 + sink(arg: t2.x) // $ flow=297 + sink(arg: t2.y) // $ flow=297 sink(arg: t2.z) sink(arg: a) // $ MISSING: flow=297 sink(arg: b) // $ MISSING: flow=297 From 6dc51edb4c7903136a257202540f0d386d3afbe9 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 3 Nov 2022 15:23:30 +0000 Subject: [PATCH 0055/1420] Swift: Assigning to tuple values. --- .../codeql/swift/dataflow/internal/DataFlowPrivate.qll | 7 +++++++ .../library-tests/dataflow/dataflow/DataFlow.expected | 8 ++++++++ swift/ql/test/library-tests/dataflow/dataflow/test.swift | 2 +- 3 files changed, 16 insertions(+), 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 554f2eb7454..35782564306 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -513,6 +513,13 @@ predicate storeStep(Node node1, ContentSet c, Node node2) { c.isSingleton(any(Content::TupleContent ct | ct.getIndex() = pos)) ) or + exists(TupleElementExpr tuple, AssignExpr assign | + tuple = assign.getDest() and + node1.asExpr() = assign.getSource() and + node2/*.(PostUpdateNode).getPreUpdateNode()*/.asExpr() = tuple.getSubExpr() and + c.isSingleton(any(Content::TupleContent ct | ct.getIndex() = tuple.getIndex())) + ) + or FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2) } diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected index 8862798d4e4..2b5638078e1 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected @@ -113,6 +113,9 @@ edges | test.swift:277:18:277:25 | call to source() : | test.swift:277:14:277:26 | (...) [Tuple element at index 1] : | | test.swift:281:15:281:15 | t1 [Tuple element at index 1] : | test.swift:281:15:281:18 | .1 | | test.swift:287:15:287:15 | t1 [Tuple element at index 1] : | test.swift:287:15:287:18 | .1 | +| test.swift:289:5:289:5 | t1 [Tuple element at index 0] : | test.swift:292:15:292:15 | t1 [Tuple element at index 0] : | +| test.swift:289:12:289:19 | call to source() : | test.swift:289:5:289:5 | t1 [Tuple element at index 0] : | +| test.swift:292:15:292:15 | t1 [Tuple element at index 0] : | test.swift:292:15:292:18 | .0 | | test.swift:293:15:293:15 | t1 [Tuple element at index 1] : | test.swift:293:15:293:18 | .1 | | test.swift:297:14:297:45 | (...) [Tuple element at index 0] : | test.swift:302:15:302:15 | t1 [Tuple element at index 0] : | | test.swift:297:14:297:45 | (...) [Tuple element at index 0] : | test.swift:306:15:306:15 | t2 [Tuple element at index 0] : | @@ -250,6 +253,10 @@ nodes | test.swift:281:15:281:18 | .1 | semmle.label | .1 | | test.swift:287:15:287:15 | t1 [Tuple element at index 1] : | semmle.label | t1 [Tuple element at index 1] : | | test.swift:287:15:287:18 | .1 | semmle.label | .1 | +| test.swift:289:5:289:5 | t1 [Tuple element at index 0] : | semmle.label | t1 [Tuple element at index 0] : | +| test.swift:289:12:289:19 | call to source() : | semmle.label | call to source() : | +| test.swift:292:15:292:15 | t1 [Tuple element at index 0] : | semmle.label | t1 [Tuple element at index 0] : | +| test.swift:292:15:292:18 | .0 | semmle.label | .0 | | test.swift:293:15:293:15 | t1 [Tuple element at index 1] : | semmle.label | t1 [Tuple element at index 1] : | | test.swift:293:15:293:18 | .1 | semmle.label | .1 | | test.swift:297:14:297:45 | (...) [Tuple element at index 0] : | semmle.label | (...) [Tuple element at index 0] : | @@ -326,6 +333,7 @@ subpaths | test.swift:266:15:266:25 | OptionalEvaluationExpr | test.swift:259:12:259:19 | call to source() : | test.swift:266:15:266:25 | OptionalEvaluationExpr | result | | test.swift:281:15:281:18 | .1 | test.swift:277:18:277:25 | call to source() : | test.swift:281:15:281:18 | .1 | result | | test.swift:287:15:287:18 | .1 | test.swift:277:18:277:25 | call to source() : | test.swift:287:15:287:18 | .1 | result | +| test.swift:292:15:292:18 | .0 | test.swift:289:12:289:19 | call to source() : | test.swift:292:15:292:18 | .0 | result | | test.swift:293:15:293:18 | .1 | test.swift:277:18:277:25 | call to source() : | test.swift:293:15:293:18 | .1 | result | | test.swift:302:15:302:18 | .0 | test.swift:297:18:297:25 | call to source() : | test.swift:302:15:302:18 | .0 | result | | test.swift:303:15:303:18 | .1 | test.swift:297:31:297:38 | call to source() : | test.swift:303:15:303:18 | .1 | result | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/test.swift b/swift/ql/test/library-tests/dataflow/dataflow/test.swift index 15635efab78..522a58aac02 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/test.swift +++ b/swift/ql/test/library-tests/dataflow/dataflow/test.swift @@ -289,7 +289,7 @@ func testTuples() { t1.0 = source() sink(arg: t1) - sink(arg: t1.0) // $ MISSING: flow=289 + sink(arg: t1.0) // $ flow=289 sink(arg: t1.1) // $ SPURIOUS: flow=277 } From 24f0eeb6dfe5acaa6465ee24e906836fefb27aa0 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 3 Nov 2022 15:36:18 +0000 Subject: [PATCH 0056/1420] Swift: Better assigning to tuple values. --- .../swift/dataflow/internal/DataFlowPrivate.qll | 4 ++-- .../dataflow/dataflow/DataFlow.expected | 16 +++------------- .../dataflow/dataflow/DataFlowInline.expected | 2 ++ .../dataflow/dataflow/LocalFlow.expected | 11 +++++++++++ .../library-tests/dataflow/dataflow/test.swift | 4 ++-- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index 35782564306..996fcd773b3 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -86,7 +86,7 @@ private module Cached { hasExprNode(n, [ any(Argument arg | modifiable(arg)).getExpr(), any(MemberRefExpr ref).getBase(), - any(ApplyExpr apply).getQualifier() + any(ApplyExpr apply).getQualifier(), any(TupleElementExpr te).getSubExpr() ]) } @@ -516,7 +516,7 @@ predicate storeStep(Node node1, ContentSet c, Node node2) { exists(TupleElementExpr tuple, AssignExpr assign | tuple = assign.getDest() and node1.asExpr() = assign.getSource() and - node2/*.(PostUpdateNode).getPreUpdateNode()*/.asExpr() = tuple.getSubExpr() and + node2.(PostUpdateNode).getPreUpdateNode().asExpr() = tuple.getSubExpr() and c.isSingleton(any(Content::TupleContent ct | ct.getIndex() = tuple.getIndex())) ) or diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected index 2b5638078e1..c4e0a7a01e6 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected @@ -108,15 +108,11 @@ edges | test.swift:266:15:266:16 | ...? : | test.swift:266:15:266:25 | call to signum() : | | test.swift:266:15:266:25 | call to signum() : | test.swift:266:15:266:25 | OptionalEvaluationExpr | | test.swift:277:14:277:26 | (...) [Tuple element at index 1] : | test.swift:281:15:281:15 | t1 [Tuple element at index 1] : | -| test.swift:277:14:277:26 | (...) [Tuple element at index 1] : | test.swift:287:15:287:15 | t1 [Tuple element at index 1] : | -| test.swift:277:14:277:26 | (...) [Tuple element at index 1] : | test.swift:293:15:293:15 | t1 [Tuple element at index 1] : | | test.swift:277:18:277:25 | call to source() : | test.swift:277:14:277:26 | (...) [Tuple element at index 1] : | | test.swift:281:15:281:15 | t1 [Tuple element at index 1] : | test.swift:281:15:281:18 | .1 | -| test.swift:287:15:287:15 | t1 [Tuple element at index 1] : | test.swift:287:15:287:18 | .1 | -| test.swift:289:5:289:5 | t1 [Tuple element at index 0] : | test.swift:292:15:292:15 | t1 [Tuple element at index 0] : | -| test.swift:289:12:289:19 | call to source() : | test.swift:289:5:289:5 | t1 [Tuple element at index 0] : | +| test.swift:289:5:289:5 | [post] t1 [Tuple element at index 0] : | test.swift:292:15:292:15 | t1 [Tuple element at index 0] : | +| test.swift:289:12:289:19 | call to source() : | test.swift:289:5:289:5 | [post] t1 [Tuple element at index 0] : | | test.swift:292:15:292:15 | t1 [Tuple element at index 0] : | test.swift:292:15:292:18 | .0 | -| test.swift:293:15:293:15 | t1 [Tuple element at index 1] : | test.swift:293:15:293:18 | .1 | | test.swift:297:14:297:45 | (...) [Tuple element at index 0] : | test.swift:302:15:302:15 | t1 [Tuple element at index 0] : | | test.swift:297:14:297:45 | (...) [Tuple element at index 0] : | test.swift:306:15:306:15 | t2 [Tuple element at index 0] : | | test.swift:297:14:297:45 | (...) [Tuple element at index 1] : | test.swift:303:15:303:15 | t1 [Tuple element at index 1] : | @@ -251,14 +247,10 @@ nodes | test.swift:277:18:277:25 | call to source() : | semmle.label | call to source() : | | test.swift:281:15:281:15 | t1 [Tuple element at index 1] : | semmle.label | t1 [Tuple element at index 1] : | | test.swift:281:15:281:18 | .1 | semmle.label | .1 | -| test.swift:287:15:287:15 | t1 [Tuple element at index 1] : | semmle.label | t1 [Tuple element at index 1] : | -| test.swift:287:15:287:18 | .1 | semmle.label | .1 | -| test.swift:289:5:289:5 | t1 [Tuple element at index 0] : | semmle.label | t1 [Tuple element at index 0] : | +| test.swift:289:5:289:5 | [post] t1 [Tuple element at index 0] : | semmle.label | [post] t1 [Tuple element at index 0] : | | test.swift:289:12:289:19 | call to source() : | semmle.label | call to source() : | | test.swift:292:15:292:15 | t1 [Tuple element at index 0] : | semmle.label | t1 [Tuple element at index 0] : | | test.swift:292:15:292:18 | .0 | semmle.label | .0 | -| test.swift:293:15:293:15 | t1 [Tuple element at index 1] : | semmle.label | t1 [Tuple element at index 1] : | -| test.swift:293:15:293:18 | .1 | semmle.label | .1 | | test.swift:297:14:297:45 | (...) [Tuple element at index 0] : | semmle.label | (...) [Tuple element at index 0] : | | test.swift:297:14:297:45 | (...) [Tuple element at index 1] : | semmle.label | (...) [Tuple element at index 1] : | | test.swift:297:18:297:25 | call to source() : | semmle.label | call to source() : | @@ -332,9 +324,7 @@ subpaths | test.swift:265:15:265:31 | call to signum() | test.swift:265:15:265:22 | call to source() : | test.swift:265:15:265:31 | call to signum() | result | | test.swift:266:15:266:25 | OptionalEvaluationExpr | test.swift:259:12:259:19 | call to source() : | test.swift:266:15:266:25 | OptionalEvaluationExpr | result | | test.swift:281:15:281:18 | .1 | test.swift:277:18:277:25 | call to source() : | test.swift:281:15:281:18 | .1 | result | -| test.swift:287:15:287:18 | .1 | test.swift:277:18:277:25 | call to source() : | test.swift:287:15:287:18 | .1 | result | | test.swift:292:15:292:18 | .0 | test.swift:289:12:289:19 | call to source() : | test.swift:292:15:292:18 | .0 | result | -| test.swift:293:15:293:18 | .1 | test.swift:277:18:277:25 | call to source() : | test.swift:293:15:293:18 | .1 | result | | test.swift:302:15:302:18 | .0 | test.swift:297:18:297:25 | call to source() : | test.swift:302:15:302:18 | .0 | result | | test.swift:303:15:303:18 | .1 | test.swift:297:31:297:38 | call to source() : | test.swift:303:15:303:18 | .1 | result | | test.swift:306:15:306:18 | .0 | test.swift:297:18:297:25 | call to source() : | test.swift:306:15:306:18 | .0 | result | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlowInline.expected b/swift/ql/test/library-tests/dataflow/dataflow/DataFlowInline.expected index e69de29bb2d..0ecaa82d583 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/DataFlowInline.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlowInline.expected @@ -0,0 +1,2 @@ +| test.swift:287:21:288:1 | // $ flow=277\n | Missing result:flow=277 | +| test.swift:293:21:294:1 | // $ flow=277\n | Missing result:flow=277 | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected index 2e945f96294..987d7073b87 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected @@ -195,14 +195,21 @@ | test.swift:277:9:277:9 | WriteDef | test.swift:279:15:279:15 | t1 | | test.swift:277:14:277:26 | (...) | test.swift:277:9:277:9 | WriteDef | | test.swift:279:15:279:15 | t1 | test.swift:280:15:280:15 | t1 | +| test.swift:280:15:280:15 | [post] t1 | test.swift:281:15:281:15 | t1 | | test.swift:280:15:280:15 | t1 | test.swift:281:15:281:15 | t1 | +| test.swift:281:15:281:15 | [post] t1 | test.swift:283:5:283:5 | t1 | | test.swift:281:15:281:15 | t1 | test.swift:283:5:283:5 | t1 | +| test.swift:283:5:283:5 | [post] t1 | test.swift:285:15:285:15 | t1 | | test.swift:283:5:283:5 | t1 | test.swift:285:15:285:15 | t1 | | test.swift:285:15:285:15 | t1 | test.swift:286:15:286:15 | t1 | +| test.swift:286:15:286:15 | [post] t1 | test.swift:287:15:287:15 | t1 | | test.swift:286:15:286:15 | t1 | test.swift:287:15:287:15 | t1 | +| test.swift:287:15:287:15 | [post] t1 | test.swift:289:5:289:5 | t1 | | test.swift:287:15:287:15 | t1 | test.swift:289:5:289:5 | t1 | +| test.swift:289:5:289:5 | [post] t1 | test.swift:291:15:291:15 | t1 | | test.swift:289:5:289:5 | t1 | test.swift:291:15:291:15 | t1 | | test.swift:291:15:291:15 | t1 | test.swift:292:15:292:15 | t1 | +| test.swift:292:15:292:15 | [post] t1 | test.swift:293:15:293:15 | t1 | | test.swift:292:15:292:15 | t1 | test.swift:293:15:293:15 | t1 | | test.swift:297:9:297:9 | WriteDef | test.swift:298:14:298:14 | t1 | | test.swift:297:14:297:45 | (...) | test.swift:297:9:297:9 | WriteDef | @@ -217,8 +224,12 @@ | test.swift:299:21:299:21 | t1 | test.swift:299:9:299:17 | WriteDef | | test.swift:299:21:299:21 | t1 | test.swift:301:15:301:15 | t1 | | test.swift:301:15:301:15 | t1 | test.swift:302:15:302:15 | t1 | +| test.swift:302:15:302:15 | [post] t1 | test.swift:303:15:303:15 | t1 | | test.swift:302:15:302:15 | t1 | test.swift:303:15:303:15 | t1 | +| test.swift:303:15:303:15 | [post] t1 | test.swift:304:15:304:15 | t1 | | test.swift:303:15:303:15 | t1 | test.swift:304:15:304:15 | t1 | | test.swift:305:15:305:15 | t2 | test.swift:306:15:306:15 | t2 | +| test.swift:306:15:306:15 | [post] t2 | test.swift:307:15:307:15 | t2 | | test.swift:306:15:306:15 | t2 | test.swift:307:15:307:15 | t2 | +| test.swift:307:15:307:15 | [post] t2 | test.swift:308:15:308:15 | t2 | | test.swift:307:15:307:15 | t2 | test.swift:308:15:308:15 | t2 | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/test.swift b/swift/ql/test/library-tests/dataflow/dataflow/test.swift index 522a58aac02..77d503dec59 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/test.swift +++ b/swift/ql/test/library-tests/dataflow/dataflow/test.swift @@ -284,13 +284,13 @@ func testTuples() { sink(arg: t1) sink(arg: t1.0) - sink(arg: t1.1) // $ SPURIOUS: flow=277 + sink(arg: t1.1) // $ flow=277 t1.0 = source() sink(arg: t1) sink(arg: t1.0) // $ flow=289 - sink(arg: t1.1) // $ SPURIOUS: flow=277 + sink(arg: t1.1) // $ flow=277 } func testTuples2() { From 86cbf1b82cd178f8604473203fb83de391c8d655 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 3 Nov 2022 18:45:43 +0000 Subject: [PATCH 0057/1420] Swift: Add comments. --- .../lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index 996fcd773b3..4b7193df8e5 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -500,6 +500,7 @@ predicate jumpStep(Node pred, Node succ) { } predicate storeStep(Node node1, ContentSet c, Node node2) { + // assignment to a member variable `obj.member = value` exists(MemberRefExpr ref, AssignExpr assign | ref = assign.getDest() and node1.asExpr() = assign.getSource() and @@ -507,12 +508,14 @@ predicate storeStep(Node node1, ContentSet c, Node node2) { c.isSingleton(any(Content::FieldContent ct | ct.getField() = ref.getMember())) ) or + // creation of a tuple `(v1, v2)` exists(TupleExpr tuple, int pos | node1.asExpr() = tuple.getElement(pos) and node2.asExpr() = tuple and c.isSingleton(any(Content::TupleContent ct | ct.getIndex() = pos)) ) or + // assignment to a tuple member `tuple.index = value` exists(TupleElementExpr tuple, AssignExpr assign | tuple = assign.getDest() and node1.asExpr() = assign.getSource() and @@ -526,6 +529,7 @@ predicate storeStep(Node node1, ContentSet c, Node node2) { predicate isLValue(Expr e) { any(AssignExpr assign).getDest() = e } predicate readStep(Node node1, ContentSet c, Node node2) { + // read of a member variable `obj.member` exists(MemberRefExpr ref | not isLValue(ref) and node1.asExpr() = ref.getBase() and @@ -533,6 +537,7 @@ predicate readStep(Node node1, ContentSet c, Node node2) { c.isSingleton(any(Content::FieldContent ct | ct.getField() = ref.getMember())) ) or + // read of a tuple member `tuple.index` exists(TupleElementExpr tuple | node1.asExpr() = tuple.getSubExpr() and node2.asExpr() = tuple and From 20147e87b2a1b267bf684443d326356239efee62 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 4 Nov 2022 09:38:12 +0000 Subject: [PATCH 0058/1420] Swift: Correct var names. --- .../codeql/swift/dataflow/internal/DataFlowPrivate.qll | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index 4b7193df8e5..4e828bfaf27 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -179,7 +179,7 @@ private module Cached { cached newtype TContent = TFieldContent(FieldDecl f) or - TTupleContent(int index) { exists(any(TupleExpr tn).getElement(index)) } + TTupleContent(int index) { exists(any(TupleExpr te).getElement(index)) } } /** @@ -512,7 +512,7 @@ predicate storeStep(Node node1, ContentSet c, Node node2) { exists(TupleExpr tuple, int pos | node1.asExpr() = tuple.getElement(pos) and node2.asExpr() = tuple and - c.isSingleton(any(Content::TupleContent ct | ct.getIndex() = pos)) + c.isSingleton(any(Content::TupleContent tc | tc.getIndex() = pos)) ) or // assignment to a tuple member `tuple.index = value` @@ -520,7 +520,7 @@ predicate storeStep(Node node1, ContentSet c, Node node2) { tuple = assign.getDest() and node1.asExpr() = assign.getSource() and node2.(PostUpdateNode).getPreUpdateNode().asExpr() = tuple.getSubExpr() and - c.isSingleton(any(Content::TupleContent ct | ct.getIndex() = tuple.getIndex())) + c.isSingleton(any(Content::TupleContent tc | tc.getIndex() = tuple.getIndex())) ) or FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2) @@ -541,7 +541,7 @@ predicate readStep(Node node1, ContentSet c, Node node2) { exists(TupleElementExpr tuple | node1.asExpr() = tuple.getSubExpr() and node2.asExpr() = tuple and - c.isSingleton(any(Content::TupleContent ct | ct.getIndex() = tuple.getIndex())) + c.isSingleton(any(Content::TupleContent tc | tc.getIndex() = tuple.getIndex())) ) } From 3c07ff592ac37ba905187a96f0bff89592cda9f6 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 4 Nov 2022 09:44:48 +0000 Subject: [PATCH 0059/1420] Swift: Fix result expectations. --- .../library-tests/dataflow/dataflow/DataFlowInline.expected | 2 -- swift/ql/test/library-tests/dataflow/dataflow/test.swift | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlowInline.expected b/swift/ql/test/library-tests/dataflow/dataflow/DataFlowInline.expected index 0ecaa82d583..e69de29bb2d 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/DataFlowInline.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlowInline.expected @@ -1,2 +0,0 @@ -| test.swift:287:21:288:1 | // $ flow=277\n | Missing result:flow=277 | -| test.swift:293:21:294:1 | // $ flow=277\n | Missing result:flow=277 | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/test.swift b/swift/ql/test/library-tests/dataflow/dataflow/test.swift index 77d503dec59..23c8d138a2d 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/test.swift +++ b/swift/ql/test/library-tests/dataflow/dataflow/test.swift @@ -284,13 +284,13 @@ func testTuples() { sink(arg: t1) sink(arg: t1.0) - sink(arg: t1.1) // $ flow=277 + sink(arg: t1.1) t1.0 = source() sink(arg: t1) sink(arg: t1.0) // $ flow=289 - sink(arg: t1.1) // $ flow=277 + sink(arg: t1.1) } func testTuples2() { From f0b505876068c2c2e4f15ca1f634e9d817cd4689 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 2 Nov 2022 10:58:30 +0100 Subject: [PATCH 0060/1420] C#: Remove filename from telemetry output. --- csharp/ql/src/Telemetry/ExternalApi.qll | 38 +++++++------------ .../ql/src/Telemetry/ExternalLibraryUsage.ql | 20 +++++----- 2 files changed, 24 insertions(+), 34 deletions(-) diff --git a/csharp/ql/src/Telemetry/ExternalApi.qll b/csharp/ql/src/Telemetry/ExternalApi.qll index d4cc0ae43cc..cf201b08737 100644 --- a/csharp/ql/src/Telemetry/ExternalApi.qll +++ b/csharp/ql/src/Telemetry/ExternalApi.qll @@ -42,22 +42,12 @@ class ExternalApi extends DotNet::Callable { /** * Gets the namespace of this API. */ - private string getNamespace() { this.getDeclaringType().hasQualifiedName(result, _) } + string getNamespace() { this.getDeclaringType().hasQualifiedName(result, _) } /** - * Gets the assembly file name containing this API. + * Gets the namespace and signature of this API. */ - private string getAssembly() { result = this.getFile().getBaseName() } - - /** - * Gets the assembly file name and namespace of this API. - */ - string getInfoPrefix() { result = this.getAssembly() + "#" + this.getNamespace() } - - /** - * Gets the assembly file name, namespace and signature of this API. - */ - string getInfo() { result = this.getInfoPrefix() + "#" + this.getSignature() } + string getApiName() { result = this.getNamespace() + "#" + this.getSignature() } /** Gets a call to this API callable. */ DispatchCall getACall() { @@ -125,30 +115,30 @@ signature predicate relevantApi(ExternalApi api); * for restricting the number or returned results based on a certain limit. */ module Results { - private int getUsages(string apiInfo) { + private int getUsages(string apiName) { result = strictcount(DispatchCall c, ExternalApi api | c = api.getACall() and - apiInfo = api.getInfo() and + apiName = api.getApiName() and getRelevantUsages(api) ) } - private int getOrder(string apiInfo) { - apiInfo = - rank[result](string info, int usages | - usages = getUsages(info) + private int getOrder(string apiName) { + apiName = + rank[result](string name, int usages | + usages = getUsages(name) | - info order by usages desc, info + name order by usages desc, name ) } /** - * Holds if there exists an API with `apiInfo` that is being used `usages` times + * Holds if there exists an API with `apiName` that is being used `usages` times * and if it is in the top results (guarded by resultLimit). */ - predicate restrict(string apiInfo, int usages) { - usages = getUsages(apiInfo) and - getOrder(apiInfo) <= resultLimit() + predicate restrict(string apiName, int usages) { + usages = getUsages(apiName) and + getOrder(apiName) <= resultLimit() } } diff --git a/csharp/ql/src/Telemetry/ExternalLibraryUsage.ql b/csharp/ql/src/Telemetry/ExternalLibraryUsage.ql index dfc1a8e062c..a9f70b84394 100644 --- a/csharp/ql/src/Telemetry/ExternalLibraryUsage.ql +++ b/csharp/ql/src/Telemetry/ExternalLibraryUsage.ql @@ -1,6 +1,6 @@ /** * @name External libraries - * @description A list of external libraries used in the code + * @description A list of external libraries used in the code given by their namespace. * @kind metric * @tags summary telemetry * @id csharp/telemetry/external-libs @@ -10,23 +10,23 @@ private import csharp private import semmle.code.csharp.dispatch.Dispatch private import ExternalApi -private predicate getRelevantUsages(string info, int usages) { +private predicate getRelevantUsages(string namespace, int usages) { usages = strictcount(DispatchCall c, ExternalApi api | c = api.getACall() and - api.getInfoPrefix() = info and + api.getNamespace() = namespace and not api.isUninteresting() ) } -private int getOrder(string info) { - info = +private int getOrder(string namespace) { + namespace = rank[result](string i, int usages | getRelevantUsages(i, usages) | i order by usages desc, i) } -from ExternalApi api, string info, int usages +from ExternalApi api, string namespace, int usages where - info = api.getInfoPrefix() and - getRelevantUsages(info, usages) and - getOrder(info) <= resultLimit() -select info, usages order by usages desc + namespace = api.getNamespace() and + getRelevantUsages(namespace, usages) and + getOrder(namespace) <= resultLimit() +select namespace, usages order by usages desc From fec4d1992d33a01956e569ebf0872e7eaf55ee67 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 2 Nov 2022 11:00:53 +0100 Subject: [PATCH 0061/1420] C#: Update telemetry expected output. --- .../Telemetry/LibraryUsage/ExternalLibraryUsage.expected | 4 ++-- .../Telemetry/LibraryUsage/SupportedExternalTaint.expected | 2 +- .../SupportedExternalSinks/SupportedExternalSinks.expected | 4 ++-- .../SupportedExternalSources.expected | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.expected b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.expected index e64b827f137..7b5c4c57661 100644 --- a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.expected +++ b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.expected @@ -1,2 +1,2 @@ -| System.Private.CoreLib.dll#System | 5 | -| System.Private.CoreLib.dll#System.Collections.Generic | 2 | +| System | 5 | +| System.Collections.Generic | 2 | diff --git a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/SupportedExternalTaint.expected b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/SupportedExternalTaint.expected index e9be925c286..b386e4acb38 100644 --- a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/SupportedExternalTaint.expected +++ b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/SupportedExternalTaint.expected @@ -1 +1 @@ -| System.Private.CoreLib.dll#System.Collections.Generic#List<>.Add(T) | 2 | +| System.Collections.Generic#List<>.Add(T) | 2 | diff --git a/csharp/ql/test/query-tests/Telemetry/SupportedExternalSinks/SupportedExternalSinks.expected b/csharp/ql/test/query-tests/Telemetry/SupportedExternalSinks/SupportedExternalSinks.expected index b85e33ddae7..b3acebc02e0 100644 --- a/csharp/ql/test/query-tests/Telemetry/SupportedExternalSinks/SupportedExternalSinks.expected +++ b/csharp/ql/test/query-tests/Telemetry/SupportedExternalSinks/SupportedExternalSinks.expected @@ -1,2 +1,2 @@ -| System.Web.cs#System.Web#HttpResponse.Write(System.Object) | 2 | -| System.Web.cs#System.Web#HttpResponse.WriteFile(System.String) | 1 | +| System.Web#HttpResponse.Write(System.Object) | 2 | +| System.Web#HttpResponse.WriteFile(System.String) | 1 | diff --git a/csharp/ql/test/query-tests/Telemetry/SupportedExternalSources/SupportedExternalSources.expected b/csharp/ql/test/query-tests/Telemetry/SupportedExternalSources/SupportedExternalSources.expected index 0f125b04b94..93c23f159a6 100644 --- a/csharp/ql/test/query-tests/Telemetry/SupportedExternalSources/SupportedExternalSources.expected +++ b/csharp/ql/test/query-tests/Telemetry/SupportedExternalSources/SupportedExternalSources.expected @@ -1,2 +1,2 @@ -| System.Console.dll#System#Console.ReadLine() | 2 | -| System.Console.dll#System#Console.Read() | 1 | +| System#Console.ReadLine() | 2 | +| System#Console.Read() | 1 | From be1129e782bef1a47bee305bf2fa3982f1471fe7 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 3 Nov 2022 09:36:23 +0100 Subject: [PATCH 0062/1420] C#: Only consider effectively public methods. --- csharp/ql/src/Telemetry/ExternalApi.qll | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/csharp/ql/src/Telemetry/ExternalApi.qll b/csharp/ql/src/Telemetry/ExternalApi.qll index cf201b08737..98678946761 100644 --- a/csharp/ql/src/Telemetry/ExternalApi.qll +++ b/csharp/ql/src/Telemetry/ExternalApi.qll @@ -28,7 +28,11 @@ class TestLibrary extends RefType { * An external API from either the C# Standard Library or a 3rd party library. */ class ExternalApi extends DotNet::Callable { - ExternalApi() { this.isUnboundDeclaration() and this.fromLibrary() } + ExternalApi() { + this.isUnboundDeclaration() and + this.fromLibrary() and + this.(Modifiable).isEffectivelyPublic() + } /** * Gets the unbound type, name and parameter types of this API. From e0d7e277fb318719728a072bc984333f0d6f7653 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 3 Nov 2022 13:17:07 +0100 Subject: [PATCH 0063/1420] C#: Align counting with Java and only count calls and not all possible dispatch calls. --- csharp/ql/src/Telemetry/ExternalApi.qll | 26 +++++++++---------- .../ql/src/Telemetry/ExternalLibraryUsage.ql | 4 +-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/csharp/ql/src/Telemetry/ExternalApi.qll b/csharp/ql/src/Telemetry/ExternalApi.qll index 98678946761..d3611fc0a2a 100644 --- a/csharp/ql/src/Telemetry/ExternalApi.qll +++ b/csharp/ql/src/Telemetry/ExternalApi.qll @@ -53,24 +53,24 @@ class ExternalApi extends DotNet::Callable { */ string getApiName() { result = this.getNamespace() + "#" + this.getSignature() } - /** Gets a call to this API callable. */ - DispatchCall getACall() { - this = result.getADynamicTarget().getUnboundDeclaration() - or - this = result.getAStaticTarget().getUnboundDeclaration() - } - /** Gets a node that is an input to a call to this API. */ private ArgumentNode getAnInput() { - result.getCall().(DataFlowDispatch::NonDelegateDataFlowCall).getDispatchCall() = this.getACall() + result + .getCall() + .(DataFlowDispatch::NonDelegateDataFlowCall) + .getATarget(_) + .getUnboundDeclaration() = this } /** Gets a node that is an output from a call to this API. */ private DataFlow::Node getAnOutput() { - exists(DataFlowDispatch::NonDelegateDataFlowCall call, DataFlowImplCommon::ReturnKindExt ret | - result = ret.getAnOutNode(call) + exists( + Call c, DataFlowDispatch::NonDelegateDataFlowCall dc, DataFlowImplCommon::ReturnKindExt ret | - this.getACall() = call.getDispatchCall() + dc.getDispatchCall().getCall() = c and + c.getTarget().getUnboundDeclaration() = this + | + result = ret.getAnOutNode(dc) ) } @@ -121,8 +121,8 @@ signature predicate relevantApi(ExternalApi api); module Results { private int getUsages(string apiName) { result = - strictcount(DispatchCall c, ExternalApi api | - c = api.getACall() and + strictcount(Call c, ExternalApi api | + c.getTarget().getUnboundDeclaration() = api and apiName = api.getApiName() and getRelevantUsages(api) ) diff --git a/csharp/ql/src/Telemetry/ExternalLibraryUsage.ql b/csharp/ql/src/Telemetry/ExternalLibraryUsage.ql index a9f70b84394..f06a18054dc 100644 --- a/csharp/ql/src/Telemetry/ExternalLibraryUsage.ql +++ b/csharp/ql/src/Telemetry/ExternalLibraryUsage.ql @@ -12,8 +12,8 @@ private import ExternalApi private predicate getRelevantUsages(string namespace, int usages) { usages = - strictcount(DispatchCall c, ExternalApi api | - c = api.getACall() and + strictcount(Call c, ExternalApi api | + c.getTarget().getUnboundDeclaration() = api and api.getNamespace() = namespace and not api.isUninteresting() ) From 366b94addc3dc24cea933f135abf7ef498fe14de Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 2 Nov 2022 14:55:35 +0100 Subject: [PATCH 0064/1420] C#: Implement override for getAPrimaryQlClass for AnonymousClass. --- csharp/ql/lib/semmle/code/csharp/Type.qll | 2 ++ 1 file changed, 2 insertions(+) diff --git a/csharp/ql/lib/semmle/code/csharp/Type.qll b/csharp/ql/lib/semmle/code/csharp/Type.qll index f95ded84771..dfabea580a4 100644 --- a/csharp/ql/lib/semmle/code/csharp/Type.qll +++ b/csharp/ql/lib/semmle/code/csharp/Type.qll @@ -824,6 +824,8 @@ class RecordClass extends RecordType, Class { */ class AnonymousClass extends Class { AnonymousClass() { anonymous_types(this) } + + override string getAPrimaryQlClass() { result = "AnonymousClass" } } /** From 187ece610b2fb5539fef80db6cd9d9ef7c5c57aa Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 2 Nov 2022 15:09:31 +0100 Subject: [PATCH 0065/1420] C#: Only evaluate api name and namespace strings if they are needed. --- csharp/ql/src/Telemetry/ExternalApi.qll | 3 +++ 1 file changed, 3 insertions(+) diff --git a/csharp/ql/src/Telemetry/ExternalApi.qll b/csharp/ql/src/Telemetry/ExternalApi.qll index d3611fc0a2a..653eb419b7f 100644 --- a/csharp/ql/src/Telemetry/ExternalApi.qll +++ b/csharp/ql/src/Telemetry/ExternalApi.qll @@ -37,6 +37,7 @@ class ExternalApi extends DotNet::Callable { /** * Gets the unbound type, name and parameter types of this API. */ + bindingset[this] private string getSignature() { result = this.getDeclaringType().getUnboundDeclaration() + "." + this.getName() + "(" + @@ -46,11 +47,13 @@ class ExternalApi extends DotNet::Callable { /** * Gets the namespace of this API. */ + bindingset[this] string getNamespace() { this.getDeclaringType().hasQualifiedName(result, _) } /** * Gets the namespace and signature of this API. */ + bindingset[this] string getApiName() { result = this.getNamespace() + "#" + this.getSignature() } /** Gets a node that is an input to a call to this API. */ From d58072216461907262b6fce6975c590ba82fb274 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 3 Nov 2022 15:04:19 +0100 Subject: [PATCH 0066/1420] C#: Modify unsupported external library meta query to use call instead of dispatchcall. --- csharp/ql/src/meta/frameworks/UnsupportedExternalAPIs.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/csharp/ql/src/meta/frameworks/UnsupportedExternalAPIs.ql b/csharp/ql/src/meta/frameworks/UnsupportedExternalAPIs.ql index 6332dfc515c..1934fdc72cc 100644 --- a/csharp/ql/src/meta/frameworks/UnsupportedExternalAPIs.ql +++ b/csharp/ql/src/meta/frameworks/UnsupportedExternalAPIs.ql @@ -14,9 +14,9 @@ private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSumma private import semmle.code.csharp.dataflow.internal.NegativeSummary private import Telemetry.ExternalApi -from DispatchCall c, ExternalApi api +from Call c, ExternalApi api where - c = api.getACall() and + c.getTarget().getUnboundDeclaration() = api and not api.isUninteresting() and not api.isSupported() and not api instanceof FlowSummaryImpl::Public::NegativeSummarizedCallable From d7f1491f419ca32b0197125755e77bf502bae58b Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Fri, 4 Nov 2022 17:19:42 +0100 Subject: [PATCH 0067/1420] fix non-attached annotations for newtype branches --- ql/ql/src/codeql_ql/ast/Ast.qll | 4 +++ ql/ql/test/printAst/Foo.qll | 4 +++ ql/ql/test/printAst/printAst.expected | 46 ++++++++++++++++++--------- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/ql/ql/src/codeql_ql/ast/Ast.qll b/ql/ql/src/codeql_ql/ast/Ast.qll index d6a5039a3c7..577fae69947 100644 --- a/ql/ql/src/codeql_ql/ast/Ast.qll +++ b/ql/ql/src/codeql_ql/ast/Ast.qll @@ -983,6 +983,8 @@ class NewTypeBranch extends TNewTypeBranch, Predicate, TypeDeclaration { override NewTypeBranchType getReturnType() { result.getDeclaration() = this } + override Annotation getAnAnnotation() { toQL(this).getAFieldOrChild() = toQL(result) } + override Type getParameterType(int i) { result = this.getField(i).getType() } override int getArity() { result = count(this.getField(_)) } @@ -2397,6 +2399,8 @@ private class AnnotationArg extends TAnnotationArg, AstNode { } override string toString() { result = this.getValue() } + + override string getAPrimaryQlClass() { result = "AnnotationArg" } } private class NoInlineArg extends AnnotationArg { diff --git a/ql/ql/test/printAst/Foo.qll b/ql/ql/test/printAst/Foo.qll index 17e4a4d636d..461402cd2eb 100644 --- a/ql/ql/test/printAst/Foo.qll +++ b/ql/ql/test/printAst/Foo.qll @@ -25,3 +25,7 @@ predicate calls(Foo f) { or true = false } + +newtype TPathNode = + pragma[assume_small_delta] + TPathNodeMid() diff --git a/ql/ql/test/printAst/printAst.expected b/ql/ql/test/printAst/printAst.expected index 333b42332e7..e53f9112569 100644 --- a/ql/ql/test/printAst/printAst.expected +++ b/ql/ql/test/printAst/printAst.expected @@ -1,8 +1,8 @@ nodes | Foo.qll:1:1:1:17 | Import | semmle.label | [Import] Import | | Foo.qll:1:1:1:17 | Import | semmle.order | 1 | -| Foo.qll:1:1:27:2 | TopLevel | semmle.label | [TopLevel] TopLevel | -| Foo.qll:1:1:27:2 | TopLevel | semmle.order | 1 | +| Foo.qll:1:1:31:17 | TopLevel | semmle.label | [TopLevel] TopLevel | +| Foo.qll:1:1:31:17 | TopLevel | semmle.order | 1 | | Foo.qll:1:8:1:17 | javascript | semmle.label | [ModuleExpr] javascript | | Foo.qll:1:8:1:17 | javascript | semmle.order | 3 | | Foo.qll:3:7:3:9 | Class Foo | semmle.label | [Class] Class Foo | @@ -153,6 +153,14 @@ nodes | Foo.qll:26:3:26:14 | ComparisonFormula | semmle.order | 75 | | Foo.qll:26:10:26:14 | Boolean | semmle.label | [Boolean] Boolean | | Foo.qll:26:10:26:14 | Boolean | semmle.order | 77 | +| Foo.qll:29:9:29:17 | NewType TPathNode | semmle.label | [NewType] NewType TPathNode | +| Foo.qll:29:9:29:17 | NewType TPathNode | semmle.order | 78 | +| Foo.qll:30:3:30:28 | annotation | semmle.label | [Annotation] annotation | +| Foo.qll:30:3:30:28 | annotation | semmle.order | 79 | +| Foo.qll:30:10:30:27 | assume_small_delta | semmle.label | [AnnotationArg] assume_small_delta | +| Foo.qll:30:10:30:27 | assume_small_delta | semmle.order | 80 | +| Foo.qll:31:3:31:14 | NewTypeBranch TPathNodeMid | semmle.label | [NewTypeBranch] NewTypeBranch TPathNodeMid | +| Foo.qll:31:3:31:14 | NewTypeBranch TPathNodeMid | semmle.order | 81 | | file://:0:0:0:0 | abs | semmle.label | [BuiltinPredicate] abs | | file://:0:0:0:0 | abs | semmle.label | [BuiltinPredicate] abs | | file://:0:0:0:0 | acos | semmle.label | [BuiltinPredicate] acos | @@ -235,22 +243,24 @@ nodes | file://:0:0:0:0 | trim | semmle.label | [BuiltinPredicate] trim | | file://:0:0:0:0 | ulp | semmle.label | [BuiltinPredicate] ulp | | printAst.ql:1:1:1:28 | Import | semmle.label | [Import] Import | -| printAst.ql:1:1:1:28 | Import | semmle.order | 78 | +| printAst.ql:1:1:1:28 | Import | semmle.order | 82 | | printAst.ql:1:1:1:29 | TopLevel | semmle.label | [TopLevel] TopLevel | -| printAst.ql:1:1:1:29 | TopLevel | semmle.order | 78 | +| printAst.ql:1:1:1:29 | TopLevel | semmle.order | 82 | | printAst.ql:1:18:1:28 | printAstAst | semmle.label | [ModuleExpr] printAstAst | -| printAst.ql:1:18:1:28 | printAstAst | semmle.order | 80 | +| printAst.ql:1:18:1:28 | printAstAst | semmle.order | 84 | edges | Foo.qll:1:1:1:17 | Import | Foo.qll:1:8:1:17 | javascript | semmle.label | getModuleExpr() | | Foo.qll:1:1:1:17 | Import | Foo.qll:1:8:1:17 | javascript | semmle.order | 3 | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:1:1:1:17 | Import | semmle.label | getAnImport() | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:1:1:1:17 | Import | semmle.order | 1 | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:3:7:3:9 | Class Foo | semmle.label | getAClass() | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:3:7:3:9 | Class Foo | semmle.order | 4 | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:9:17:9:19 | ClasslessPredicate foo | semmle.label | getAPredicate() | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:9:17:9:19 | ClasslessPredicate foo | semmle.order | 16 | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:13:11:13:15 | ClasslessPredicate calls | semmle.label | getAPredicate() | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:13:11:13:15 | ClasslessPredicate calls | semmle.order | 32 | +| Foo.qll:1:1:31:17 | TopLevel | Foo.qll:1:1:1:17 | Import | semmle.label | getAnImport() | +| Foo.qll:1:1:31:17 | TopLevel | Foo.qll:1:1:1:17 | Import | semmle.order | 1 | +| Foo.qll:1:1:31:17 | TopLevel | Foo.qll:3:7:3:9 | Class Foo | semmle.label | getAClass() | +| Foo.qll:1:1:31:17 | TopLevel | Foo.qll:3:7:3:9 | Class Foo | semmle.order | 4 | +| Foo.qll:1:1:31:17 | TopLevel | Foo.qll:9:17:9:19 | ClasslessPredicate foo | semmle.label | getAPredicate() | +| Foo.qll:1:1:31:17 | TopLevel | Foo.qll:9:17:9:19 | ClasslessPredicate foo | semmle.order | 16 | +| Foo.qll:1:1:31:17 | TopLevel | Foo.qll:13:11:13:15 | ClasslessPredicate calls | semmle.label | getAPredicate() | +| Foo.qll:1:1:31:17 | TopLevel | Foo.qll:13:11:13:15 | ClasslessPredicate calls | semmle.order | 32 | +| Foo.qll:1:1:31:17 | TopLevel | Foo.qll:29:9:29:17 | NewType TPathNode | semmle.label | getANewType() | +| Foo.qll:1:1:31:17 | TopLevel | Foo.qll:29:9:29:17 | NewType TPathNode | semmle.order | 78 | | Foo.qll:3:7:3:9 | Class Foo | Foo.qll:3:19:3:22 | TypeExpr | semmle.label | getASuperType() | | Foo.qll:3:7:3:9 | Class Foo | Foo.qll:3:19:3:22 | TypeExpr | semmle.order | 5 | | Foo.qll:3:7:3:9 | Class Foo | Foo.qll:4:3:4:17 | CharPred Foo | semmle.label | getCharPred() | @@ -393,9 +403,15 @@ edges | Foo.qll:26:3:26:14 | ComparisonFormula | Foo.qll:26:3:26:6 | Boolean | semmle.order | 75 | | Foo.qll:26:3:26:14 | ComparisonFormula | Foo.qll:26:10:26:14 | Boolean | semmle.label | getRightOperand() | | Foo.qll:26:3:26:14 | ComparisonFormula | Foo.qll:26:10:26:14 | Boolean | semmle.order | 77 | +| Foo.qll:29:9:29:17 | NewType TPathNode | Foo.qll:31:3:31:14 | NewTypeBranch TPathNodeMid | semmle.label | getABranch() | +| Foo.qll:29:9:29:17 | NewType TPathNode | Foo.qll:31:3:31:14 | NewTypeBranch TPathNodeMid | semmle.order | 81 | +| Foo.qll:30:3:30:28 | annotation | Foo.qll:30:10:30:27 | assume_small_delta | semmle.label | getArgs(_) | +| Foo.qll:30:3:30:28 | annotation | Foo.qll:30:10:30:27 | assume_small_delta | semmle.order | 80 | +| Foo.qll:31:3:31:14 | NewTypeBranch TPathNodeMid | Foo.qll:30:3:30:28 | annotation | semmle.label | getAnAnnotation() | +| Foo.qll:31:3:31:14 | NewTypeBranch TPathNodeMid | Foo.qll:30:3:30:28 | annotation | semmle.order | 79 | | printAst.ql:1:1:1:28 | Import | printAst.ql:1:18:1:28 | printAstAst | semmle.label | getModuleExpr() | -| printAst.ql:1:1:1:28 | Import | printAst.ql:1:18:1:28 | printAstAst | semmle.order | 80 | +| printAst.ql:1:1:1:28 | Import | printAst.ql:1:18:1:28 | printAstAst | semmle.order | 84 | | printAst.ql:1:1:1:29 | TopLevel | printAst.ql:1:1:1:28 | Import | semmle.label | getAnImport() | -| printAst.ql:1:1:1:29 | TopLevel | printAst.ql:1:1:1:28 | Import | semmle.order | 78 | +| printAst.ql:1:1:1:29 | TopLevel | printAst.ql:1:1:1:28 | Import | semmle.order | 82 | graphProperties | semmle.graphKind | tree | From bc5b7455cf879ed3a7ad381d7e8835d7f12dda15 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Wed, 2 Nov 2022 13:55:39 +0100 Subject: [PATCH 0068/1420] add failing test --- .../UnsafeShellCommandConstruction/lib/subLib/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/subLib/index.js b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/subLib/index.js index 9e105338669..fe6eaa449ae 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/subLib/index.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/subLib/index.js @@ -8,4 +8,8 @@ module.exports.foo = function (name) { cp.exec("rm -rf " + name); // NOT OK - this is being called explicitly from child_process-test.js }; -module.exports.amd = require("./amd.js"); \ No newline at end of file +module.exports.amd = require("./amd.js"); + +module.exports.arrToShell = function (cmd, arr) { + cp.spawn("echo", arr, {shell: true}); // NOT OK - but not flagged [INCONSISTENCY] +} \ No newline at end of file From 40032f295ae1625c51eba9075b905bdb6f3ce1df Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Wed, 2 Nov 2022 14:06:57 +0100 Subject: [PATCH 0069/1420] treat arrays that gets executed with shell:true as a sink for `js/shell-command-constructed-from-input` --- ...ShellCommandConstructionCustomizations.qll | 24 +++++++++++++------ .../UnsafeShellCommandConstruction.expected | 23 ++++++++++++++++++ .../lib/subLib/index.js | 2 +- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll index 0b4923de179..ca6920db466 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll @@ -156,14 +156,9 @@ module UnsafeShellCommandConstruction { } /** - * Gets a node that ends up in an array that is ultimately executed as a shell script by `sys`. + * Holds if the arguments array given to `sys` is joined as a string because `shell` is set to true. */ - private DataFlow::SourceNode endsInShellExecutedArray( - DataFlow::TypeBackTracker t, SystemCommandExecution sys - ) { - t.start() and - result = sys.getArgumentList().getALocalSource() and - // the array gets joined to a string when `shell` is set to true. + predicate executesArrayAsShell(SystemCommandExecution sys) { sys.getOptionsArg() .getALocalSource() .getAPropertyWrite("shell") @@ -171,6 +166,17 @@ module UnsafeShellCommandConstruction { .asExpr() .(BooleanLiteral) .getValue() = "true" + } + + /** + * Gets a node that ends up in an array that is ultimately executed as a shell script by `sys`. + */ + private DataFlow::SourceNode endsInShellExecutedArray( + DataFlow::TypeBackTracker t, SystemCommandExecution sys + ) { + t.start() and + result = sys.getArgumentList().getALocalSource() and + executesArrayAsShell(sys) or exists(DataFlow::TypeBackTracker t2 | result = endsInShellExecutedArray(t2, sys).backtrack(t2, t) @@ -193,6 +199,10 @@ module UnsafeShellCommandConstruction { or this = arr.getAMethodCall(["push", "unshift"]).getAnArgument() ) + or + this = sys.getArgumentList() and + not this instanceof DataFlow::ArrayCreationNode and + executesArrayAsShell(sys) } override string getSinkType() { result = "shell argument" } diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected index 4cf79a4aedd..9cf2707f58f 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected @@ -223,8 +223,14 @@ nodes | lib/lib.js:420:29:420:32 | name | | lib/lib.js:424:24:424:27 | name | | lib/lib.js:424:24:424:27 | name | +| lib/lib.js:425:6:425:13 | arr | +| lib/lib.js:425:12:425:13 | [] | | lib/lib.js:426:11:426:14 | name | | lib/lib.js:426:11:426:14 | name | +| lib/lib.js:427:14:427:16 | arr | +| lib/lib.js:427:14:427:16 | arr | +| lib/lib.js:428:14:428:58 | build(" ... + '-') | +| lib/lib.js:428:14:428:58 | build(" ... + '-') | | lib/lib.js:428:28:428:51 | (name ? ... ' : '') | | lib/lib.js:428:28:428:57 | (name ? ... ) + '-' | | lib/lib.js:428:29:428:50 | name ? ... :' : '' | @@ -302,6 +308,10 @@ nodes | lib/subLib/index.js:7:32:7:35 | name | | lib/subLib/index.js:8:22:8:25 | name | | lib/subLib/index.js:8:22:8:25 | name | +| lib/subLib/index.js:13:44:13:46 | arr | +| lib/subLib/index.js:13:44:13:46 | arr | +| lib/subLib/index.js:14:22:14:24 | arr | +| lib/subLib/index.js:14:22:14:24 | arr | edges | lib/isImported.js:5:49:5:52 | name | lib/isImported.js:6:22:6:25 | name | | lib/isImported.js:5:49:5:52 | name | lib/isImported.js:6:22:6:25 | name | @@ -575,7 +585,13 @@ edges | lib/lib.js:414:40:414:43 | name | lib/lib.js:426:11:426:14 | name | | lib/lib.js:414:40:414:43 | name | lib/lib.js:428:36:428:39 | name | | lib/lib.js:414:40:414:43 | name | lib/lib.js:428:36:428:39 | name | +| lib/lib.js:425:6:425:13 | arr | lib/lib.js:427:14:427:16 | arr | +| lib/lib.js:425:6:425:13 | arr | lib/lib.js:427:14:427:16 | arr | +| lib/lib.js:425:12:425:13 | [] | lib/lib.js:425:6:425:13 | arr | +| lib/lib.js:426:11:426:14 | name | lib/lib.js:425:12:425:13 | [] | | lib/lib.js:428:28:428:51 | (name ? ... ' : '') | lib/lib.js:428:28:428:57 | (name ? ... ) + '-' | +| lib/lib.js:428:28:428:57 | (name ? ... ) + '-' | lib/lib.js:428:14:428:58 | build(" ... + '-') | +| lib/lib.js:428:28:428:57 | (name ? ... ) + '-' | lib/lib.js:428:14:428:58 | build(" ... + '-') | | lib/lib.js:428:28:428:57 | (name ? ... ) + '-' | lib/lib.js:431:23:431:26 | last | | lib/lib.js:428:29:428:50 | name ? ... :' : '' | lib/lib.js:428:28:428:51 | (name ? ... ' : '') | | lib/lib.js:428:36:428:39 | name | lib/lib.js:428:36:428:45 | name + ':' | @@ -663,6 +679,10 @@ edges | lib/subLib/index.js:7:32:7:35 | name | lib/subLib/index.js:8:22:8:25 | name | | lib/subLib/index.js:7:32:7:35 | name | lib/subLib/index.js:8:22:8:25 | name | | lib/subLib/index.js:7:32:7:35 | name | lib/subLib/index.js:8:22:8:25 | name | +| lib/subLib/index.js:13:44:13:46 | arr | lib/subLib/index.js:14:22:14:24 | arr | +| lib/subLib/index.js:13:44:13:46 | arr | lib/subLib/index.js:14:22:14:24 | arr | +| lib/subLib/index.js:13:44:13:46 | arr | lib/subLib/index.js:14:22:14:24 | arr | +| lib/subLib/index.js:13:44:13:46 | arr | lib/subLib/index.js:14:22:14:24 | arr | #select | lib/isImported.js:6:10:6:25 | "rm -rf " + name | lib/isImported.js:5:49:5:52 | name | lib/isImported.js:6:22:6:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/isImported.js:5:49:5:52 | name | library input | lib/isImported.js:6:2:6:26 | cp.exec ... + name) | shell command | | lib/lib2.js:4:10:4:25 | "rm -rf " + name | lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/lib2.js:3:28:3:31 | name | library input | lib/lib2.js:4:2:4:26 | cp.exec ... + name) | shell command | @@ -729,6 +749,8 @@ edges | lib/lib.js:420:29:420:32 | name | lib/lib.js:414:40:414:43 | name | lib/lib.js:420:29:420:32 | name | This shell argument which depends on $@ is later used in a $@. | lib/lib.js:414:40:414:43 | name | library input | lib/lib.js:420:2:420:49 | cp.spaw ... true}) | shell command | | lib/lib.js:424:24:424:27 | name | lib/lib.js:414:40:414:43 | name | lib/lib.js:424:24:424:27 | name | This shell argument which depends on $@ is later used in a $@. | lib/lib.js:414:40:414:43 | name | library input | lib/lib.js:424:2:424:40 | spawn(" ... WN_OPT) | shell command | | lib/lib.js:426:11:426:14 | name | lib/lib.js:414:40:414:43 | name | lib/lib.js:426:11:426:14 | name | This shell argument which depends on $@ is later used in a $@. | lib/lib.js:414:40:414:43 | name | library input | lib/lib.js:427:2:427:28 | spawn(" ... WN_OPT) | shell command | +| lib/lib.js:427:14:427:16 | arr | lib/lib.js:414:40:414:43 | name | lib/lib.js:427:14:427:16 | arr | This shell argument which depends on $@ is later used in a $@. | lib/lib.js:414:40:414:43 | name | library input | lib/lib.js:427:2:427:28 | spawn(" ... WN_OPT) | shell command | +| lib/lib.js:428:14:428:58 | build(" ... + '-') | lib/lib.js:414:40:414:43 | name | lib/lib.js:428:14:428:58 | build(" ... + '-') | This shell argument which depends on $@ is later used in a $@. | lib/lib.js:414:40:414:43 | name | library input | lib/lib.js:428:2:428:70 | spawn(" ... WN_OPT) | shell command | | lib/lib.js:436:19:436:22 | last | lib/lib.js:414:40:414:43 | name | lib/lib.js:436:19:436:22 | last | This shell argument which depends on $@ is later used in a $@. | lib/lib.js:414:40:414:43 | name | library input | lib/lib.js:428:2:428:70 | spawn(" ... WN_OPT) | shell command | | lib/lib.js:442:12:442:27 | "rm -rf " + name | lib/lib.js:441:39:441:42 | name | lib/lib.js:442:24:442:27 | name | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:441:39:441:42 | name | library input | lib/lib.js:442:2:442:28 | asyncEx ... + name) | shell command | | lib/lib.js:447:13:447:28 | "rm -rf " + name | lib/lib.js:446:20:446:23 | name | lib/lib.js:447:25:447:28 | name | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:446:20:446:23 | name | library input | lib/lib.js:447:3:447:29 | asyncEx ... + name) | shell command | @@ -750,3 +772,4 @@ edges | lib/subLib/amdSub.js:4:10:4:25 | "rm -rf " + name | lib/subLib/amdSub.js:3:28:3:31 | name | lib/subLib/amdSub.js:4:22:4:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/subLib/amdSub.js:3:28:3:31 | name | library input | lib/subLib/amdSub.js:4:2:4:26 | cp.exec ... + name) | shell command | | lib/subLib/index.js:4:10:4:25 | "rm -rf " + name | lib/subLib/index.js:3:28:3:31 | name | lib/subLib/index.js:4:22:4:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/subLib/index.js:3:28:3:31 | name | library input | lib/subLib/index.js:4:2:4:26 | cp.exec ... + name) | shell command | | lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | lib/subLib/index.js:7:32:7:35 | name | lib/subLib/index.js:8:22:8:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/subLib/index.js:7:32:7:35 | name | library input | lib/subLib/index.js:8:2:8:26 | cp.exec ... + name) | shell command | +| lib/subLib/index.js:14:22:14:24 | arr | lib/subLib/index.js:13:44:13:46 | arr | lib/subLib/index.js:14:22:14:24 | arr | This shell argument which depends on $@ is later used in a $@. | lib/subLib/index.js:13:44:13:46 | arr | library input | lib/subLib/index.js:14:5:14:40 | cp.spaw ... true}) | shell command | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/subLib/index.js b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/subLib/index.js index fe6eaa449ae..6e7d3498723 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/subLib/index.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/subLib/index.js @@ -11,5 +11,5 @@ module.exports.foo = function (name) { module.exports.amd = require("./amd.js"); module.exports.arrToShell = function (cmd, arr) { - cp.spawn("echo", arr, {shell: true}); // NOT OK - but not flagged [INCONSISTENCY] + cp.spawn("echo", arr, {shell: true}); // NOT OK } \ No newline at end of file From 74ee1015923b025efd1da565c7797920bb37dd2b Mon Sep 17 00:00:00 2001 From: JarLob Date: Mon, 7 Nov 2022 13:05:37 +0100 Subject: [PATCH 0070/1420] Extend `Constant Condition` query with `String.IsNullOrEmpty`. --- .../Control-Flow/ConstantCondition.ql | 25 ++++++++ .../2022-11-07-constant-expression.md | 4 ++ .../ConstantCondition.expected | 4 ++ .../ConstantIsNullOrEmpty.cs | 60 +++++++++++++++++++ 4 files changed, 93 insertions(+) create mode 100644 csharp/ql/src/change-notes/released/2022-11-07-constant-expression.md create mode 100644 csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantIsNullOrEmpty.cs diff --git a/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql b/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql index a0542a94735..cb53d103f47 100644 --- a/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql +++ b/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql @@ -15,6 +15,7 @@ import csharp import semmle.code.csharp.commons.Assertions import semmle.code.csharp.commons.Constants +private import semmle.code.csharp.frameworks.System /** A constant condition. */ abstract class ConstantCondition extends Expr { @@ -72,6 +73,30 @@ class ConstantIfCondition extends ConstantBooleanCondition { } } +/** A constant return value from a function with constant input expression. */ +class ConstantReturnValueCondition extends ConstantCondition { + boolean b; + + ConstantReturnValueCondition() { + exists(Method m, Call c, Expr expr | + m = any(SystemStringClass s).getIsNullOrEmptyMethod() and + c.getTarget() = m and + this = c and + expr = c.getArgument(0) and + expr.hasValue() and + if expr.getValue().length() > 0 and not expr instanceof NullLiteral + then b = false + else b = true + ) + } + + override string getMessage() { + if b = true + then result = "Expression is always 'true'." + else result = "Expression is always 'false'." + } +} + /** A constant loop condition. */ class ConstantLoopCondition extends ConstantBooleanCondition { ConstantLoopCondition() { this = any(LoopStmt ls).getCondition() } diff --git a/csharp/ql/src/change-notes/released/2022-11-07-constant-expression.md b/csharp/ql/src/change-notes/released/2022-11-07-constant-expression.md new file mode 100644 index 00000000000..9e2d667d2eb --- /dev/null +++ b/csharp/ql/src/change-notes/released/2022-11-07-constant-expression.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The `Constant Condition` query was extended to catch cases when `String.IsNullOrEmpty` returns a constant value. \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantCondition.expected b/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantCondition.expected index 17a51125c4c..064837e8552 100644 --- a/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantCondition.expected +++ b/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantCondition.expected @@ -19,6 +19,10 @@ | ConstantIfCondition.cs:11:17:11:29 | ... == ... | Condition always evaluates to 'true'. | | ConstantIfCondition.cs:14:17:14:21 | false | Condition always evaluates to 'false'. | | ConstantIfCondition.cs:17:17:17:26 | ... == ... | Condition always evaluates to 'true'. | +| ConstantIsNullOrEmpty.cs:10:21:10:54 | call to method IsNullOrEmpty | Expression is always 'false'. | +| ConstantIsNullOrEmpty.cs:46:21:46:46 | call to method IsNullOrEmpty | Expression is always 'true'. | +| ConstantIsNullOrEmpty.cs:50:21:50:44 | call to method IsNullOrEmpty | Expression is always 'true'. | +| ConstantIsNullOrEmpty.cs:54:21:54:45 | call to method IsNullOrEmpty | Expression is always 'false'. | | ConstantNullCoalescingLeftHandOperand.cs:11:24:11:34 | access to constant NULL_OBJECT | Expression is never 'null'. | | ConstantNullCoalescingLeftHandOperand.cs:12:24:12:27 | null | Expression is always 'null'. | | ConstantWhileCondition.cs:12:20:12:32 | ... == ... | Condition always evaluates to 'true'. | diff --git a/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantIsNullOrEmpty.cs b/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantIsNullOrEmpty.cs new file mode 100644 index 00000000000..5cad2e818ab --- /dev/null +++ b/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantIsNullOrEmpty.cs @@ -0,0 +1,60 @@ +using System.Threading.Tasks; + +namespace ConstantIsNullOrEmpty +{ + internal class Program + { + static void Main(string[] args) + { + { + if (string.IsNullOrEmpty(nameof(args))) // bad: always false + { + } + + string? x = null; + if (string.IsNullOrEmpty(x)) // would be nice... bad: always true + { + } + + string y = ""; + if (string.IsNullOrEmpty(y)) // would be nice... bad: always true + { + } + + if (args[1] != null) + y = "b"; + if (string.IsNullOrEmpty(y)) // good: non-constant + { + } + + string z = " "; + if (string.IsNullOrEmpty(z)) // would be nice... bad: always false + { + } + + string a = "a"; + if (string.IsNullOrEmpty(a)) // would be nice... bad: always false + { + } + + if (args[1] != null) + a = ""; + if (string.IsNullOrEmpty(a)) // good: non-constant + { + } + + if (string.IsNullOrEmpty(null)) // bad: always true + { + } + + if (string.IsNullOrEmpty("")) // bad: always true + { + } + + if (string.IsNullOrEmpty(" ")) // bad: always false + { + } + } + } + } +} \ No newline at end of file From e122f94c1c5b2cbf87c2c2b8a9473db2cdc035a8 Mon Sep 17 00:00:00 2001 From: JarLob Date: Mon, 7 Nov 2022 13:38:05 +0100 Subject: [PATCH 0071/1420] Move to isBooleanConstant --- .../controlflow/internal/Completion.qll | 11 +++++++++ .../Control-Flow/ConstantCondition.ql | 24 ------------------- .../ConstantCondition.expected | 8 +++---- 3 files changed, 15 insertions(+), 28 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Completion.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Completion.qll index bda14e0b4ae..2b4adad031f 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Completion.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Completion.qll @@ -199,6 +199,17 @@ private predicate isBooleanConstant(Expr e, boolean value) { value = false or isConstantComparison(e, value) + or + exists(Method m, Call c, Expr expr | + m = any(SystemStringClass s).getIsNullOrEmptyMethod() and + c.getTarget() = m and + e = c and + expr = c.getArgument(0) and + expr.hasValue() and + if expr.getValue().length() > 0 and not expr instanceof NullLiteral + then value = false + else value = true + ) ) } diff --git a/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql b/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql index cb53d103f47..4d9fedb3633 100644 --- a/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql +++ b/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql @@ -73,30 +73,6 @@ class ConstantIfCondition extends ConstantBooleanCondition { } } -/** A constant return value from a function with constant input expression. */ -class ConstantReturnValueCondition extends ConstantCondition { - boolean b; - - ConstantReturnValueCondition() { - exists(Method m, Call c, Expr expr | - m = any(SystemStringClass s).getIsNullOrEmptyMethod() and - c.getTarget() = m and - this = c and - expr = c.getArgument(0) and - expr.hasValue() and - if expr.getValue().length() > 0 and not expr instanceof NullLiteral - then b = false - else b = true - ) - } - - override string getMessage() { - if b = true - then result = "Expression is always 'true'." - else result = "Expression is always 'false'." - } -} - /** A constant loop condition. */ class ConstantLoopCondition extends ConstantBooleanCondition { ConstantLoopCondition() { this = any(LoopStmt ls).getCondition() } diff --git a/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantCondition.expected b/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantCondition.expected index 064837e8552..397d77531b2 100644 --- a/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantCondition.expected +++ b/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantCondition.expected @@ -19,10 +19,10 @@ | ConstantIfCondition.cs:11:17:11:29 | ... == ... | Condition always evaluates to 'true'. | | ConstantIfCondition.cs:14:17:14:21 | false | Condition always evaluates to 'false'. | | ConstantIfCondition.cs:17:17:17:26 | ... == ... | Condition always evaluates to 'true'. | -| ConstantIsNullOrEmpty.cs:10:21:10:54 | call to method IsNullOrEmpty | Expression is always 'false'. | -| ConstantIsNullOrEmpty.cs:46:21:46:46 | call to method IsNullOrEmpty | Expression is always 'true'. | -| ConstantIsNullOrEmpty.cs:50:21:50:44 | call to method IsNullOrEmpty | Expression is always 'true'. | -| ConstantIsNullOrEmpty.cs:54:21:54:45 | call to method IsNullOrEmpty | Expression is always 'false'. | +| ConstantIsNullOrEmpty.cs:10:21:10:54 | call to method IsNullOrEmpty | Condition always evaluates to 'false'. | +| ConstantIsNullOrEmpty.cs:46:21:46:46 | call to method IsNullOrEmpty | Condition always evaluates to 'true'. | +| ConstantIsNullOrEmpty.cs:50:21:50:44 | call to method IsNullOrEmpty | Condition always evaluates to 'true'. | +| ConstantIsNullOrEmpty.cs:54:21:54:45 | call to method IsNullOrEmpty | Condition always evaluates to 'false'. | | ConstantNullCoalescingLeftHandOperand.cs:11:24:11:34 | access to constant NULL_OBJECT | Expression is never 'null'. | | ConstantNullCoalescingLeftHandOperand.cs:12:24:12:27 | null | Expression is always 'null'. | | ConstantWhileCondition.cs:12:20:12:32 | ... == ... | Condition always evaluates to 'true'. | From d865f2ecf5d54ee6792c8a5abcdca4914b5ab87a Mon Sep 17 00:00:00 2001 From: JarLob Date: Mon, 7 Nov 2022 14:19:24 +0100 Subject: [PATCH 0072/1420] Remove import --- csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql | 1 - 1 file changed, 1 deletion(-) diff --git a/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql b/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql index 4d9fedb3633..a0542a94735 100644 --- a/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql +++ b/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql @@ -15,7 +15,6 @@ import csharp import semmle.code.csharp.commons.Assertions import semmle.code.csharp.commons.Constants -private import semmle.code.csharp.frameworks.System /** A constant condition. */ abstract class ConstantCondition extends Expr { From 5ec22bc180da321822f694b00a5f9614e6220071 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Tue, 1 Nov 2022 09:52:31 +0100 Subject: [PATCH 0073/1420] add a shared regex pack --- .../2022-09-26-initial-version.md | 4 + shared/regex/codeql-pack.lock.yml | 4 + .../codeql/regex/OverlyLargeRangeQuery.qll | 300 ++++ shared/regex/codeql/regex/RegexTreeView.qll | 451 ++++++ .../codeql/regex/nfa/BadTagFilterQuery.qll | 177 +++ .../regex/nfa/ExponentialBackTracking.qll | 355 +++++ shared/regex/codeql/regex/nfa/NfaUtils.qll | 1378 +++++++++++++++++ .../regex/codeql/regex/nfa/RegexpMatching.qll | 176 +++ .../regex/nfa/SuperlinearBackTracking.qll | 440 ++++++ shared/regex/qlpack.yml | 5 + 10 files changed, 3290 insertions(+) create mode 100644 shared/regex/change-notes/2022-09-26-initial-version.md create mode 100644 shared/regex/codeql-pack.lock.yml create mode 100644 shared/regex/codeql/regex/OverlyLargeRangeQuery.qll create mode 100644 shared/regex/codeql/regex/RegexTreeView.qll create mode 100644 shared/regex/codeql/regex/nfa/BadTagFilterQuery.qll create mode 100644 shared/regex/codeql/regex/nfa/ExponentialBackTracking.qll create mode 100644 shared/regex/codeql/regex/nfa/NfaUtils.qll create mode 100644 shared/regex/codeql/regex/nfa/RegexpMatching.qll create mode 100644 shared/regex/codeql/regex/nfa/SuperlinearBackTracking.qll create mode 100644 shared/regex/qlpack.yml diff --git a/shared/regex/change-notes/2022-09-26-initial-version.md b/shared/regex/change-notes/2022-09-26-initial-version.md new file mode 100644 index 00000000000..e4d6e0490c2 --- /dev/null +++ b/shared/regex/change-notes/2022-09-26-initial-version.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Initial release. Extracted common regex related code, including the ReDoS analysis, into a library pack to share code between languages. diff --git a/shared/regex/codeql-pack.lock.yml b/shared/regex/codeql-pack.lock.yml new file mode 100644 index 00000000000..a046f6d9786 --- /dev/null +++ b/shared/regex/codeql-pack.lock.yml @@ -0,0 +1,4 @@ +--- +dependencies: {} +compiled: false +lockVersion: 1.0.0 \ No newline at end of file diff --git a/shared/regex/codeql/regex/OverlyLargeRangeQuery.qll b/shared/regex/codeql/regex/OverlyLargeRangeQuery.qll new file mode 100644 index 00000000000..8d3a0b9c0ff --- /dev/null +++ b/shared/regex/codeql/regex/OverlyLargeRangeQuery.qll @@ -0,0 +1,300 @@ +/** + * Classes and predicates for working with suspicious character ranges. + */ + +private import RegexTreeView + +/** + * Classes and predicates implementing an analysis detecting suspicious character ranges. + */ +module Make { + private import TreeImpl + + /** + * Gets a rank for `range` that is unique for ranges in the same file. + * Prioritizes ranges that match more characters. + */ + int rankRange(RegExpCharacterRange range) { + range = + rank[result](RegExpCharacterRange r, int startline, int startcolumn, int low, int high | + r.hasLocationInfo(_, startline, startcolumn, _, _) and + isRange(r, low, high) + | + r order by (high - low) desc, startline, startcolumn + ) + } + + /** Holds if `range` spans from the unicode code points `low` to `high` (both inclusive). */ + predicate isRange(RegExpCharacterRange range, int low, int high) { + exists(string lowc, string highc | + range.isRange(lowc, highc) and + low.toUnicode() = lowc and + high.toUnicode() = highc + ) + } + + /** Holds if `char` is an alpha-numeric character. */ + predicate isAlphanumeric(string char) { + // written like this to avoid having a bindingset for the predicate + char = [[48 .. 57], [65 .. 90], [97 .. 122]].toUnicode() // 0-9, A-Z, a-z + } + + /** + * Holds if the given ranges are from the same character class + * and there exists at least one character matched by both ranges. + */ + predicate overlap(RegExpCharacterRange a, RegExpCharacterRange b) { + exists(RegExpCharacterClass clz | + a = clz.getAChild() and + b = clz.getAChild() and + a != b + | + exists(int alow, int ahigh, int blow, int bhigh | + isRange(a, alow, ahigh) and + isRange(b, blow, bhigh) and + alow <= bhigh and + blow <= ahigh + ) + ) + } + + /** + * Holds if `range` overlaps with the char class `escape` from the same character class. + */ + predicate overlapsWithCharEscape(RegExpCharacterRange range, RegExpCharacterClassEscape escape) { + exists(RegExpCharacterClass clz, string low, string high | + range = clz.getAChild() and + escape = clz.getAChild() and + range.isRange(low, high) + | + escape.getValue() = "w" and + getInRange(low, high).regexpMatch("\\w") + or + escape.getValue() = "d" and + getInRange(low, high).regexpMatch("\\d") + or + escape.getValue() = "s" and + getInRange(low, high).regexpMatch("\\s") + ) + } + + /** Gets the unicode code point for a `char`. */ + bindingset[char] + int toCodePoint(string char) { result.toUnicode() = char } + + /** A character range that appears to be overly wide. */ + class OverlyWideRange instanceof RegExpCharacterRange { + OverlyWideRange() { + exists(int low, int high, int numChars | + isRange(this, low, high) and + numChars = (1 + high - low) and + this.getRootTerm().isUsedAsRegExp() and + numChars >= 10 + | + // across the Z-a range (which includes backticks) + toCodePoint("Z") >= low and + toCodePoint("a") <= high + or + // across the 9-A range (which includes e.g. ; and ?) + toCodePoint("9") >= low and + toCodePoint("A") <= high + or + // a non-alphanumeric char as part of the range boundaries + exists(int bound | bound = [low, high] | not isAlphanumeric(bound.toUnicode())) and + // while still being ascii + low < 128 and + high < 128 + ) and + // allowlist for known ranges + not this = allowedWideRanges() + } + + /** Gets a string representation of a character class that matches the same chars as this range. */ + string printEquivalent() { result = RangePrinter::printEquivalentCharClass(this) } + + /** Gets a string representation of this range. */ + string toString() { result = super.toString() } + + /** Holds if `lo` is the lower bound of this character range and `hi` the upper bound. */ + predicate isRange(string lo, string hi) { super.isRange(lo, hi) } + } + + /** Gets a range that should not be reported as an overly wide range. */ + RegExpCharacterRange allowedWideRanges() { + // ~ is the last printable ASCII character, it's used right in various wide ranges. + result.isRange(_, "~") + or + // the same with " " and "!". " " is the first printable character, and "!" is the first non-white-space printable character. + result.isRange([" ", "!"], _) + or + // the `[@-_]` range is intentional + result.isRange("@", "_") + or + // starting from the zero byte is a good indication that it's purposely matching a large range. + result.isRange(0.toUnicode(), _) + } + + /** Gets a char between (and including) `low` and `high`. */ + bindingset[low, high] + private string getInRange(string low, string high) { + result = [toCodePoint(low) .. toCodePoint(high)].toUnicode() + } + + /** A module computing an equivalent character class for an overly wide range. */ + module RangePrinter { + bindingset[char] + bindingset[result] + private string next(string char) { + exists(int prev, int next | + prev.toUnicode() = char and + next.toUnicode() = result and + next = prev + 1 + ) + } + + /** Gets the points where the parts of the pretty printed range should be cut off. */ + private string cutoffs() { result = ["A", "Z", "a", "z", "0", "9"] } + + /** Gets the char to use in the low end of a range for a given `cut` */ + private string lowCut(string cut) { + cut = ["A", "a", "0"] and + result = cut + or + cut = ["Z", "z", "9"] and + result = next(cut) + } + + /** Gets the char to use in the high end of a range for a given `cut` */ + private string highCut(string cut) { + cut = ["Z", "z", "9"] and + result = cut + or + cut = ["A", "a", "0"] and + next(result) = cut + } + + /** Gets the cutoff char used for a given `part` of a range when pretty-printing it. */ + private string cutoff(OverlyWideRange range, int part) { + exists(int low, int high | isRange(range, low, high) | + result = + rank[part + 1](string cut | + cut = cutoffs() and low < toCodePoint(cut) and toCodePoint(cut) < high + | + cut order by toCodePoint(cut) + ) + ) + } + + /** Gets the number of parts we should print for a given `range`. */ + private int parts(OverlyWideRange range) { result = 1 + count(cutoff(range, _)) } + + /** Holds if the given part of a range should span from `low` to `high`. */ + private predicate part(OverlyWideRange range, int part, string low, string high) { + // first part. + part = 0 and + ( + range.isRange(low, high) and + parts(range) = 1 + or + parts(range) >= 2 and + range.isRange(low, _) and + high = highCut(cutoff(range, part)) + ) + or + // middle + part >= 1 and + part < parts(range) - 1 and + low = lowCut(cutoff(range, part - 1)) and + high = highCut(cutoff(range, part)) + or + // last. + part = parts(range) - 1 and + low = lowCut(cutoff(range, part - 1)) and + range.isRange(_, high) + } + + /** Gets an escaped `char` for use in a character class. */ + bindingset[char] + private string escape(string char) { + exists(string reg | reg = "(\\[|\\]|\\\\|-|/)" | + if char.regexpMatch(reg) then result = "\\" + char else result = char + ) + } + + /** Gets a part of the equivalent range. */ + private string printEquivalentCharClass(OverlyWideRange range, int part) { + exists(string low, string high | part(range, part, low, high) | + if + isAlphanumeric(low) and + isAlphanumeric(high) + then result = low + "-" + high + else + result = + strictconcat(string char | char = getInRange(low, high) | escape(char) order by char) + ) + } + + /** Gets the entire pretty printed equivalent range. */ + string printEquivalentCharClass(OverlyWideRange range) { + result = + strictconcat(string r, int part | + r = "[" and part = -1 and exists(range) + or + r = printEquivalentCharClass(range, part) + or + r = "]" and part = parts(range) + | + r order by part + ) + } + } + + /** Gets a char range that is overly large because of `reason`. */ + RegExpCharacterRange getABadRange(string reason, int priority) { + result instanceof OverlyWideRange and + priority = 0 and + exists(string equiv | equiv = result.(OverlyWideRange).printEquivalent() | + if equiv.length() <= 50 + then reason = "is equivalent to " + equiv + else reason = "is equivalent to " + equiv.substring(0, 50) + "..." + ) + or + priority = 1 and + exists(RegExpCharacterRange other | + reason = "overlaps with " + other + " in the same character class" and + rankRange(result) < rankRange(other) and + overlap(result, other) + ) + or + priority = 2 and + exists(RegExpCharacterClassEscape escape | + reason = "overlaps with " + escape + " in the same character class" and + overlapsWithCharEscape(result, escape) + ) + or + reason = "is empty" and + priority = 3 and + exists(int low, int high | + isRange(result, low, high) and + low > high + ) + } + + /** Holds if `range` matches suspiciously many characters. */ + predicate problem(RegExpCharacterRange range, string reason) { + reason = + strictconcat(string m, int priority | + range = getABadRange(m, priority) + | + m, ", and " order by priority desc + ) and + // specifying a range using an escape is usually OK. + not range.getAChild() instanceof RegExpEscape and + // Unicode escapes in strings are interpreted before it turns into a regexp, + // so e.g. [\u0001-\uFFFF] will just turn up as a range between two constants. + // We therefore exclude these ranges. + range.getRootTerm().getParent() instanceof RegExpLiteral and + // is used as regexp (mostly for JS where regular expressions are parsed eagerly) + range.getRootTerm().isUsedAsRegExp() + } +} diff --git a/shared/regex/codeql/regex/RegexTreeView.qll b/shared/regex/codeql/regex/RegexTreeView.qll new file mode 100644 index 00000000000..f805bd83185 --- /dev/null +++ b/shared/regex/codeql/regex/RegexTreeView.qll @@ -0,0 +1,451 @@ +/** + * This file contains a `RegexTreeViewSig` module describing the syntax tree of regular expressions. + */ + +/** + * A signature describing the syntax tree of regular expressions. + */ +signature module RegexTreeViewSig { + /** + * An element used in some way as or in a regular expression. + * This class exists to have a common supertype that all languages can agree on. + */ + class Top; + + /** + * An element containing a regular expression term, that is, either + * a string literal (parsed as a regular expression; the root of the parse tree) + * or another regular expression term (a descendant of the root). + */ + class RegExpParent extends Top; + + /** + * A regular expression literal. + * + * Note that this class does not cover regular expressions constructed by calling the built-in + * `RegExp` function. + * + * Example: + * + * ``` + * /(?i)ab*c(d|e)$/ + * ``` + */ + class RegExpLiteral extends RegExpParent; + + /** + * A regular expression term, that is, a syntactic part of a regular expression. + * These are the tree nodes that form the parse tree of a regular expression literal. + */ + class RegExpTerm extends Top { + /** Gets a child term of this term. */ + RegExpTerm getAChild(); + + /** + * Holds if this is the root term of a regular expression. + */ + predicate isRootTerm(); + + /** + * Gets the parent term of this regular expression term, or the + * regular expression literal if this is the root term. + */ + RegExpParent getParent(); + + /** + * Holds if this term is part of a regular expression literal, or a string literal + * that is interpreted as a regular expression. + */ + predicate isUsedAsRegExp(); + + /** Gets the outermost term of this regular expression. */ + RegExpTerm getRootTerm(); + + /** Gets the raw source text of this term. */ + string getRawValue(); + + /** Gets the `i`th child term of this term. */ + RegExpTerm getChild(int i); + + /** Gets the number of child terms of this term. */ + int getNumChild(); + + /** Gets the regular expression term that is matched (textually) after this one, if any. */ + RegExpTerm getSuccessor(); + + string toString(); + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ); + } + + /** + * A quantified regular expression term. + * + * Example: + * + * ``` + * ((ECMA|Java)[sS]cript)* + * ``` + */ + class RegExpQuantifier extends RegExpTerm; + + /** + * A star-quantified term. + * + * Example: + * + * ``` + * \w* + * ``` + */ + class RegExpStar extends RegExpQuantifier; + + /** + * An optional term. + * + * Example: + * + * ``` + * ;? + * ``` + */ + class RegExpOpt extends RegExpQuantifier; + + /** + * A plus-quantified term. + * + * Example: + * + * ``` + * \w+ + * ``` + */ + class RegExpPlus extends RegExpQuantifier; + + /** + * A range-quantified term + * + * Examples: + * + * ``` + * \w{2,4} + * \w{2,} + * \w{2} + * ``` + */ + class RegExpRange extends RegExpQuantifier { + /** Gets the lower bound of the range. */ + int getLowerBound(); + + /** + * Gets the upper bound of the range, if any. + * + * If there is no upper bound, any number of repetitions is allowed. + * For a term of the form `r{lo}`, both the lower and the upper bound + * are `lo`. + */ + int getUpperBound(); + } + + /** + * An escaped regular expression term, that is, a regular expression + * term starting with a backslash. + * + * Example: + * + * ``` + * \. + * \w + * ``` + */ + class RegExpEscape extends RegExpTerm; + + /** + * A character class escape in a regular expression. + * + * Examples: + * + * ``` + * \w + * \S + * ``` + */ + class RegExpCharacterClassEscape extends RegExpEscape { + /** Gets the name of the character class; for example, `w` for `\w`. */ + string getValue(); + } + + /** + * An alternative term, that is, a term of the form `a|b`. + * + * Example: + * + * ``` + * ECMA|Java + * ``` + */ + class RegExpAlt extends RegExpTerm; + + /** + * A grouped regular expression. + * + * Examples: + * + * ``` + * (ECMA|Java) + * (?:ECMA|Java) + * (?['"]) + * ``` + */ + class RegExpGroup extends RegExpTerm { + /** + * Gets the index of this capture group within the enclosing regular + * expression literal. + * + * For example, in the regular expression `/((a?).)(?:b)/`, the + * group `((a?).)` has index 1, the group `(a?)` nested inside it + * has index 2, and the group `(?:b)` has no index, since it is + * not a capture group. + */ + int getNumber(); + } + + /** + * A back reference, that is, a term of the form `\i` or `\k` + * in a regular expression. + * + * Examples: + * + * ``` + * \1 + * \k + * ``` + */ + class RegExpBackRef extends RegExpTerm { + /** Gets the capture group this back reference refers to. */ + RegExpGroup getGroup(); + } + + /** + * A sequence term. + * + * Example: + * + * ``` + * (ECMA|Java)Script + * ``` + * + * This is a sequence with the elements `(ECMA|Java)` and `Script`. + */ + class RegExpSequence extends RegExpTerm; + + /** + * A zero-width lookahead or lookbehind assertion. + * + * Examples: + * + * ``` + * (?=\w) + * (?!\n) + * (?<=\.) + * (?&] + * ``` + */ + class RegExpCharacterClass extends RegExpTerm { + /** + * Holds if this character class matches any character. + */ + predicate isUniversalClass(); + + /** Holds if this is an inverted character class, that is, a term of the form `[^...]`. */ + predicate isInverted(); + } + + /** + * A character range in a character class in a regular expression. + * + * Example: + * + * ``` + * a-z + * ``` + */ + class RegExpCharacterRange extends RegExpTerm { + /** Holds if `lo` is the lower bound of this character range and `hi` the upper bound. */ + predicate isRange(string lo, string hi); + } + + /** + * A dot regular expression. + * + * Example: + * + * ``` + * . + * ``` + */ + class RegExpDot extends RegExpTerm; + + /** + * A dollar assertion `$` matching the end of a line. + * + * Example: + * + * ``` + * $ + * ``` + */ + class RegExpDollar extends RegExpTerm; + + /** + * A caret assertion `^` matching the beginning of a line. + * + * Example: + * + * ``` + * ^ + * ``` + */ + class RegExpCaret extends RegExpTerm; + + /** + * A word boundary assertion. + * + * Example: + * + * ``` + * \b + * ``` + */ + class RegExpWordBoundary extends RegExpTerm; + + /** + * A regular expression term that permits unlimited repetitions. + */ + class InfiniteRepetitionQuantifier extends RegExpQuantifier; + + /** + * Holds if the regular expression should not be considered. + * + * For javascript we make the pragmatic performance optimization to ignore minified files. + */ + predicate isExcluded(RegExpParent parent); + + /** + * Holds if `term` is a possessive quantifier. + * As javascript's regexes do not support possessive quantifiers, this never holds, but is used by the shared library. + */ + predicate isPossessive(RegExpQuantifier term); + + /** + * Holds if the regex that `term` is part of is used in a way that ignores any leading prefix of the input it's matched against. + * Not yet implemented for JavaScript. + */ + predicate matchesAnyPrefix(RegExpTerm term); + + /** + * Holds if the regex that `term` is part of is used in a way that ignores any trailing suffix of the input it's matched against. + * Not yet implemented for JavaScript. + */ + predicate matchesAnySuffix(RegExpTerm term); + + /** + * Holds if `term` is an escape class representing e.g. `\d`. + * `clazz` is which character class it represents, e.g. "d" for `\d`. + */ + predicate isEscapeClass(RegExpTerm term, string clazz); + + /** + * Holds if `root` has the `i` flag for case-insensitive matching. + */ + predicate isIgnoreCase(RegExpTerm root); + + /** + * Holds if `root` has the `s` flag for multi-line matching. + */ + predicate isDotAll(RegExpTerm root); +} diff --git a/shared/regex/codeql/regex/nfa/BadTagFilterQuery.qll b/shared/regex/codeql/regex/nfa/BadTagFilterQuery.qll new file mode 100644 index 00000000000..c9c254fe990 --- /dev/null +++ b/shared/regex/codeql/regex/nfa/BadTagFilterQuery.qll @@ -0,0 +1,177 @@ +/** + * Provides predicates for reasoning about bad tag filter vulnerabilities. + */ + +private import NfaUtils as NfaUtils +private import RegexpMatching as RM +private import codeql.regex.RegexTreeView + +/** + * Module implementing classes and predicates reasoing about bad tag filter vulnerabilities. + */ +module Make { + private import TreeImpl + import RM::Make + + /** + * Holds if the regexp `root` should be tested against `str`. + * Implements the `isRegexpMatchingCandidateSig` signature from `RegexpMatching`. + * `ignorePrefix` toggles whether the regular expression should be treated as accepting any prefix if it's unanchored. + * `testWithGroups` toggles whether it's tested which groups are filled by a given input string. + */ + private predicate isBadTagFilterCandidate( + RootTerm root, string str, boolean ignorePrefix, boolean testWithGroups + ) { + // the regexp must mention "<" and ">" explicitly. + forall(string angleBracket | angleBracket = ["<", ">"] | + any(RegExpConstant term | term.getValue().matches("%" + angleBracket + "%")).getRootTerm() = + root + ) and + ignorePrefix = true and + ( + str = ["", "", "", "", "", + "", "", "", "", + "", "", + "", "", "", + "", "", + "", "", + "") and + regexp.matches("") and + not regexp.matches("") and + ( + not regexp.matches("") and + msg = "This regular expression matches , but not " + or + not regexp.matches("") and + msg = "This regular expression matches , but not " + ) + or + regexp.matches("") and + regexp.matches("") and + not regexp.matches("") and + not regexp.matches("") and + msg = + "This regular expression does not match script tags where the attribute uses single-quotes." + or + regexp.matches("") and + regexp.matches("") and + not regexp.matches("") and + not regexp.matches("") and + msg = + "This regular expression does not match script tags where the attribute uses double-quotes." + or + regexp.matches("") and + regexp.matches("") and + not regexp.matches("") and + not regexp.matches("") and + not regexp.matches("") and + msg = + "This regular expression does not match script tags where tabs are used between attributes." + or + regexp.matches("") and + not isIgnoreCase(regexp) and + not regexp.matches("") and + not regexp.matches("") and + ( + not regexp.matches("") and + msg = "This regular expression does not match upper case ") and + regexp.matches("") and + msg = "This regular expression does not match mixed case ") and + not regexp.matches("") and + not regexp.matches("") and + ( + not regexp.matches("") and + msg = "This regular expression does not match script end tags like ." + or + not regexp.matches("") and + msg = "This regular expression does not match script end tags like ." + or + not regexp.matches("", - "", "", "", "", - "", "", - "", "", "", - "", "", "", - "", "") and - regexp.matches("") and - not regexp.matches("") and - ( - not regexp.matches("") and - msg = "This regular expression matches , but not " - or - not regexp.matches("") and - msg = "This regular expression matches , but not " - ) - or - regexp.matches("") and - regexp.matches("") and - not regexp.matches("") and - not regexp.matches("") and - msg = "This regular expression does not match script tags where the attribute uses single-quotes." - or - regexp.matches("") and - regexp.matches("") and - not regexp.matches("") and - not regexp.matches("") and - msg = "This regular expression does not match script tags where the attribute uses double-quotes." - or - regexp.matches("") and - regexp.matches("") and - not regexp.matches("") and - not regexp.matches("") and - not regexp.matches("") and - msg = "This regular expression does not match script tags where tabs are used between attributes." - or - regexp.matches("") and - not RegExpFlags::isIgnoreCase(regexp) and - not regexp.matches("") and - not regexp.matches("") and - ( - not regexp.matches("") and - msg = "This regular expression does not match upper case ") and - regexp.matches("") and - msg = "This regular expression does not match mixed case ") and - not regexp.matches("") and - not regexp.matches("") and - ( - not regexp.matches("") and - msg = "This regular expression does not match script end tags like ." - or - not regexp.matches("") and - msg = "This regular expression does not match script end tags like ." - or - not regexp.matches("", - "", "", "", "", - "", "", - "", "", "", - "", "", "", - "", "") and - regexp.matches("") and - not regexp.matches("") and - ( - not regexp.matches("") and - msg = "This regular expression matches , but not " - or - not regexp.matches("") and - msg = "This regular expression matches , but not " - ) - or - regexp.matches("") and - regexp.matches("") and - not regexp.matches("") and - not regexp.matches("") and - msg = "This regular expression does not match script tags where the attribute uses single-quotes." - or - regexp.matches("") and - regexp.matches("") and - not regexp.matches("") and - not regexp.matches("") and - msg = "This regular expression does not match script tags where the attribute uses double-quotes." - or - regexp.matches("") and - regexp.matches("") and - not regexp.matches("") and - not regexp.matches("") and - not regexp.matches("") and - msg = "This regular expression does not match script tags where tabs are used between attributes." - or - regexp.matches("") and - not RegExpFlags::isIgnoreCase(regexp) and - not regexp.matches("") and - not regexp.matches("") and - ( - not regexp.matches("") and - msg = "This regular expression does not match upper case ") and - regexp.matches("") and - msg = "This regular expression does not match mixed case ") and - not regexp.matches("") and - not regexp.matches("") and - ( - not regexp.matches("") and - msg = "This regular expression does not match script end tags like ." - or - not regexp.matches("") and - msg = "This regular expression does not match script end tags like ." - or - not regexp.matches(" diff --git a/javascript/extractor/tests/vue/output/trap/rails.erb.trap b/javascript/extractor/tests/vue/output/trap/rails.erb.trap new file mode 100644 index 00000000000..76af8c5c8d3 --- /dev/null +++ b/javascript/extractor/tests/vue/output/trap/rails.erb.trap @@ -0,0 +1,137 @@ +#10000=@"/rails.erb;sourcefile" +files(#10000,"/rails.erb") +#10001=@"/;folder" +folders(#10001,"/") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=* +#20002=@"script;{#10000},1,9" +#20003=* +lines(#20003,#20002,""," +") +#20004=@"loc,{#10000},1,9,1,8" +locations_default(#20004,#10000,1,9,1,8) +hasLocation(#20003,#20004) +#20005=* +lines(#20005,#20002," console.log(""FOO"");"," +") +#20006=@"loc,{#10000},2,1,2,21" +locations_default(#20006,#10000,2,1,2,21) +hasLocation(#20005,#20006) +indentation(#10000,2," ",2) +numlines(#20002,2,1,0) +#20007=* +tokeninfo(#20007,6,#20002,0,"console") +#20008=@"loc,{#10000},2,3,2,9" +locations_default(#20008,#10000,2,3,2,9) +hasLocation(#20007,#20008) +#20009=* +tokeninfo(#20009,8,#20002,1,".") +#20010=@"loc,{#10000},2,10,2,10" +locations_default(#20010,#10000,2,10,2,10) +hasLocation(#20009,#20010) +#20011=* +tokeninfo(#20011,6,#20002,2,"log") +#20012=@"loc,{#10000},2,11,2,13" +locations_default(#20012,#10000,2,11,2,13) +hasLocation(#20011,#20012) +#20013=* +tokeninfo(#20013,8,#20002,3,"(") +#20014=@"loc,{#10000},2,14,2,14" +locations_default(#20014,#10000,2,14,2,14) +hasLocation(#20013,#20014) +#20015=* +tokeninfo(#20015,4,#20002,4,"""FOO""") +#20016=@"loc,{#10000},2,15,2,19" +locations_default(#20016,#10000,2,15,2,19) +hasLocation(#20015,#20016) +#20017=* +tokeninfo(#20017,8,#20002,5,")") +#20018=@"loc,{#10000},2,20,2,20" +locations_default(#20018,#10000,2,20,2,20) +hasLocation(#20017,#20018) +#20019=* +tokeninfo(#20019,8,#20002,6,";") +#20020=@"loc,{#10000},2,21,2,21" +locations_default(#20020,#10000,2,21,2,21) +hasLocation(#20019,#20020) +#20021=* +tokeninfo(#20021,0,#20002,7,"") +#20022=@"loc,{#10000},3,1,3,0" +locations_default(#20022,#10000,3,1,3,0) +hasLocation(#20021,#20022) +toplevels(#20002,1) +#20023=@"loc,{#10000},1,9,3,0" +locations_default(#20023,#10000,1,9,3,0) +hasLocation(#20002,#20023) +#20024=* +stmts(#20024,2,#20002,0,"console.log(""FOO"");") +#20025=@"loc,{#10000},2,3,2,21" +locations_default(#20025,#10000,2,3,2,21) +hasLocation(#20024,#20025) +stmt_containers(#20024,#20002) +#20026=* +exprs(#20026,13,#20024,0,"console.log(""FOO"")") +#20027=@"loc,{#10000},2,3,2,20" +locations_default(#20027,#10000,2,3,2,20) +hasLocation(#20026,#20027) +enclosing_stmt(#20026,#20024) +expr_containers(#20026,#20002) +#20028=* +exprs(#20028,14,#20026,-1,"console.log") +#20029=@"loc,{#10000},2,3,2,13" +locations_default(#20029,#10000,2,3,2,13) +hasLocation(#20028,#20029) +enclosing_stmt(#20028,#20024) +expr_containers(#20028,#20002) +#20030=* +exprs(#20030,79,#20028,0,"console") +hasLocation(#20030,#20008) +enclosing_stmt(#20030,#20024) +expr_containers(#20030,#20002) +literals("console","console",#20030) +#20031=@"var;{console};{#20000}" +variables(#20031,"console",#20000) +bind(#20030,#20031) +#20032=* +exprs(#20032,0,#20028,1,"log") +hasLocation(#20032,#20012) +enclosing_stmt(#20032,#20024) +expr_containers(#20032,#20002) +literals("log","log",#20032) +#20033=* +exprs(#20033,4,#20026,0,"""FOO""") +hasLocation(#20033,#20016) +enclosing_stmt(#20033,#20024) +expr_containers(#20033,#20002) +literals("FOO","""FOO""",#20033) +#20034=* +regexpterm(#20034,14,#20033,0,"FOO") +#20035=@"loc,{#10000},2,16,2,18" +locations_default(#20035,#10000,2,16,2,18) +hasLocation(#20034,#20035) +regexp_const_value(#20034,"FOO") +#20036=* +entry_cfg_node(#20036,#20002) +hasLocation(#20036,#20004) +#20037=* +exit_cfg_node(#20037,#20002) +hasLocation(#20037,#20022) +successor(#20024,#20030) +successor(#20033,#20026) +successor(#20032,#20028) +successor(#20030,#20032) +successor(#20028,#20033) +successor(#20026,#20037) +successor(#20036,#20024) +toplevel_parent_xml_node(#20002,#20001) +xmlElements(#20001,"script",#10000,0,#10000) +#20038=@"loc,{#10000},1,1,3,9" +locations_default(#20038,#10000,1,1,3,9) +xmllocations(#20001,#20038) +numlines(#10000,3,1,0) +filetype(#10000,"html") From 5940f17b8394c6a641740080cb37a6f1d444f0e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Wed, 9 Nov 2022 13:10:08 +0100 Subject: [PATCH 0131/1420] Swift: Docs + doctests --- .../Security/CWE-094/UnsafeJsEval.qhelp | 32 ++++ .../Security/CWE-094/UnsafeJsEvalBad.swift | 6 + .../Security/CWE-094/UnsafeJsEvalGood.swift | 10 + .../Security/CWE-094/UnsafeJsEval.expected | 176 +++++++++--------- .../Security/CWE-094/UnsafeJsEval.swift | 20 +- 5 files changed, 152 insertions(+), 92 deletions(-) create mode 100644 swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.qhelp create mode 100644 swift/ql/src/queries/Security/CWE-094/UnsafeJsEvalBad.swift create mode 100644 swift/ql/src/queries/Security/CWE-094/UnsafeJsEvalGood.swift diff --git a/swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.qhelp b/swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.qhelp new file mode 100644 index 00000000000..26eae7ff91b --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.qhelp @@ -0,0 +1,32 @@ + + + +

    Evaluating JavaScript that contains a substring from a remote origin may lead to remote code execution. Code under control of an attacker can execute arbitrary unauthorized actions, including exfiltration of local data by sending it to a third party web service.

    + +
    + + +

    When loading JavaScript into a web view, evaluate only known, locally-defined source code. If a part of the input does come from a remote source, instead of injecting it into the JavaScript code to be evaluated, prefer sending it as data to the web view using an API such as WKWebView.callAsyncJavaScript with the arguments dictionary to pass remote data objects.

    + +
    + + +

    In the following example, a call to WKWebView.evaluateJavaScript evaluates JavaScript source code that is tainted with remote data, potentially introducing a code injection vulnerability.

    + + + +

    To fix the problem, we sanitize the remote data by passing it using the arguments dictionary of WKWebView.callAsyncJavaScript. This ensures that untrusted data cannot be evaluated as JavaScript source code.

    + + + +
    + + +
  • + Apple Developer Documentation - WKWebView.callAsyncJavaScript(_:arguments:in:contentWorld:) +
  • + +
    +
    diff --git a/swift/ql/src/queries/Security/CWE-094/UnsafeJsEvalBad.swift b/swift/ql/src/queries/Security/CWE-094/UnsafeJsEvalBad.swift new file mode 100644 index 00000000000..2e5b0233e79 --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-094/UnsafeJsEvalBad.swift @@ -0,0 +1,6 @@ +let webview: WKWebView +let remoteData = try String(contentsOf: URL(string: "http://example.com/evil.json")!) + +... + +_ = try await webview.evaluateJavaScript("alert(" + remoteData + ")") // BAD diff --git a/swift/ql/src/queries/Security/CWE-094/UnsafeJsEvalGood.swift b/swift/ql/src/queries/Security/CWE-094/UnsafeJsEvalGood.swift new file mode 100644 index 00000000000..a51ffd60b63 --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-094/UnsafeJsEvalGood.swift @@ -0,0 +1,10 @@ +let webview: WKWebView +let remoteData = try String(contentsOf: URL(string: "http://example.com/evil.json")!) + +... + +_ = try await webview.callAsyncJavaScript( + "alert(JSON.parse(data))", + arguments: ["data": remoteData], // GOOD + contentWorld: .page +) diff --git a/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.expected b/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.expected index c9c794bbbb1..05881a11eb5 100644 --- a/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.expected +++ b/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.expected @@ -1,92 +1,92 @@ edges -| UnsafeJsEval.swift:123:21:123:42 | string : | UnsafeJsEval.swift:123:70:123:70 | string : | -| UnsafeJsEval.swift:164:10:164:37 | try ... : | UnsafeJsEval.swift:200:21:200:35 | call to getRemoteData() : | -| UnsafeJsEval.swift:164:10:164:37 | try ... : | UnsafeJsEval.swift:203:7:203:21 | call to getRemoteData() : | -| UnsafeJsEval.swift:164:14:164:37 | call to init(contentsOf:) : | UnsafeJsEval.swift:164:10:164:37 | try ... : | -| UnsafeJsEval.swift:200:21:200:35 | call to getRemoteData() : | UnsafeJsEval.swift:204:7:204:7 | remoteString : | -| UnsafeJsEval.swift:200:21:200:35 | call to getRemoteData() : | UnsafeJsEval.swift:207:7:207:39 | ... .+(_:_:) ... : | -| UnsafeJsEval.swift:200:21:200:35 | call to getRemoteData() : | UnsafeJsEval.swift:213:7:213:49 | call to init(decoding:as:) : | -| UnsafeJsEval.swift:203:7:203:21 | call to getRemoteData() : | UnsafeJsEval.swift:264:13:264:13 | string : | -| UnsafeJsEval.swift:203:7:203:21 | call to getRemoteData() : | UnsafeJsEval.swift:267:13:267:13 | string : | -| UnsafeJsEval.swift:203:7:203:21 | call to getRemoteData() : | UnsafeJsEval.swift:275:13:275:13 | string : | -| UnsafeJsEval.swift:203:7:203:21 | call to getRemoteData() : | UnsafeJsEval.swift:278:13:278:13 | string : | -| UnsafeJsEval.swift:203:7:203:21 | call to getRemoteData() : | UnsafeJsEval.swift:284:13:284:13 | string : | -| UnsafeJsEval.swift:203:7:203:21 | call to getRemoteData() : | UnsafeJsEval.swift:298:13:298:13 | string : | -| UnsafeJsEval.swift:204:7:204:7 | remoteString : | UnsafeJsEval.swift:264:13:264:13 | string : | -| UnsafeJsEval.swift:204:7:204:7 | remoteString : | UnsafeJsEval.swift:267:13:267:13 | string : | -| UnsafeJsEval.swift:204:7:204:7 | remoteString : | UnsafeJsEval.swift:275:13:275:13 | string : | -| UnsafeJsEval.swift:204:7:204:7 | remoteString : | UnsafeJsEval.swift:278:13:278:13 | string : | -| UnsafeJsEval.swift:204:7:204:7 | remoteString : | UnsafeJsEval.swift:284:13:284:13 | string : | -| UnsafeJsEval.swift:204:7:204:7 | remoteString : | UnsafeJsEval.swift:298:13:298:13 | string : | -| UnsafeJsEval.swift:207:7:207:39 | ... .+(_:_:) ... : | UnsafeJsEval.swift:264:13:264:13 | string : | -| UnsafeJsEval.swift:207:7:207:39 | ... .+(_:_:) ... : | UnsafeJsEval.swift:267:13:267:13 | string : | -| UnsafeJsEval.swift:207:7:207:39 | ... .+(_:_:) ... : | UnsafeJsEval.swift:275:13:275:13 | string : | -| UnsafeJsEval.swift:207:7:207:39 | ... .+(_:_:) ... : | UnsafeJsEval.swift:278:13:278:13 | string : | -| UnsafeJsEval.swift:207:7:207:39 | ... .+(_:_:) ... : | UnsafeJsEval.swift:284:13:284:13 | string : | -| UnsafeJsEval.swift:207:7:207:39 | ... .+(_:_:) ... : | UnsafeJsEval.swift:298:13:298:13 | string : | -| UnsafeJsEval.swift:213:7:213:49 | call to init(decoding:as:) : | UnsafeJsEval.swift:264:13:264:13 | string : | -| UnsafeJsEval.swift:213:7:213:49 | call to init(decoding:as:) : | UnsafeJsEval.swift:267:13:267:13 | string : | -| UnsafeJsEval.swift:213:7:213:49 | call to init(decoding:as:) : | UnsafeJsEval.swift:275:13:275:13 | string : | -| UnsafeJsEval.swift:213:7:213:49 | call to init(decoding:as:) : | UnsafeJsEval.swift:278:13:278:13 | string : | -| UnsafeJsEval.swift:213:7:213:49 | call to init(decoding:as:) : | UnsafeJsEval.swift:284:13:284:13 | string : | -| UnsafeJsEval.swift:213:7:213:49 | call to init(decoding:as:) : | UnsafeJsEval.swift:298:13:298:13 | string : | -| UnsafeJsEval.swift:264:13:264:13 | string : | UnsafeJsEval.swift:265:22:265:107 | call to init(source:injectionTime:forMainFrameOnly:) | -| UnsafeJsEval.swift:267:13:267:13 | string : | UnsafeJsEval.swift:268:22:268:124 | call to init(source:injectionTime:forMainFrameOnly:in:) | -| UnsafeJsEval.swift:275:13:275:13 | string : | UnsafeJsEval.swift:276:26:276:26 | string | -| UnsafeJsEval.swift:278:13:278:13 | string : | UnsafeJsEval.swift:279:26:279:26 | string | -| UnsafeJsEval.swift:284:13:284:13 | string : | UnsafeJsEval.swift:285:3:285:10 | .utf16 : | -| UnsafeJsEval.swift:285:3:285:10 | .utf16 : | UnsafeJsEval.swift:285:51:285:51 | stringBytes : | -| UnsafeJsEval.swift:285:51:285:51 | stringBytes : | UnsafeJsEval.swift:286:31:286:97 | call to JSStringCreateWithCharacters(_:_:) : | -| UnsafeJsEval.swift:285:51:285:51 | stringBytes : | UnsafeJsEval.swift:290:17:290:17 | jsstr | -| UnsafeJsEval.swift:286:16:286:98 | call to JSStringRetain(_:) : | UnsafeJsEval.swift:290:17:290:17 | jsstr | -| UnsafeJsEval.swift:286:31:286:97 | call to JSStringCreateWithCharacters(_:_:) : | UnsafeJsEval.swift:123:21:123:42 | string : | -| UnsafeJsEval.swift:286:31:286:97 | call to JSStringCreateWithCharacters(_:_:) : | UnsafeJsEval.swift:286:16:286:98 | call to JSStringRetain(_:) : | -| UnsafeJsEval.swift:286:31:286:97 | call to JSStringCreateWithCharacters(_:_:) : | UnsafeJsEval.swift:290:17:290:17 | jsstr | -| UnsafeJsEval.swift:298:13:298:13 | string : | UnsafeJsEval.swift:299:3:299:10 | .utf8CString : | -| UnsafeJsEval.swift:299:3:299:10 | .utf8CString : | UnsafeJsEval.swift:299:48:299:48 | stringBytes : | -| UnsafeJsEval.swift:299:48:299:48 | stringBytes : | UnsafeJsEval.swift:300:31:300:84 | call to JSStringCreateWithUTF8CString(_:) : | -| UnsafeJsEval.swift:299:48:299:48 | stringBytes : | UnsafeJsEval.swift:304:17:304:17 | jsstr | -| UnsafeJsEval.swift:300:16:300:85 | call to JSStringRetain(_:) : | UnsafeJsEval.swift:304:17:304:17 | jsstr | -| UnsafeJsEval.swift:300:31:300:84 | call to JSStringCreateWithUTF8CString(_:) : | UnsafeJsEval.swift:123:21:123:42 | string : | -| UnsafeJsEval.swift:300:31:300:84 | call to JSStringCreateWithUTF8CString(_:) : | UnsafeJsEval.swift:300:16:300:85 | call to JSStringRetain(_:) : | -| UnsafeJsEval.swift:300:31:300:84 | call to JSStringCreateWithUTF8CString(_:) : | UnsafeJsEval.swift:304:17:304:17 | jsstr | +| UnsafeJsEval.swift:124:21:124:42 | string : | UnsafeJsEval.swift:124:70:124:70 | string : | +| UnsafeJsEval.swift:165:10:165:37 | try ... : | UnsafeJsEval.swift:201:21:201:35 | call to getRemoteData() : | +| UnsafeJsEval.swift:165:10:165:37 | try ... : | UnsafeJsEval.swift:204:7:204:21 | call to getRemoteData() : | +| UnsafeJsEval.swift:165:14:165:37 | call to init(contentsOf:) : | UnsafeJsEval.swift:165:10:165:37 | try ... : | +| UnsafeJsEval.swift:201:21:201:35 | call to getRemoteData() : | UnsafeJsEval.swift:205:7:205:7 | remoteString : | +| UnsafeJsEval.swift:201:21:201:35 | call to getRemoteData() : | UnsafeJsEval.swift:208:7:208:39 | ... .+(_:_:) ... : | +| UnsafeJsEval.swift:201:21:201:35 | call to getRemoteData() : | UnsafeJsEval.swift:214:7:214:49 | call to init(decoding:as:) : | +| UnsafeJsEval.swift:204:7:204:21 | call to getRemoteData() : | UnsafeJsEval.swift:265:13:265:13 | string : | +| UnsafeJsEval.swift:204:7:204:21 | call to getRemoteData() : | UnsafeJsEval.swift:268:13:268:13 | string : | +| UnsafeJsEval.swift:204:7:204:21 | call to getRemoteData() : | UnsafeJsEval.swift:276:13:276:13 | string : | +| UnsafeJsEval.swift:204:7:204:21 | call to getRemoteData() : | UnsafeJsEval.swift:279:13:279:13 | string : | +| UnsafeJsEval.swift:204:7:204:21 | call to getRemoteData() : | UnsafeJsEval.swift:285:13:285:13 | string : | +| UnsafeJsEval.swift:204:7:204:21 | call to getRemoteData() : | UnsafeJsEval.swift:299:13:299:13 | string : | +| UnsafeJsEval.swift:205:7:205:7 | remoteString : | UnsafeJsEval.swift:265:13:265:13 | string : | +| UnsafeJsEval.swift:205:7:205:7 | remoteString : | UnsafeJsEval.swift:268:13:268:13 | string : | +| UnsafeJsEval.swift:205:7:205:7 | remoteString : | UnsafeJsEval.swift:276:13:276:13 | string : | +| UnsafeJsEval.swift:205:7:205:7 | remoteString : | UnsafeJsEval.swift:279:13:279:13 | string : | +| UnsafeJsEval.swift:205:7:205:7 | remoteString : | UnsafeJsEval.swift:285:13:285:13 | string : | +| UnsafeJsEval.swift:205:7:205:7 | remoteString : | UnsafeJsEval.swift:299:13:299:13 | string : | +| UnsafeJsEval.swift:208:7:208:39 | ... .+(_:_:) ... : | UnsafeJsEval.swift:265:13:265:13 | string : | +| UnsafeJsEval.swift:208:7:208:39 | ... .+(_:_:) ... : | UnsafeJsEval.swift:268:13:268:13 | string : | +| UnsafeJsEval.swift:208:7:208:39 | ... .+(_:_:) ... : | UnsafeJsEval.swift:276:13:276:13 | string : | +| UnsafeJsEval.swift:208:7:208:39 | ... .+(_:_:) ... : | UnsafeJsEval.swift:279:13:279:13 | string : | +| UnsafeJsEval.swift:208:7:208:39 | ... .+(_:_:) ... : | UnsafeJsEval.swift:285:13:285:13 | string : | +| UnsafeJsEval.swift:208:7:208:39 | ... .+(_:_:) ... : | UnsafeJsEval.swift:299:13:299:13 | string : | +| UnsafeJsEval.swift:214:7:214:49 | call to init(decoding:as:) : | UnsafeJsEval.swift:265:13:265:13 | string : | +| UnsafeJsEval.swift:214:7:214:49 | call to init(decoding:as:) : | UnsafeJsEval.swift:268:13:268:13 | string : | +| UnsafeJsEval.swift:214:7:214:49 | call to init(decoding:as:) : | UnsafeJsEval.swift:276:13:276:13 | string : | +| UnsafeJsEval.swift:214:7:214:49 | call to init(decoding:as:) : | UnsafeJsEval.swift:279:13:279:13 | string : | +| UnsafeJsEval.swift:214:7:214:49 | call to init(decoding:as:) : | UnsafeJsEval.swift:285:13:285:13 | string : | +| UnsafeJsEval.swift:214:7:214:49 | call to init(decoding:as:) : | UnsafeJsEval.swift:299:13:299:13 | string : | +| UnsafeJsEval.swift:265:13:265:13 | string : | UnsafeJsEval.swift:266:22:266:107 | call to init(source:injectionTime:forMainFrameOnly:) | +| UnsafeJsEval.swift:268:13:268:13 | string : | UnsafeJsEval.swift:269:22:269:124 | call to init(source:injectionTime:forMainFrameOnly:in:) | +| UnsafeJsEval.swift:276:13:276:13 | string : | UnsafeJsEval.swift:277:26:277:26 | string | +| UnsafeJsEval.swift:279:13:279:13 | string : | UnsafeJsEval.swift:280:26:280:26 | string | +| UnsafeJsEval.swift:285:13:285:13 | string : | UnsafeJsEval.swift:286:3:286:10 | .utf16 : | +| UnsafeJsEval.swift:286:3:286:10 | .utf16 : | UnsafeJsEval.swift:286:51:286:51 | stringBytes : | +| UnsafeJsEval.swift:286:51:286:51 | stringBytes : | UnsafeJsEval.swift:287:31:287:97 | call to JSStringCreateWithCharacters(_:_:) : | +| UnsafeJsEval.swift:286:51:286:51 | stringBytes : | UnsafeJsEval.swift:291:17:291:17 | jsstr | +| UnsafeJsEval.swift:287:16:287:98 | call to JSStringRetain(_:) : | UnsafeJsEval.swift:291:17:291:17 | jsstr | +| UnsafeJsEval.swift:287:31:287:97 | call to JSStringCreateWithCharacters(_:_:) : | UnsafeJsEval.swift:124:21:124:42 | string : | +| UnsafeJsEval.swift:287:31:287:97 | call to JSStringCreateWithCharacters(_:_:) : | UnsafeJsEval.swift:287:16:287:98 | call to JSStringRetain(_:) : | +| UnsafeJsEval.swift:287:31:287:97 | call to JSStringCreateWithCharacters(_:_:) : | UnsafeJsEval.swift:291:17:291:17 | jsstr | +| UnsafeJsEval.swift:299:13:299:13 | string : | UnsafeJsEval.swift:300:3:300:10 | .utf8CString : | +| UnsafeJsEval.swift:300:3:300:10 | .utf8CString : | UnsafeJsEval.swift:300:48:300:48 | stringBytes : | +| UnsafeJsEval.swift:300:48:300:48 | stringBytes : | UnsafeJsEval.swift:301:31:301:84 | call to JSStringCreateWithUTF8CString(_:) : | +| UnsafeJsEval.swift:300:48:300:48 | stringBytes : | UnsafeJsEval.swift:305:17:305:17 | jsstr | +| UnsafeJsEval.swift:301:16:301:85 | call to JSStringRetain(_:) : | UnsafeJsEval.swift:305:17:305:17 | jsstr | +| UnsafeJsEval.swift:301:31:301:84 | call to JSStringCreateWithUTF8CString(_:) : | UnsafeJsEval.swift:124:21:124:42 | string : | +| UnsafeJsEval.swift:301:31:301:84 | call to JSStringCreateWithUTF8CString(_:) : | UnsafeJsEval.swift:301:16:301:85 | call to JSStringRetain(_:) : | +| UnsafeJsEval.swift:301:31:301:84 | call to JSStringCreateWithUTF8CString(_:) : | UnsafeJsEval.swift:305:17:305:17 | jsstr | nodes -| UnsafeJsEval.swift:123:21:123:42 | string : | semmle.label | string : | -| UnsafeJsEval.swift:123:70:123:70 | string : | semmle.label | string : | -| UnsafeJsEval.swift:164:10:164:37 | try ... : | semmle.label | try ... : | -| UnsafeJsEval.swift:164:14:164:37 | call to init(contentsOf:) : | semmle.label | call to init(contentsOf:) : | -| UnsafeJsEval.swift:200:21:200:35 | call to getRemoteData() : | semmle.label | call to getRemoteData() : | -| UnsafeJsEval.swift:203:7:203:21 | call to getRemoteData() : | semmle.label | call to getRemoteData() : | -| UnsafeJsEval.swift:204:7:204:7 | remoteString : | semmle.label | remoteString : | -| UnsafeJsEval.swift:207:7:207:39 | ... .+(_:_:) ... : | semmle.label | ... .+(_:_:) ... : | -| UnsafeJsEval.swift:213:7:213:49 | call to init(decoding:as:) : | semmle.label | call to init(decoding:as:) : | -| UnsafeJsEval.swift:264:13:264:13 | string : | semmle.label | string : | -| UnsafeJsEval.swift:265:22:265:107 | call to init(source:injectionTime:forMainFrameOnly:) | semmle.label | call to init(source:injectionTime:forMainFrameOnly:) | -| UnsafeJsEval.swift:267:13:267:13 | string : | semmle.label | string : | -| UnsafeJsEval.swift:268:22:268:124 | call to init(source:injectionTime:forMainFrameOnly:in:) | semmle.label | call to init(source:injectionTime:forMainFrameOnly:in:) | -| UnsafeJsEval.swift:275:13:275:13 | string : | semmle.label | string : | -| UnsafeJsEval.swift:276:26:276:26 | string | semmle.label | string | -| UnsafeJsEval.swift:278:13:278:13 | string : | semmle.label | string : | -| UnsafeJsEval.swift:279:26:279:26 | string | semmle.label | string | -| UnsafeJsEval.swift:284:13:284:13 | string : | semmle.label | string : | -| UnsafeJsEval.swift:285:3:285:10 | .utf16 : | semmle.label | .utf16 : | -| UnsafeJsEval.swift:285:51:285:51 | stringBytes : | semmle.label | stringBytes : | -| UnsafeJsEval.swift:286:16:286:98 | call to JSStringRetain(_:) : | semmle.label | call to JSStringRetain(_:) : | -| UnsafeJsEval.swift:286:31:286:97 | call to JSStringCreateWithCharacters(_:_:) : | semmle.label | call to JSStringCreateWithCharacters(_:_:) : | -| UnsafeJsEval.swift:290:17:290:17 | jsstr | semmle.label | jsstr | -| UnsafeJsEval.swift:298:13:298:13 | string : | semmle.label | string : | -| UnsafeJsEval.swift:299:3:299:10 | .utf8CString : | semmle.label | .utf8CString : | -| UnsafeJsEval.swift:299:48:299:48 | stringBytes : | semmle.label | stringBytes : | -| UnsafeJsEval.swift:300:16:300:85 | call to JSStringRetain(_:) : | semmle.label | call to JSStringRetain(_:) : | -| UnsafeJsEval.swift:300:31:300:84 | call to JSStringCreateWithUTF8CString(_:) : | semmle.label | call to JSStringCreateWithUTF8CString(_:) : | -| UnsafeJsEval.swift:304:17:304:17 | jsstr | semmle.label | jsstr | +| UnsafeJsEval.swift:124:21:124:42 | string : | semmle.label | string : | +| UnsafeJsEval.swift:124:70:124:70 | string : | semmle.label | string : | +| UnsafeJsEval.swift:165:10:165:37 | try ... : | semmle.label | try ... : | +| UnsafeJsEval.swift:165:14:165:37 | call to init(contentsOf:) : | semmle.label | call to init(contentsOf:) : | +| UnsafeJsEval.swift:201:21:201:35 | call to getRemoteData() : | semmle.label | call to getRemoteData() : | +| UnsafeJsEval.swift:204:7:204:21 | call to getRemoteData() : | semmle.label | call to getRemoteData() : | +| UnsafeJsEval.swift:205:7:205:7 | remoteString : | semmle.label | remoteString : | +| UnsafeJsEval.swift:208:7:208:39 | ... .+(_:_:) ... : | semmle.label | ... .+(_:_:) ... : | +| UnsafeJsEval.swift:214:7:214:49 | call to init(decoding:as:) : | semmle.label | call to init(decoding:as:) : | +| UnsafeJsEval.swift:265:13:265:13 | string : | semmle.label | string : | +| UnsafeJsEval.swift:266:22:266:107 | call to init(source:injectionTime:forMainFrameOnly:) | semmle.label | call to init(source:injectionTime:forMainFrameOnly:) | +| UnsafeJsEval.swift:268:13:268:13 | string : | semmle.label | string : | +| UnsafeJsEval.swift:269:22:269:124 | call to init(source:injectionTime:forMainFrameOnly:in:) | semmle.label | call to init(source:injectionTime:forMainFrameOnly:in:) | +| UnsafeJsEval.swift:276:13:276:13 | string : | semmle.label | string : | +| UnsafeJsEval.swift:277:26:277:26 | string | semmle.label | string | +| UnsafeJsEval.swift:279:13:279:13 | string : | semmle.label | string : | +| UnsafeJsEval.swift:280:26:280:26 | string | semmle.label | string | +| UnsafeJsEval.swift:285:13:285:13 | string : | semmle.label | string : | +| UnsafeJsEval.swift:286:3:286:10 | .utf16 : | semmle.label | .utf16 : | +| UnsafeJsEval.swift:286:51:286:51 | stringBytes : | semmle.label | stringBytes : | +| UnsafeJsEval.swift:287:16:287:98 | call to JSStringRetain(_:) : | semmle.label | call to JSStringRetain(_:) : | +| UnsafeJsEval.swift:287:31:287:97 | call to JSStringCreateWithCharacters(_:_:) : | semmle.label | call to JSStringCreateWithCharacters(_:_:) : | +| UnsafeJsEval.swift:291:17:291:17 | jsstr | semmle.label | jsstr | +| UnsafeJsEval.swift:299:13:299:13 | string : | semmle.label | string : | +| UnsafeJsEval.swift:300:3:300:10 | .utf8CString : | semmle.label | .utf8CString : | +| UnsafeJsEval.swift:300:48:300:48 | stringBytes : | semmle.label | stringBytes : | +| UnsafeJsEval.swift:301:16:301:85 | call to JSStringRetain(_:) : | semmle.label | call to JSStringRetain(_:) : | +| UnsafeJsEval.swift:301:31:301:84 | call to JSStringCreateWithUTF8CString(_:) : | semmle.label | call to JSStringCreateWithUTF8CString(_:) : | +| UnsafeJsEval.swift:305:17:305:17 | jsstr | semmle.label | jsstr | subpaths -| UnsafeJsEval.swift:286:31:286:97 | call to JSStringCreateWithCharacters(_:_:) : | UnsafeJsEval.swift:123:21:123:42 | string : | UnsafeJsEval.swift:123:70:123:70 | string : | UnsafeJsEval.swift:286:16:286:98 | call to JSStringRetain(_:) : | -| UnsafeJsEval.swift:300:31:300:84 | call to JSStringCreateWithUTF8CString(_:) : | UnsafeJsEval.swift:123:21:123:42 | string : | UnsafeJsEval.swift:123:70:123:70 | string : | UnsafeJsEval.swift:300:16:300:85 | call to JSStringRetain(_:) : | +| UnsafeJsEval.swift:287:31:287:97 | call to JSStringCreateWithCharacters(_:_:) : | UnsafeJsEval.swift:124:21:124:42 | string : | UnsafeJsEval.swift:124:70:124:70 | string : | UnsafeJsEval.swift:287:16:287:98 | call to JSStringRetain(_:) : | +| UnsafeJsEval.swift:301:31:301:84 | call to JSStringCreateWithUTF8CString(_:) : | UnsafeJsEval.swift:124:21:124:42 | string : | UnsafeJsEval.swift:124:70:124:70 | string : | UnsafeJsEval.swift:301:16:301:85 | call to JSStringRetain(_:) : | #select -| UnsafeJsEval.swift:265:22:265:107 | call to init(source:injectionTime:forMainFrameOnly:) | UnsafeJsEval.swift:164:14:164:37 | call to init(contentsOf:) : | UnsafeJsEval.swift:265:22:265:107 | call to init(source:injectionTime:forMainFrameOnly:) | Evaluation of uncontrolled JavaScript from a remote source. | -| UnsafeJsEval.swift:268:22:268:124 | call to init(source:injectionTime:forMainFrameOnly:in:) | UnsafeJsEval.swift:164:14:164:37 | call to init(contentsOf:) : | UnsafeJsEval.swift:268:22:268:124 | call to init(source:injectionTime:forMainFrameOnly:in:) | Evaluation of uncontrolled JavaScript from a remote source. | -| UnsafeJsEval.swift:276:26:276:26 | string | UnsafeJsEval.swift:164:14:164:37 | call to init(contentsOf:) : | UnsafeJsEval.swift:276:26:276:26 | string | Evaluation of uncontrolled JavaScript from a remote source. | -| UnsafeJsEval.swift:279:26:279:26 | string | UnsafeJsEval.swift:164:14:164:37 | call to init(contentsOf:) : | UnsafeJsEval.swift:279:26:279:26 | string | Evaluation of uncontrolled JavaScript from a remote source. | -| UnsafeJsEval.swift:290:17:290:17 | jsstr | UnsafeJsEval.swift:164:14:164:37 | call to init(contentsOf:) : | UnsafeJsEval.swift:290:17:290:17 | jsstr | Evaluation of uncontrolled JavaScript from a remote source. | -| UnsafeJsEval.swift:304:17:304:17 | jsstr | UnsafeJsEval.swift:164:14:164:37 | call to init(contentsOf:) : | UnsafeJsEval.swift:304:17:304:17 | jsstr | Evaluation of uncontrolled JavaScript from a remote source. | +| UnsafeJsEval.swift:266:22:266:107 | call to init(source:injectionTime:forMainFrameOnly:) | UnsafeJsEval.swift:165:14:165:37 | call to init(contentsOf:) : | UnsafeJsEval.swift:266:22:266:107 | call to init(source:injectionTime:forMainFrameOnly:) | Evaluation of uncontrolled JavaScript from a remote source. | +| UnsafeJsEval.swift:269:22:269:124 | call to init(source:injectionTime:forMainFrameOnly:in:) | UnsafeJsEval.swift:165:14:165:37 | call to init(contentsOf:) : | UnsafeJsEval.swift:269:22:269:124 | call to init(source:injectionTime:forMainFrameOnly:in:) | Evaluation of uncontrolled JavaScript from a remote source. | +| UnsafeJsEval.swift:277:26:277:26 | string | UnsafeJsEval.swift:165:14:165:37 | call to init(contentsOf:) : | UnsafeJsEval.swift:277:26:277:26 | string | Evaluation of uncontrolled JavaScript from a remote source. | +| UnsafeJsEval.swift:280:26:280:26 | string | UnsafeJsEval.swift:165:14:165:37 | call to init(contentsOf:) : | UnsafeJsEval.swift:280:26:280:26 | string | Evaluation of uncontrolled JavaScript from a remote source. | +| UnsafeJsEval.swift:291:17:291:17 | jsstr | UnsafeJsEval.swift:165:14:165:37 | call to init(contentsOf:) : | UnsafeJsEval.swift:291:17:291:17 | jsstr | Evaluation of uncontrolled JavaScript from a remote source. | +| UnsafeJsEval.swift:305:17:305:17 | jsstr | UnsafeJsEval.swift:165:14:165:37 | call to init(contentsOf:) : | UnsafeJsEval.swift:305:17:305:17 | jsstr | Evaluation of uncontrolled JavaScript from a remote source. | diff --git a/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.swift b/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.swift index 98f34e9826f..36cee943b66 100644 --- a/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.swift +++ b/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.swift @@ -12,6 +12,7 @@ class NSView : NSResponder {} class WKFrameInfo : NSObject {} class WKContentWorld : NSObject { class var defaultClient: WKContentWorld { WKContentWorld() } + class var page: WKContentWorld { WKContentWorld() } } class WKWebView : UIView { @@ -174,17 +175,17 @@ func testAsync(_ sink: @escaping (String) async throws -> ()) { let remoteString = getRemoteData() try! await sink(localString) // GOOD: the HTML data is local - try! await sink(getRemoteData()) // BAD: HTML contains remote input, may access local secrets - try! await sink(remoteString) // BAD + try! await sink(getRemoteData()) // BAD [NOT DETECTED - TODO: extract Callables of @MainActor method calls]: HTML contains remote input, may access local secrets + try! await sink(remoteString) // BAD [NOT DETECTED - TODO: extract Callables of @MainActor method calls] try! await sink("console.log(" + localStringFragment + ")") // GOOD: the HTML data is local - try! await sink("console.log(" + remoteString + ")") // BAD + try! await sink("console.log(" + remoteString + ")") // BAD [NOT DETECTED - TODO: extract Callables of @MainActor method calls] let localData = Data(localString.utf8) let remoteData = Data(remoteString.utf8) try! await sink(String(decoding: localData, as: UTF8.self)) // GOOD: the data is local - try! await sink(String(decoding: remoteData, as: UTF8.self)) // BAD: the data is remote + try! await sink(String(decoding: remoteData, as: UTF8.self)) // BAD [NOT DETECTED - TODO: extract Callables of @MainActor method calls]: the data is remote try! await sink("console.log(" + String(Int(localStringFragment) ?? 0) + ")") // GOOD: Primitive conversion try! await sink("console.log(" + String(Int(remoteString) ?? 0) + ")") // GOOD: Primitive conversion @@ -312,7 +313,18 @@ func testJSEvaluateScript() { } func testQHelpExamples() { + Task { + let webview = WKWebView() + let remoteData = try String(contentsOf: URL(string: "http://example.com/evil.json")!) + _ = try await webview.evaluateJavaScript("alert(" + remoteData + ")") // BAD [NOT DETECTED - TODO: extract Callables of @MainActor method calls] + + _ = try await webview.callAsyncJavaScript( + "alert(JSON.parse(data))", + arguments: ["data": remoteData], // GOOD + contentWorld: .page + ) + } } testUIWebView() From ade83b3cfe8f0e9689cf42ac86f2dd1160a454bc Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 9 Nov 2022 12:52:33 +0100 Subject: [PATCH 0132/1420] Dataflow: Introduce support for src/sink grouping in path results. --- .../java/dataflow/internal/DataFlowImpl.qll | 114 +++++++++++++++++- 1 file changed, 111 insertions(+), 3 deletions(-) diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration From b3b77111494f43ad6da643aafaf5ebbcdaa3870d Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 9 Nov 2022 14:23:15 +0100 Subject: [PATCH 0133/1420] Dataflow: Sync. --- .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 114 +++++++++++++++++- .../ir/dataflow/internal/DataFlowImpl2.qll | 114 +++++++++++++++++- .../ir/dataflow/internal/DataFlowImpl3.qll | 114 +++++++++++++++++- .../ir/dataflow/internal/DataFlowImpl4.qll | 114 +++++++++++++++++- .../cpp/dataflow/internal/DataFlowImpl.qll | 114 +++++++++++++++++- .../cpp/dataflow/internal/DataFlowImpl2.qll | 114 +++++++++++++++++- .../cpp/dataflow/internal/DataFlowImpl3.qll | 114 +++++++++++++++++- .../cpp/dataflow/internal/DataFlowImpl4.qll | 114 +++++++++++++++++- .../dataflow/internal/DataFlowImplLocal.qll | 114 +++++++++++++++++- .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 114 +++++++++++++++++- .../ir/dataflow/internal/DataFlowImpl2.qll | 114 +++++++++++++++++- .../ir/dataflow/internal/DataFlowImpl3.qll | 114 +++++++++++++++++- .../ir/dataflow/internal/DataFlowImpl4.qll | 114 +++++++++++++++++- .../csharp/dataflow/internal/DataFlowImpl.qll | 114 +++++++++++++++++- .../dataflow/internal/DataFlowImpl2.qll | 114 +++++++++++++++++- .../dataflow/internal/DataFlowImpl3.qll | 114 +++++++++++++++++- .../dataflow/internal/DataFlowImpl4.qll | 114 +++++++++++++++++- .../dataflow/internal/DataFlowImpl5.qll | 114 +++++++++++++++++- .../DataFlowImplForContentDataFlow.qll | 114 +++++++++++++++++- .../java/dataflow/internal/DataFlowImpl2.qll | 114 +++++++++++++++++- .../java/dataflow/internal/DataFlowImpl3.qll | 114 +++++++++++++++++- .../java/dataflow/internal/DataFlowImpl4.qll | 114 +++++++++++++++++- .../java/dataflow/internal/DataFlowImpl5.qll | 114 +++++++++++++++++- .../java/dataflow/internal/DataFlowImpl6.qll | 114 +++++++++++++++++- .../DataFlowImplForOnActivityResult.qll | 114 +++++++++++++++++- .../DataFlowImplForSerializability.qll | 114 +++++++++++++++++- .../dataflow/new/internal/DataFlowImpl.qll | 114 +++++++++++++++++- .../dataflow/new/internal/DataFlowImpl2.qll | 114 +++++++++++++++++- .../dataflow/new/internal/DataFlowImpl3.qll | 114 +++++++++++++++++- .../dataflow/new/internal/DataFlowImpl4.qll | 114 +++++++++++++++++- .../ruby/dataflow/internal/DataFlowImpl.qll | 114 +++++++++++++++++- .../ruby/dataflow/internal/DataFlowImpl2.qll | 114 +++++++++++++++++- .../DataFlowImplForHttpClientLibraries.qll | 114 +++++++++++++++++- .../internal/DataFlowImplForPathname.qll | 114 +++++++++++++++++- .../internal/DataFlowImplForRegExp.qll | 114 +++++++++++++++++- .../swift/dataflow/internal/DataFlowImpl.qll | 114 +++++++++++++++++- 36 files changed, 3996 insertions(+), 108 deletions(-) diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForRegExp.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForRegExp.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForRegExp.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForRegExp.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll index 2dde1c2819d..3c41b1876dc 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll @@ -147,6 +147,12 @@ abstract class Configuration extends string { */ FlowFeature getAFeature() { none() } + /** Holds if sources should be grouped in the result of `hasFlowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `hasFlowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup) { none() } + /** * Holds if data may flow from `source` to `sink` for this configuration. */ @@ -158,7 +164,7 @@ abstract class Configuration extends string { * The corresponding paths are generated from the end-points and the graph * included in the module `PathGraph`. */ - predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) } /** * Holds if data may flow from some source to `sink` for this configuration. @@ -2712,6 +2718,18 @@ private newtype TPathNode = state = sink.getState() and config = sink.getConfiguration() ) + } or + TPathNodeSourceGroup(string sourceGroup, Configuration config) { + exists(PathNodeImpl source | + sourceGroup = source.getSourceGroup() and + config = source.getConfiguration() + ) + } or + TPathNodeSinkGroup(string sinkGroup, Configuration config) { + exists(PathNodeSink sink | + sinkGroup = sink.getSinkGroup() and + config = sink.getConfiguration() + ) } /** @@ -2920,6 +2938,22 @@ abstract private class PathNodeImpl extends TPathNode { ) } + string getSourceGroup() { + this.isSource() and + this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2959,7 +2993,9 @@ abstract private class PathNodeImpl extends TPathNode { /** Holds if `n` can reach a sink. */ private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) } /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ @@ -3015,6 +3051,12 @@ class PathNode instanceof PathNodeImpl { /** Holds if this node is a source. */ final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) } } /** @@ -3136,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNodeImpl getASuccessorImpl() { none() } + override PathNodeImpl getASuccessorImpl() { + result = TPathNodeSinkGroup(this.getSinkGroup(), config) + } override predicate isSource() { sourceNode(node, state, config) } + + string getSinkGroup() { config.sinkGrouping(node.asNode(), result) } +} + +private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + Configuration config; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { + result.getSourceGroup() = sourceGroup and + result.getConfiguration() = config + } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } +} + +private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + Configuration config; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } } private predicate pathNode( @@ -3495,6 +3594,15 @@ private module Subpaths { * Will only have results if `configuration` has non-empty sources and * sinks. */ +private predicate hasFlowPath( + PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration +) { + flowsource.isFlowSource() and + flowsource.getConfiguration() = configuration and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() +} + private predicate flowsTo( PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration From db20e7d14313ee42aa58c8f86b27a47a87653bc2 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 3 Nov 2022 15:51:19 +0000 Subject: [PATCH 0134/1420] Ruby: add ActionCable channel RPC params as remote-flow sources --- .../codeql/ruby/frameworks/ActionCable.qll | 31 +++++++++++++++++++ .../action_cable/ActionCable.expected | 3 ++ .../frameworks/action_cable/ActionCable.ql | 3 ++ .../frameworks/action_cable/action_cable.rb | 10 ++++++ 4 files changed, 47 insertions(+) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll index e306b7d2b99..094b7d0d46f 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll @@ -6,6 +6,8 @@ private import codeql.ruby.AST private import codeql.ruby.Concepts private import codeql.ruby.ApiGraphs +private import codeql.ruby.DataFlow +private import codeql.ruby.dataflow.RemoteFlowSources private import codeql.ruby.frameworks.stdlib.Logger::Logger as StdlibLogger /** @@ -26,4 +28,33 @@ module ActionCable { } } } + + /** + * The data argument in an RPC endpoint method on a subclass of + * `ActionCable::Channel::Base`, considered as a remote flow source. + */ + class ActionCableChannelRpcParam extends RemoteFlowSource::Range { + ActionCableChannelRpcParam() { + exists(DataFlow::MethodNode m | + // Any method on a subclass of `ActionCable::Channel::Base` + // automatically becomes an RPC endpoint + m = + DataFlow::getConstant("ActionCable") + .getConstant("Channel") + .getConstant("Base") + .getADescendentModule() + .getAnOwnInstanceMethod() and + // as long as it's public + m.asCallableAstNode().isPublic() and + // and is not called `subscribed` or `unsubscribed`. + not m.getMethodName() = ["subscribed", "unsubscribed"] + | + // If the method takes a parameter, it contains data from the remote + // request. + this = m.getParameter(0) + ) + } + + override string getSourceType() { result = "ActionCable channel RPC data" } + } } diff --git a/ruby/ql/test/library-tests/frameworks/action_cable/ActionCable.expected b/ruby/ql/test/library-tests/frameworks/action_cable/ActionCable.expected index 94e553900b1..815990a36f6 100644 --- a/ruby/ql/test/library-tests/frameworks/action_cable/ActionCable.expected +++ b/ruby/ql/test/library-tests/frameworks/action_cable/ActionCable.expected @@ -1 +1,4 @@ +loggerInstantiations | action_cable.rb:1:1:1:54 | call to new | +remoteFlowSources +| action_cable.rb:9:10:9:13 | data | diff --git a/ruby/ql/test/library-tests/frameworks/action_cable/ActionCable.ql b/ruby/ql/test/library-tests/frameworks/action_cable/ActionCable.ql index fd2359607f7..3a06be800f5 100644 --- a/ruby/ql/test/library-tests/frameworks/action_cable/ActionCable.ql +++ b/ruby/ql/test/library-tests/frameworks/action_cable/ActionCable.ql @@ -1,4 +1,7 @@ import codeql.ruby.frameworks.ActionCable import codeql.ruby.frameworks.stdlib.Logger +import codeql.ruby.dataflow.RemoteFlowSources query predicate loggerInstantiations(Logger::LoggerInstantiation l) { any() } + +query predicate remoteFlowSources(RemoteFlowSource s) { any() } diff --git a/ruby/ql/test/library-tests/frameworks/action_cable/action_cable.rb b/ruby/ql/test/library-tests/frameworks/action_cable/action_cable.rb index 3988ec4c875..5dc902d7b74 100644 --- a/ruby/ql/test/library-tests/frameworks/action_cable/action_cable.rb +++ b/ruby/ql/test/library-tests/frameworks/action_cable/action_cable.rb @@ -1 +1,11 @@ ActionCable::Connection::TaggedLoggerProxy.new(logger) + +class MyChannel < ActionCable::Channel::Base + # An RPC endpoint without the optional data parm, so no remote flow source. + def foo + end + + # An RPC endpoint including the optional data parm, which is a remote flow source. + def bar(data) + end +end \ No newline at end of file From 199b3f4d71337bd67ed8c78386a2d3bf221c91cd Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Wed, 9 Nov 2022 14:18:44 +0000 Subject: [PATCH 0135/1420] Ruby: add change note for ActionCable channel remote flow sources --- ruby/ql/lib/change-notes/2022-11-09-actioncable-channels.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 ruby/ql/lib/change-notes/2022-11-09-actioncable-channels.md diff --git a/ruby/ql/lib/change-notes/2022-11-09-actioncable-channels.md b/ruby/ql/lib/change-notes/2022-11-09-actioncable-channels.md new file mode 100644 index 00000000000..3248fc194e0 --- /dev/null +++ b/ruby/ql/lib/change-notes/2022-11-09-actioncable-channels.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Arguments to RPC endpoints (public methods) on subclasses of `ActionCable::Channel::Base` are now recognized as sources of remote user input. From 611ed93e3987c141fa6dee39590569a7fff5751c Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Wed, 9 Nov 2022 15:18:16 +0000 Subject: [PATCH 0136/1420] Ruby: add is{Public,Protected,Private} to DataFlow::MethodNode --- .../lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll index e04449fbeaa..bc903c64f84 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll @@ -1058,6 +1058,15 @@ class MethodNode extends CallableNode { /** Gets the name of this method. */ string getMethodName() { result = this.asCallableAstNode().getName() } + + /** Holds if this method is public. */ + predicate isPublic() { this.asCallableAstNode().isPublic() } + + /** Holds if this method is private. */ + predicate isPrivate() { this.asCallableAstNode().isPrivate() } + + /** Holds if this method is protected. */ + predicate isProtected() { this.asCallableAstNode().isProtected() } } /** From cfde7e9edcd71c806fea0594d8a0e1cb5d3b1764 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Wed, 9 Nov 2022 16:14:11 +0000 Subject: [PATCH 0137/1420] Ruby: more accurate modeling of which ActionCable channel methods become endpoints --- ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll index 094b7d0d46f..a3f9b720c30 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll @@ -43,8 +43,17 @@ module ActionCable { .getConstant("Channel") .getConstant("Base") .getADescendentModule() - .getAnOwnInstanceMethod() and - // as long as it's public + .getAnInstanceMethod() and + // as long as it's not an instance method of + // `ActionCable::Channel::Base` itself, which might exist in the + // database + not m = + DataFlow::getConstant("ActionCable") + .getConstant("Channel") + .getConstant("Base") + .asModule() + .getAnInstanceMethod() and + // and as long as it's public m.asCallableAstNode().isPublic() and // and is not called `subscribed` or `unsubscribed`. not m.getMethodName() = ["subscribed", "unsubscribed"] From 6cb01a210fdf8886c82f410ffed1faf11c9df49c Mon Sep 17 00:00:00 2001 From: Tiferet Gazit Date: Wed, 9 Nov 2022 12:53:52 -0800 Subject: [PATCH 0138/1420] Apply suggestions from code review Co-authored-by: Stephan Brandauer --- .../adaptivethreatmodeling/EndpointCharacteristics.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll index ce99a62f24c..58c9bb6d3bd 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll @@ -36,9 +36,9 @@ abstract class EndpointCharacteristic extends string { * isPositiveIndicator: If true, this characteristic indicates that this endpoint _is_ a member of the class; if * false, it indicates that it _isn't_ a member of the class. * confidence: A float in [0, 1], which tells us how strong an indicator this characteristic is for the endpoint - * belonging / not belonging to the given class. A confidence near zero means this characterestic is a very weak + * belonging / not belonging to the given class. A confidence near zero means this characteristic is a very weak * indicator of whether or not the endpoint belongs to the class. A confidence of 1 means that all endpoints with - * this characteristic difinitively do/don't belong to the class. + * this characteristic definitively do/don't belong to the class. */ abstract predicate getImplications( EndpointType endpointClass, boolean isPositiveIndicator, float confidence From 243980ef73d14e2099e6d4fb98abc7a7904f5e66 Mon Sep 17 00:00:00 2001 From: tiferet Date: Wed, 9 Nov 2022 13:04:16 -0800 Subject: [PATCH 0139/1420] Documentation improvements --- .../adaptivethreatmodeling/EndpointCharacteristics.qll | 5 +++-- .../experimental/adaptivethreatmodeling/EndpointTypes.qll | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll index 58c9bb6d3bd..4756f645a95 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll @@ -31,8 +31,9 @@ abstract class EndpointCharacteristic extends string { * This predicate describes what the characteristic tells us about an endpoint. * * Params: - * endpointClass: Class 0 is the negative class, containing non-sink endpoints. Each positive int corresponds to a - * single sink type. + * endpointClass: The sink type. Each EndpointType has a predicate getEncoding, which specifies the classifier + * class for this sink type. Class 0 is the negative class (non-sink). Each positive int corresponds to a single + * sink type. * isPositiveIndicator: If true, this characteristic indicates that this endpoint _is_ a member of the class; if * false, it indicates that it _isn't_ a member of the class. * confidence: A float in [0, 1], which tells us how strong an indicator this characteristic is for the endpoint diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll index aa625b12862..093ed9f3437 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll @@ -16,6 +16,11 @@ newtype TEndpointType = abstract class EndpointType extends TEndpointType { abstract string getDescription(); + /** + * The integer representation of this endpoint type. This integer representation specifies the class number used + * by the endpoint scoring model (the classifier) to represent this endpoint type. Class 0 is the negative class + * (non-sink). Each positive int corresponds to a single sink type. + */ abstract int getEncoding(); string toString() { result = getDescription() } From b6532fa9a0b3de49b7ad045cca35562b873850d2 Mon Sep 17 00:00:00 2001 From: tiferet Date: Wed, 9 Nov 2022 13:10:54 -0800 Subject: [PATCH 0140/1420] Fix QLDoc style warning --- .../experimental/adaptivethreatmodeling/EndpointTypes.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll index 093ed9f3437..d2cc37b1b33 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll @@ -17,9 +17,9 @@ abstract class EndpointType extends TEndpointType { abstract string getDescription(); /** - * The integer representation of this endpoint type. This integer representation specifies the class number used - * by the endpoint scoring model (the classifier) to represent this endpoint type. Class 0 is the negative class - * (non-sink). Each positive int corresponds to a single sink type. + * Gets the integer representation of this endpoint type. This integer representation specifies the class number + * used by the endpoint scoring model (the classifier) to represent this endpoint type. Class 0 is the negative + * class (non-sink). Each positive int corresponds to a single sink type. */ abstract int getEncoding(); From dbcdc2209e556484408971a0188e63fbed2377ae Mon Sep 17 00:00:00 2001 From: tiferet Date: Wed, 9 Nov 2022 14:25:08 -0800 Subject: [PATCH 0141/1420] Use names constants for confidence levels --- .../adaptivethreatmodeling/ATMConfig.qll | 3 +- .../EndpointCharacteristics.qll | 28 +++++++++++++++---- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/ATMConfig.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/ATMConfig.qll index ba555b0de5b..55d75ad2e4d 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/ATMConfig.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/ATMConfig.qll @@ -50,7 +50,8 @@ abstract class AtmConfig extends string { // known sink for the class. exists(EndpointCharacteristic characteristic | characteristic.getEndpoints(sink) and - characteristic.getImplications(this.getASinkEndpointType(), true, 1.0) + characteristic + .getImplications(this.getASinkEndpointType(), true, characteristic.maximalConfidence()) ) } diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll index 4756f645a95..33cd559503c 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll @@ -44,6 +44,14 @@ abstract class EndpointCharacteristic extends string { abstract predicate getImplications( EndpointType endpointClass, boolean isPositiveIndicator, float confidence ); + + // The following are some confidence values that are used in practice by the subclasses. They are defined as named + // constants here to make it easier to change them in the future. + final float maximalConfidence() { result = 1.0 } + + final float highConfidence() { result = 0.9 } + + final float mediumConfidence() { result = 0.6 } } /* @@ -63,7 +71,9 @@ private class DomBasedXssSinkCharacteristic extends EndpointCharacteristic { override predicate getImplications( EndpointType endpointClass, boolean isPositiveIndicator, float confidence ) { - endpointClass instanceof XssSinkType and isPositiveIndicator = true and confidence = 1.0 + endpointClass instanceof XssSinkType and + isPositiveIndicator = true and + confidence = maximalConfidence() } } @@ -79,7 +89,9 @@ private class TaintedPathSinkCharacteristic extends EndpointCharacteristic { override predicate getImplications( EndpointType endpointClass, boolean isPositiveIndicator, float confidence ) { - endpointClass instanceof TaintedPathSinkType and isPositiveIndicator = true and confidence = 1.0 + endpointClass instanceof TaintedPathSinkType and + isPositiveIndicator = true and + confidence = maximalConfidence() } } @@ -97,7 +109,7 @@ private class SqlInjectionSinkCharacteristic extends EndpointCharacteristic { ) { endpointClass instanceof SqlInjectionSinkType and isPositiveIndicator = true and - confidence = 1.0 + confidence = maximalConfidence() } } @@ -115,7 +127,7 @@ private class NosqlInjectionSinkCharacteristic extends EndpointCharacteristic { ) { endpointClass instanceof NosqlInjectionSinkType and isPositiveIndicator = true and - confidence = 1.0 + confidence = maximalConfidence() } } @@ -151,7 +163,9 @@ abstract private class NotASinkCharacteristic extends OtherModeledArgumentCharac override predicate getImplications( EndpointType endpointClass, boolean isPositiveIndicator, float confidence ) { - endpointClass instanceof NegativeType and isPositiveIndicator = true and confidence = 0.9 + endpointClass instanceof NegativeType and + isPositiveIndicator = true and + confidence = highConfidence() } } @@ -168,7 +182,9 @@ abstract class LikelyNotASinkCharacteristic extends OtherModeledArgumentCharacte override predicate getImplications( EndpointType endpointClass, boolean isPositiveIndicator, float confidence ) { - endpointClass instanceof NegativeType and isPositiveIndicator = true and confidence = 0.6 + endpointClass instanceof NegativeType and + isPositiveIndicator = true and + confidence = mediumConfidence() } } From a8b0d298ffd0f14aae5952a7612e1044aa038e71 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Thu, 10 Nov 2022 16:38:09 +1300 Subject: [PATCH 0142/1420] Ruby: More string comparison guards Recognise if statements with conditionals made up or logical `and` or `or` clauses as barrier guards. --- .../codeql/ruby/dataflow/BarrierGuards.qll | 25 ++++++++++ .../dataflow/barrier-guards/barrier-guards.rb | 46 +++++++++++++++++-- 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll index f42ca8155b2..b2827e53255 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll @@ -25,6 +25,31 @@ private predicate stringConstCompare(CfgNodes::AstCfgNode guard, CfgNode testedN ) or stringConstCaseCompare(guard, testedNode, branch) + or + stringConstCompareOr(guard, testedNode, branch) + or + stringConstCompareAnd(guard, testedNode, branch) +} + +private predicate stringConstCompareOr( + CfgNodes::ExprNodes::BinaryOperationCfgNode guard, CfgNode testedNode, boolean branch +) { + guard.getExpr() instanceof LogicalOrExpr and + branch = true and + exists(Ssa::Definition def | + forall(CfgNode innerGuard | innerGuard = guard.getAnOperand() | + stringConstCompare(innerGuard, def.getARead(), branch) + ) and + testedNode = any(CfgNode node | stringConstCompare(guard.getAnOperand(), node, _)) + ) +} + +private predicate stringConstCompareAnd( + CfgNodes::ExprNodes::BinaryOperationCfgNode guard, CfgNode testedNode, boolean branch +) { + guard.getExpr() instanceof LogicalAndExpr and + branch = true and + stringConstCompare(guard.getAnOperand(), testedNode, branch) } /** diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb index 926cb4a06c5..26c5da44798 100644 --- a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb @@ -136,10 +136,10 @@ when "bar" end case foo -when "foo", "bar" # $ MISSING: guarded - foo -when "baz", "quux" # $ MISSING: guarded - foo +when "foo", "bar" + foo # $ MISSING: guarded +when "baz", "quux" + foo # $ MISSING: guarded else foo end @@ -189,7 +189,43 @@ when *FOO_AND_X # not a guard - includes non-constant element `x` end if foo == "foo" or foo == "bar" - foo # $ MISSING: guarded + foo # $ guarded +end + +if foo == "foo" or foo == "bar" or foo == "baz" + foo # $ guarded +end + +if foo == "foo" or foo == "bar" or foo == x + foo +end + +if foo == "foo" or bar == "bar" or foo == "baz" + foo +end + +if foo == "foo" and x + foo # $ guarded +end + +if x and foo == "foo" + foo # $ guarded +end + +if x and y and foo == "foo" + foo # $ guarded +end + +if foo == "foo" and foo == "bar" # $ guarded (second `foo` is guarded by the first comparison) + foo # $ guarded +end + +if x and y + foo +end + +if foo == "foo" and y + bar end case From ee02265ac2fd18bfaa8cd1eafa857fc354db7f6c Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Thu, 10 Nov 2022 12:24:39 +0100 Subject: [PATCH 0143/1420] Add property `params` to `RequestInputAccess` --- javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll b/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll index 18790d979c2..4c7dd3e5a49 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll @@ -134,7 +134,7 @@ module Hapi { kind = "parameter" and exists(DataFlow::PropRead query | // `request.query.name` - query.accesses(request, "query") and + query.accesses(request, ["query", "params"]) and this.(DataFlow::PropRead).accesses(query, _) ) or From 2b5e2ed2825250c45ba53f4d050f7f741f7c5a82 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 10 Nov 2022 11:41:52 +0000 Subject: [PATCH 0144/1420] Ruby: factor out some code into a helper predicate --- .../lib/codeql/ruby/frameworks/ActionCable.qll | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll index a3f9b720c30..2da79661c21 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll @@ -29,6 +29,10 @@ module ActionCable { } } + private DataFlow::ConstRef getActionCableChannelBase() { + result = DataFlow::getConstant("ActionCable").getConstant("Channel").getConstant("Base") + } + /** * The data argument in an RPC endpoint method on a subclass of * `ActionCable::Channel::Base`, considered as a remote flow source. @@ -38,21 +42,11 @@ module ActionCable { exists(DataFlow::MethodNode m | // Any method on a subclass of `ActionCable::Channel::Base` // automatically becomes an RPC endpoint - m = - DataFlow::getConstant("ActionCable") - .getConstant("Channel") - .getConstant("Base") - .getADescendentModule() - .getAnInstanceMethod() and + m = getActionCableChannelBase().getADescendentModule().getAnInstanceMethod() and // as long as it's not an instance method of // `ActionCable::Channel::Base` itself, which might exist in the // database - not m = - DataFlow::getConstant("ActionCable") - .getConstant("Channel") - .getConstant("Base") - .asModule() - .getAnInstanceMethod() and + not m = getActionCableChannelBase().asModule().getAnInstanceMethod() and // and as long as it's public m.asCallableAstNode().isPublic() and // and is not called `subscribed` or `unsubscribed`. From 4a98ef064ea0aba1ce83e2107c6c0460d0ad4c6c Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 10 Nov 2022 11:51:47 +0000 Subject: [PATCH 0145/1420] Ruby: use the 'customizations' pattern for the SQL injection query --- .../security/SqlInjectionCustomizations.qll | 45 +++++++++++++++++++ .../ruby/security/SqlInjectionQuery.qll | 18 ++++++++ .../queries/security/cwe-089/SqlInjection.ql | 21 +-------- 3 files changed, 65 insertions(+), 19 deletions(-) create mode 100644 ruby/ql/lib/codeql/ruby/security/SqlInjectionCustomizations.qll create mode 100644 ruby/ql/lib/codeql/ruby/security/SqlInjectionQuery.qll diff --git a/ruby/ql/lib/codeql/ruby/security/SqlInjectionCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/SqlInjectionCustomizations.qll new file mode 100644 index 00000000000..68f053109f5 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/SqlInjectionCustomizations.qll @@ -0,0 +1,45 @@ +/** + * Provides default sources, sinks and sanitizers for detecting SQL injection + * vulnerabilities, as well as extension points for adding your own. + */ + +private import codeql.ruby.Concepts +private import codeql.ruby.DataFlow +private import codeql.ruby.dataflow.BarrierGuards +private import codeql.ruby.dataflow.RemoteFlowSources + +/** + * Provides default sources, sinks and sanitizers for detecting SQL injection + * vulnerabilities, as well as extension points for adding your own. + */ +module SqlInjection { + abstract class Source extends DataFlow::Node { } + + abstract class Sink extends DataFlow::Node { } + + abstract class Sanitizer extends DataFlow::Node { } + + /** + * A source of remote user input, considered as a flow source. + */ + private class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + + /** + * A SQL statement of a SQL execution, considered as a flow sink. + */ + private class SqlExecutionAsSink extends Sink { + SqlExecutionAsSink() { this = any(SqlExecution e).getSql() } + } + + /** + * A comparison with a constant string, considered as a sanitizer-guard. + */ + private class StringConstCompareAsSanitizerGuard extends Sanitizer, StringConstCompareBarrier { } + + /** + * An inclusion check against an array of constant strings, considered as a + * sanitizer-guard. + */ + class StringConstArrayInclusionCallAsSanitizer extends Sanitizer, + StringConstArrayInclusionCallBarrier { } +} diff --git a/ruby/ql/lib/codeql/ruby/security/SqlInjectionQuery.qll b/ruby/ql/lib/codeql/ruby/security/SqlInjectionQuery.qll new file mode 100644 index 00000000000..e2dd327ccbd --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/SqlInjectionQuery.qll @@ -0,0 +1,18 @@ +/** + * Provides default sources, sinks and sanitizers for detecting SQL injection + * vulnerabilities, as well as extension points for adding your own. + */ + +private import codeql.ruby.DataFlow +private import codeql.ruby.TaintTracking +import SqlInjectionCustomizations::SqlInjection + +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "SqlInjectionConfiguration" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node source) { source instanceof Sink } + + override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer } +} diff --git a/ruby/ql/src/queries/security/cwe-089/SqlInjection.ql b/ruby/ql/src/queries/security/cwe-089/SqlInjection.ql index fa7bc13402c..28be96deb7d 100644 --- a/ruby/ql/src/queries/security/cwe-089/SqlInjection.ql +++ b/ruby/ql/src/queries/security/cwe-089/SqlInjection.ql @@ -11,28 +11,11 @@ * external/cwe/cwe-089 */ -import codeql.ruby.AST -import codeql.ruby.Concepts import codeql.ruby.DataFlow -import codeql.ruby.dataflow.BarrierGuards -import codeql.ruby.dataflow.RemoteFlowSources -import codeql.ruby.TaintTracking +import codeql.ruby.security.SqlInjectionQuery import DataFlow::PathGraph -class SqlInjectionConfiguration extends TaintTracking::Configuration { - SqlInjectionConfiguration() { this = "SQLInjectionConfiguration" } - - override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - - override predicate isSink(DataFlow::Node sink) { sink instanceof SqlExecution } - - override predicate isSanitizer(DataFlow::Node node) { - node instanceof StringConstCompareBarrier or - node instanceof StringConstArrayInclusionCallBarrier - } -} - -from SqlInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink +from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink where config.hasFlowPath(source, sink) select sink.getNode(), source, sink, "This SQL query depends on a $@.", source.getNode(), "user-provided value" From 9f31ef851fead59ba63a2bf594da622bacf8cd7f Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 10 Nov 2022 11:52:47 +0000 Subject: [PATCH 0146/1420] Python: fix spelling of SqlExecution class in comment --- python/ql/lib/semmle/python/Concepts.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/lib/semmle/python/Concepts.qll b/python/ql/lib/semmle/python/Concepts.qll index fe091f84e9c..861e4e7ca81 100644 --- a/python/ql/lib/semmle/python/Concepts.qll +++ b/python/ql/lib/semmle/python/Concepts.qll @@ -311,7 +311,7 @@ module CodeExecution { * 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`. + * 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. @@ -329,7 +329,7 @@ module SqlConstruction { * 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`. + * 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. From c9d34947b76d702a9e709cd2232dd799c33e9a92 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 10 Nov 2022 12:17:56 +0000 Subject: [PATCH 0147/1420] Ruby: add SqlConstruction concept --- ruby/ql/lib/codeql/ruby/Concepts.qll | 41 +++++++++++++++++++ .../security/SqlInjectionCustomizations.qll | 9 +++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/ruby/ql/lib/codeql/ruby/Concepts.qll b/ruby/ql/lib/codeql/ruby/Concepts.qll index 2d70cc31796..a42fa354660 100644 --- a/ruby/ql/lib/codeql/ruby/Concepts.qll +++ b/ruby/ql/lib/codeql/ruby/Concepts.qll @@ -11,9 +11,47 @@ private import codeql.ruby.Frameworks private import codeql.ruby.dataflow.RemoteFlowSources private import codeql.ruby.ApiGraphs +/** + * A data-flow node that constructs a SQL statement. + * + * Often, it is worthy of an alert if a SQL statement is constructed such that + * executing it would be a security risk. + * + * If it is important that the SQL statement is indeed executed, 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() } +} + +/** Provides a class for modeling new SQL execution APIs. */ +module SqlConstruction { + /** + * A data-flow node that constructs a SQL statement. + * + * Often, it is worthy of an alert if a SQL statement is constructed such that + * executing it would be a security risk. + * + * If it is important that the SQL statement is indeed executed, 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(); + } +} + /** * A data-flow node that executes SQL statements. * + * If the context of interest is such that merely constructing a SQL statement + * would be valuable to report, consider using `SqlConstruction`. + * * Extend this class to refine existing API models. If you want to model new APIs, * extend `SqlExecution::Range` instead. */ @@ -27,6 +65,9 @@ module SqlExecution { /** * A data-flow node that executes SQL statements. * + * If the context of interest is such that merely constructing a SQL + * statement would be valuable to report, consider using `SqlConstruction`. + * * Extend this class to model new APIs. If you want to refine existing API models, * extend `SqlExecution` instead. */ diff --git a/ruby/ql/lib/codeql/ruby/security/SqlInjectionCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/SqlInjectionCustomizations.qll index 68f053109f5..175d0935fad 100644 --- a/ruby/ql/lib/codeql/ruby/security/SqlInjectionCustomizations.qll +++ b/ruby/ql/lib/codeql/ruby/security/SqlInjectionCustomizations.qll @@ -25,12 +25,19 @@ module SqlInjection { private class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } /** - * A SQL statement of a SQL execution, considered as a flow sink. + * An SQL statement of a SQL execution, considered as a flow sink. */ private class SqlExecutionAsSink extends Sink { SqlExecutionAsSink() { this = any(SqlExecution e).getSql() } } + /** + * An SQL statement of a SQL construction, considered as a flow sink. + */ + private class SqlConstructionAsSink extends Sink { + SqlConstructionAsSink() { this = any(SqlConstruction e).getSql() } + } + /** * A comparison with a constant string, considered as a sanitizer-guard. */ From 5a15558355feefe79590df04b259b51eac3a0a26 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 10 Nov 2022 12:53:58 +0000 Subject: [PATCH 0148/1420] Ruby: treat an Arel.sql call as a SqlConstruction --- ruby/ql/lib/codeql/ruby/frameworks/Arel.qll | 12 ++++++++++++ .../frameworks/arel/SqlConstruction.expected | 2 ++ .../library-tests/frameworks/arel/SqlConstruction.ql | 4 ++++ .../query-tests/security/cwe-089/ArelInjection.rb | 8 ++++++++ .../security/cwe-089/SqlInjection.expected | 6 ++++++ 5 files changed, 32 insertions(+) create mode 100644 ruby/ql/test/library-tests/frameworks/arel/SqlConstruction.expected create mode 100644 ruby/ql/test/library-tests/frameworks/arel/SqlConstruction.ql create mode 100644 ruby/ql/test/query-tests/security/cwe-089/ArelInjection.rb diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Arel.qll b/ruby/ql/lib/codeql/ruby/frameworks/Arel.qll index 9fa17f4e5a5..00f45507cfc 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Arel.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Arel.qll @@ -6,6 +6,8 @@ private import codeql.ruby.ApiGraphs private import codeql.ruby.dataflow.FlowSummary +private import codeql.ruby.Concepts +private import codeql.ruby.DataFlow /** * Provides modeling for Arel, a low level SQL library that powers ActiveRecord. @@ -28,4 +30,14 @@ module Arel { input = "Argument[0]" and output = "ReturnValue" and preservesValue = false } } + + /** A call to `Arel.sql`, considered as a SQL construction. */ + private class ArelSqlConstruction extends SqlConstruction::Range, DataFlow::CallNode { + ArelSqlConstruction() { + this = DataFlow::getConstant("Arel").getAMethodCall() and + this.getMethodName() = "sql" + } + + override DataFlow::Node getSql() { result = this.getArgument(0) } + } } diff --git a/ruby/ql/test/library-tests/frameworks/arel/SqlConstruction.expected b/ruby/ql/test/library-tests/frameworks/arel/SqlConstruction.expected new file mode 100644 index 00000000000..b25b2b387ca --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/arel/SqlConstruction.expected @@ -0,0 +1,2 @@ +| arel.rb:3:8:3:18 | call to sql | arel.rb:3:17:3:17 | x | +| arel.rb:8:8:8:18 | call to sql | arel.rb:8:17:8:17 | x | diff --git a/ruby/ql/test/library-tests/frameworks/arel/SqlConstruction.ql b/ruby/ql/test/library-tests/frameworks/arel/SqlConstruction.ql new file mode 100644 index 00000000000..2b2920918e5 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/arel/SqlConstruction.ql @@ -0,0 +1,4 @@ +import codeql.ruby.Concepts +import codeql.ruby.DataFlow + +query predicate sqlConstructions(SqlConstruction c, DataFlow::Node sql) { sql = c.getSql() } diff --git a/ruby/ql/test/query-tests/security/cwe-089/ArelInjection.rb b/ruby/ql/test/query-tests/security/cwe-089/ArelInjection.rb new file mode 100644 index 00000000000..a1efb3adabb --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-089/ArelInjection.rb @@ -0,0 +1,8 @@ + +class PotatoController < ActionController::Base + def unsafe_action + name = params[:user_name] + # BAD: SQL statement constructed from user input + sql = Arel.sql("SELECT * FROM users WHERE name = #{name}") + end +end \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected index d78b2675f25..d664bad4293 100644 --- a/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected +++ b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected @@ -33,6 +33,8 @@ edges | ActiveRecordInjection.rb:137:21:137:44 | ...[...] : | ActiveRecordInjection.rb:20:22:20:30 | condition : | | ActiveRecordInjection.rb:151:59:151:64 | call to params : | ActiveRecordInjection.rb:151:59:151:74 | ...[...] : | | ActiveRecordInjection.rb:151:59:151:74 | ...[...] : | ActiveRecordInjection.rb:151:27:151:76 | "this is an unsafe annotation:..." | +| ArelInjection.rb:4:12:4:17 | call to params : | ArelInjection.rb:4:12:4:29 | ...[...] : | +| ArelInjection.rb:4:12:4:29 | ...[...] : | ArelInjection.rb:6:20:6:61 | "SELECT * FROM users WHERE nam..." | nodes | ActiveRecordInjection.rb:8:25:8:28 | name : | semmle.label | name : | | ActiveRecordInjection.rb:8:31:8:34 | pass : | semmle.label | pass : | @@ -85,6 +87,9 @@ nodes | ActiveRecordInjection.rb:151:27:151:76 | "this is an unsafe annotation:..." | semmle.label | "this is an unsafe annotation:..." | | ActiveRecordInjection.rb:151:59:151:64 | call to params : | semmle.label | call to params : | | ActiveRecordInjection.rb:151:59:151:74 | ...[...] : | semmle.label | ...[...] : | +| ArelInjection.rb:4:12:4:17 | call to params : | semmle.label | call to params : | +| ArelInjection.rb:4:12:4:29 | ...[...] : | semmle.label | ...[...] : | +| ArelInjection.rb:6:20:6:61 | "SELECT * FROM users WHERE nam..." | semmle.label | "SELECT * FROM users WHERE nam..." | subpaths #select | ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | ActiveRecordInjection.rb:70:23:70:28 | call to params : | ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:70:23:70:28 | call to params | user-provided value | @@ -105,3 +110,4 @@ subpaths | ActiveRecordInjection.rb:92:21:92:35 | ...[...] | ActiveRecordInjection.rb:92:21:92:26 | call to params : | ActiveRecordInjection.rb:92:21:92:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:92:21:92:26 | call to params | user-provided value | | ActiveRecordInjection.rb:104:20:104:32 | ... + ... | ActiveRecordInjection.rb:98:10:98:15 | call to params : | ActiveRecordInjection.rb:104:20:104:32 | ... + ... | This SQL query depends on a $@. | ActiveRecordInjection.rb:98:10:98:15 | call to params | user-provided value | | ActiveRecordInjection.rb:151:27:151:76 | "this is an unsafe annotation:..." | ActiveRecordInjection.rb:151:59:151:64 | call to params : | ActiveRecordInjection.rb:151:27:151:76 | "this is an unsafe annotation:..." | This SQL query depends on a $@. | ActiveRecordInjection.rb:151:59:151:64 | call to params | user-provided value | +| ArelInjection.rb:6:20:6:61 | "SELECT * FROM users WHERE nam..." | ArelInjection.rb:4:12:4:17 | call to params : | ArelInjection.rb:6:20:6:61 | "SELECT * FROM users WHERE nam..." | This SQL query depends on a $@. | ArelInjection.rb:4:12:4:17 | call to params | user-provided value | From 0337ccb93a2f6d5304756df20d786a807a028693 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 10 Nov 2022 13:00:29 +0000 Subject: [PATCH 0149/1420] Ruby: add change notes for Arel.sql / SqlConstruction changes --- ruby/ql/lib/change-notes/2022-11-10-arel-sql.md | 5 +++++ ruby/ql/src/change-notes/2022-11-10-arel-sql.md | 4 ++++ 2 files changed, 9 insertions(+) create mode 100644 ruby/ql/lib/change-notes/2022-11-10-arel-sql.md create mode 100644 ruby/ql/src/change-notes/2022-11-10-arel-sql.md diff --git a/ruby/ql/lib/change-notes/2022-11-10-arel-sql.md b/ruby/ql/lib/change-notes/2022-11-10-arel-sql.md new file mode 100644 index 00000000000..e803d0e0895 --- /dev/null +++ b/ruby/ql/lib/change-notes/2022-11-10-arel-sql.md @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- +* The `codeql.ruby.Concepts` library now has a `SqlConstruction` class, in addition to the existing `SqlExecution` class. +* Calls to `Arel.sql` are now modeled as instances of the new `SqlConstruction` concept. diff --git a/ruby/ql/src/change-notes/2022-11-10-arel-sql.md b/ruby/ql/src/change-notes/2022-11-10-arel-sql.md new file mode 100644 index 00000000000..918e46a9d9b --- /dev/null +++ b/ruby/ql/src/change-notes/2022-11-10-arel-sql.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The `rb/sql-injection` query now considers consider SQL constructions, such as calls to `Arel.sql`, as sinks. From 511fb9727343d8c46ad2c980a1728ffa460eac4b Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 10 Nov 2022 14:30:06 +0000 Subject: [PATCH 0150/1420] Ruby: remove redundant import --- ruby/ql/lib/codeql/ruby/frameworks/Arel.qll | 1 - 1 file changed, 1 deletion(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Arel.qll b/ruby/ql/lib/codeql/ruby/frameworks/Arel.qll index 00f45507cfc..f57fa41c740 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Arel.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Arel.qll @@ -7,7 +7,6 @@ private import codeql.ruby.ApiGraphs private import codeql.ruby.dataflow.FlowSummary private import codeql.ruby.Concepts -private import codeql.ruby.DataFlow /** * Provides modeling for Arel, a low level SQL library that powers ActiveRecord. From 23ff3769aca3646e64ecbe5af45e57a82f31a538 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 31 Oct 2022 18:24:13 +0000 Subject: [PATCH 0151/1420] Swift: Add Alamofire tests for swift/cleartext-transmission. --- .../Security/CWE-311/SensitiveExprs.expected | 10 + .../Security/CWE-311/testAlamofire.swift | 218 ++++++++++++++++++ 2 files changed, 228 insertions(+) create mode 100644 swift/ql/test/query-tests/Security/CWE-311/testAlamofire.swift diff --git a/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.expected b/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.expected index 9e359b62e27..3c61ec86e95 100644 --- a/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.expected +++ b/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.expected @@ -1,3 +1,13 @@ +| testAlamofire.swift:150:45:150:45 | password | label:password, type:credential | +| testAlamofire.swift:152:51:152:51 | password | label:password, type:credential | +| testAlamofire.swift:154:38:154:38 | email | label:email, type:private information | +| testAlamofire.swift:159:26:159:26 | email | label:email, type:private information | +| testAlamofire.swift:171:35:171:35 | email | label:email, type:private information | +| testAlamofire.swift:177:35:177:35 | email | label:email, type:private information | +| testAlamofire.swift:187:65:187:65 | password | label:password, type:credential | +| testAlamofire.swift:195:64:195:64 | password | label:password, type:credential | +| testAlamofire.swift:205:62:205:62 | password | label:password, type:credential | +| testAlamofire.swift:213:65:213:65 | password | label:password, type:credential | | testCoreData.swift:48:15:48:15 | password | label:password, type:credential | | testCoreData.swift:51:24:51:24 | password | label:password, type:credential | | testCoreData.swift:58:15:58:15 | password | label:password, type:credential | diff --git a/swift/ql/test/query-tests/Security/CWE-311/testAlamofire.swift b/swift/ql/test/query-tests/Security/CWE-311/testAlamofire.swift new file mode 100644 index 00000000000..9056eebdbf1 --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-311/testAlamofire.swift @@ -0,0 +1,218 @@ + +// --- Foundation stubs --- + +class NSObject { +} + +struct URL { +} + +struct URLRequest { +} + +class URLResponse: NSObject { +} + +class HTTPURLResponse : URLResponse { +} + +// --- Alamofire stubs --- + +protocol URLConvertible { +} + +extension String: URLConvertible { +} + +struct HTTPMethod { + static let get = HTTPMethod(rawValue: "GET") + static let post = HTTPMethod(rawValue: "POST") + + init(rawValue: String) {} +} + +struct HTTPHeaders { + init(_ dictionary: [String: String]) {} + + mutating func add(name: String, value: String) {} + mutating func update(name: String, value: String) {} +} + +extension HTTPHeaders: ExpressibleByDictionaryLiteral { + public init(dictionaryLiteral elements: (String, String)...) {} +} + +typealias Parameters = [String: Any] + +protocol ParameterEncoding { +} + +struct URLEncoding: ParameterEncoding { + static var `default`: URLEncoding { URLEncoding() } +} + +protocol ParameterEncoder { +} + +class URLEncodedFormParameterEncoder: ParameterEncoder { + static var `default`: URLEncodedFormParameterEncoder { URLEncodedFormParameterEncoder() } +} + +protocol RequestInterceptor { +} + +class Request { +} + +class DataRequest: Request { +} + +final class DataStreamRequest: Request { +} + +class DownloadRequest: Request { + struct Options: OptionSet { + let rawValue: Int + + init(rawValue: Int) { + self.rawValue = rawValue + } + } + + typealias Destination = + (_ temporaryURL: URL, _ response: HTTPURLResponse) -> + (destinationURL: URL, options: Options) +} + +class Session { + static let `default` = Session() + + typealias RequestModifier = (inout URLRequest) throws -> Void + + func request( + _ convertible: URLConvertible, + method: HTTPMethod = .get, + parameters: Parameters? = nil, + encoding: ParameterEncoding = URLEncoding.default, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + requestModifier: RequestModifier? = nil) -> DataRequest { + return DataRequest() + } + + func request( + _ convertible: URLConvertible, + method: HTTPMethod = .get, + parameters: Parameters? = nil, + encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + requestModifier: RequestModifier? = nil) -> DataRequest { + return DataRequest() + } + + func streamRequest( + _ convertible: URLConvertible, + method: HTTPMethod = .get, + headers: HTTPHeaders? = nil, + automaticallyCancelOnStreamError: Bool = false, + interceptor: RequestInterceptor? = nil, + requestModifier: RequestModifier? = nil) -> DataStreamRequest { + return DataStreamRequest() + } + + func download( + _ convertible: URLConvertible, + method: HTTPMethod = .get, + parameters: Parameters? = nil, + encoding: ParameterEncoding = URLEncoding.default, + headers: HTTPHeaders? = nil, + interceptor: RequestInterceptor? = nil, + requestModifier: RequestModifier? = nil, + to destination: DownloadRequest.Destination? = nil) -> DownloadRequest { + return DownloadRequest() + } + + // (there are many more variants of `request`, `streamRequest` and `download`) +} + +let AF = Session.default + +// --- tests --- + +struct MyEncodable: Encodable { + let value: String +} + +func test1(username: String, password: String, email: String, harmless: String) { + // sensitive data in URL + + AF.request("http://example.com/login?p=" + password) // BAD [NOT DETECTED] + AF.request("http://example.com/login?h=" + harmless) // GOOD (not sensitive) + AF.streamRequest("http://example.com/login?p=" + password) // BAD [NOT DETECTED] + AF.streamRequest("http://example.com/login?h=" + harmless) // GOOD (not sensitive) + AF.download("http://example.com/" + email + ".html") // BAD [NOT DETECTED] + AF.download("http://example.com/" + harmless + ".html") // GOOD (not sensitive) + + // sensitive data in parameters + + let params1 = ["value": email] + let params2 = ["value": harmless] + + AF.request("http://example.com/", parameters: params1) // BAD [NOT DETECTED] + AF.request("http://example.com/", parameters: params2) // GOOD (not sensitive) + AF.request("http://example.com/", parameters: params1, encoding: URLEncoding.default) // BAD [NOT DETECTED] + AF.request("http://example.com/", parameters: params2, encoding: URLEncoding.default) // GOOD (not sensitive) + AF.request("http://example.com/", parameters: params1, encoder: URLEncodedFormParameterEncoder.default) // BAD [NOT DETECTED] + AF.request("http://example.com/", parameters: params2, encoder: URLEncodedFormParameterEncoder.default) // GOOD (not sensitive) + AF.download("http://example.com/", parameters: params1) // BAD [NOT DETECTED] + AF.download("http://example.com/", parameters: params2) // GOOD (not sensitive) + + let params3 = ["values": ["...", email, "..."]] + let params4 = ["values": ["...", harmless, "..."]] + + AF.request("http://example.com/", method:.post, parameters: params3) // BAD [NOT DETECTED] + AF.request("http://example.com/", method:.post, parameters: params4) // GOOD (not sensitive) + + let params5 = MyEncodable(value: email) + let params6 = MyEncodable(value: harmless) + + AF.request("http://example.com/", parameters: params5) // BAD [NOT DETECTED] + AF.request("http://example.com/", parameters: params6) // GOOD (not sensitive) + + // request headers + // - in real usage a password here would normally be base64 encoded for transmission + // - the risk is greatly reduced (but not eliminated) if HTTPS is used + + let headers1: HTTPHeaders = ["Authorization": username + ":" + password] + let headers2: HTTPHeaders = ["Value": harmless] + + AF.request("http://example.com/", headers: headers1) // BAD [NOT DETECTED] + AF.request("http://example.com/", headers: headers2) // GOOD (not sensitive) + AF.streamRequest("http://example.com/", headers: headers1) // BAD [NOT DETECTED] + AF.streamRequest("http://example.com/", headers: headers2) // GOOD (not sensitive) + + let headers3 = HTTPHeaders(["Authorization": username + ":" + password]) + let headers4 = HTTPHeaders(["Value": harmless]) + + AF.request("http://example.com/", headers: headers3) // BAD [NOT DETECTED] + AF.request("http://example.com/", headers: headers4) // GOOD (not sensitive) + AF.download("http://example.com/", headers: headers1) // BAD [NOT DETECTED] + AF.download("http://example.com/", headers: headers2) // GOOD (not sensitive) + + var headers5 = HTTPHeaders([:]) + var headers6 = HTTPHeaders([:]) + headers5.add(name: "Authorization", value: username + ":" + password) + headers6.add(name: "Data", value: harmless) + + AF.request("http://example.com/", headers: headers5) // BAD [NOT DETECTED] + AF.request("http://example.com/", headers: headers6) // GOOD (not sensitive) + + var headers7 = HTTPHeaders([:]) + var headers8 = HTTPHeaders([:]) + headers7.update(name: "Authorization", value: username + ":" + password) + headers8.update(name: "Data", value: harmless) + + AF.request("http://example.com/", headers: headers7) // BAD [NOT DETECTED] + AF.request("http://example.com/", headers: headers8) // GOOD (not sensitive) +} From 012fb28e258f91011d78630dc2384b9e983ee06d Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Thu, 10 Nov 2022 15:38:51 +0100 Subject: [PATCH 0152/1420] only extract .html.erb files instead of all .erb files --- javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java | 2 +- .../extractor/src/com/semmle/js/extractor/FileExtractor.java | 2 +- .../extractor/tests/vue/input/{rails.erb => rails.html.erb} | 0 .../vue/output/trap/{rails.erb.trap => rails.html.erb.trap} | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename javascript/extractor/tests/vue/input/{rails.erb => rails.html.erb} (100%) rename javascript/extractor/tests/vue/output/trap/{rails.erb.trap => rails.html.erb.trap} (100%) diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 9d5a4c3b52b..7f28d93a183 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -141,7 +141,7 @@ import com.semmle.util.trap.TrapWriter; *
  • All JavaScript files, that is, files with one of the extensions supported by {@link * FileType#JS} (currently ".js", ".jsx", ".mjs", ".cjs", ".es6", ".es"). *
  • All HTML files, that is, files with with one of the extensions supported by {@link - * FileType#HTML} (currently ".htm", ".html", ".xhtm", ".xhtml", ".vue", ".erb"). + * FileType#HTML} (currently ".htm", ".html", ".xhtm", ".xhtml", ".vue", ".html.erb"). *
  • All YAML files, that is, files with one of the extensions supported by {@link * FileType#YAML} (currently ".raml", ".yaml", ".yml"). *
  • Files with base name "package.json" or "tsconfig.json", and files whose base name diff --git a/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java index 77871a95fbe..45bd48bf408 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java @@ -104,7 +104,7 @@ public class FileExtractor { /** Information about supported file types. */ public static enum FileType { - HTML(".htm", ".html", ".xhtm", ".xhtml", ".vue", ".hbs", ".ejs", ".njk", ".erb") { + HTML(".htm", ".html", ".xhtm", ".xhtml", ".vue", ".hbs", ".ejs", ".njk", ".html.erb") { @Override public IExtractor mkExtractor(ExtractorConfig config, ExtractorState state) { return new HTMLExtractor(config, state); diff --git a/javascript/extractor/tests/vue/input/rails.erb b/javascript/extractor/tests/vue/input/rails.html.erb similarity index 100% rename from javascript/extractor/tests/vue/input/rails.erb rename to javascript/extractor/tests/vue/input/rails.html.erb diff --git a/javascript/extractor/tests/vue/output/trap/rails.erb.trap b/javascript/extractor/tests/vue/output/trap/rails.html.erb.trap similarity index 100% rename from javascript/extractor/tests/vue/output/trap/rails.erb.trap rename to javascript/extractor/tests/vue/output/trap/rails.html.erb.trap From c5bb32d6d22e082c83099a23764c95081052ff0c Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Thu, 10 Nov 2022 10:19:23 +0100 Subject: [PATCH 0153/1420] Swift: create common `ErrorElement` superclass --- swift/ql/lib/codeql/swift/elements.qll | 2 +- .../swift/elements/UnresolvedElement.qll | 4 - .../codeql/swift/generated/ParentChild.qll | 145 ++++++++++-------- swift/ql/lib/codeql/swift/generated/Raw.qll | 26 ++-- swift/ql/lib/codeql/swift/generated/Synth.qll | 114 ++++++++------ .../swift/generated/UnresolvedElement.qll | 8 - .../swift/generated/UnspecifiedElement.qll | 4 +- .../codeql/swift/generated/expr/ErrorExpr.qll | 3 +- .../generated/expr/OverloadedDeclRefExpr.qll | 3 +- .../generated/expr/UnresolvedDeclRefExpr.qll | 4 +- .../generated/expr/UnresolvedDotExpr.qll | 4 +- .../expr/UnresolvedMemberChainResultExpr.qll | 4 +- .../generated/expr/UnresolvedMemberExpr.qll | 4 +- .../generated/expr/UnresolvedPatternExpr.qll | 4 +- .../expr/UnresolvedSpecializeExpr.qll | 4 +- .../expr/UnresolvedTypeConversionExpr.qll | 4 +- .../codeql/swift/generated/type/ErrorType.qll | 3 +- .../swift/generated/type/UnresolvedType.qll | 4 +- swift/ql/lib/swift.dbscheme | 27 ++-- .../UnresolvedElement/UnresolvedElement.ql | 7 - .../UnspecifiedElement/UnspecifiedElement.ql | 11 -- .../UnspecifiedElement_getIndex.ql | 7 - .../UnspecifiedElement_getParent.ql | 7 - .../OverloadedDeclRefExpr.ql | 7 - ...oadedDeclRefExpr_getPossibleDeclaration.ql | 7 - .../OverloadedDeclRefExpr_getType.ql | 7 - .../type/ErrorType/MISSING_SOURCE.txt | 4 - swift/schema.py | 29 ++-- 28 files changed, 210 insertions(+), 247 deletions(-) delete mode 100644 swift/ql/lib/codeql/swift/elements/UnresolvedElement.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/UnresolvedElement.qll delete mode 100644 swift/ql/test/extractor-tests/generated/UnresolvedElement/UnresolvedElement.ql delete mode 100644 swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement.ql delete mode 100644 swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement_getIndex.ql delete mode 100644 swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement_getParent.ql delete mode 100644 swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr.ql delete mode 100644 swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr_getPossibleDeclaration.ql delete mode 100644 swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr_getType.ql delete mode 100644 swift/ql/test/extractor-tests/generated/type/ErrorType/MISSING_SOURCE.txt diff --git a/swift/ql/lib/codeql/swift/elements.qll b/swift/ql/lib/codeql/swift/elements.qll index c839ec52bc3..d821cd8fb49 100644 --- a/swift/ql/lib/codeql/swift/elements.qll +++ b/swift/ql/lib/codeql/swift/elements.qll @@ -6,12 +6,12 @@ import codeql.swift.elements.DbFile import codeql.swift.elements.DbLocation import codeql.swift.elements.Diagnostics import codeql.swift.elements.Element +import codeql.swift.elements.ErrorElement import codeql.swift.elements.File import codeql.swift.elements.Locatable import codeql.swift.elements.Location import codeql.swift.elements.UnknownFile import codeql.swift.elements.UnknownLocation -import codeql.swift.elements.UnresolvedElement import codeql.swift.elements.UnspecifiedElement import codeql.swift.elements.decl.AbstractFunctionDecl import codeql.swift.elements.decl.AbstractStorageDecl diff --git a/swift/ql/lib/codeql/swift/elements/UnresolvedElement.qll b/swift/ql/lib/codeql/swift/elements/UnresolvedElement.qll deleted file mode 100644 index 06456d4c28c..00000000000 --- a/swift/ql/lib/codeql/swift/elements/UnresolvedElement.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.UnresolvedElement - -class UnresolvedElement extends Generated::UnresolvedElement { } diff --git a/swift/ql/lib/codeql/swift/generated/ParentChild.qll b/swift/ql/lib/codeql/swift/generated/ParentChild.qll index a04f66fba88..34c422443f0 100644 --- a/swift/ql/lib/codeql/swift/generated/ParentChild.qll +++ b/swift/ql/lib/codeql/swift/generated/ParentChild.qll @@ -135,6 +135,21 @@ private module Impl { ) } + private Element getImmediateChildOfErrorElement( + ErrorElement e, int index, string partialPredicateCall + ) { + exists(int b, int bLocatable, int n | + b = 0 and + bLocatable = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfLocatable(e, i, _)) | i) and + n = bLocatable and + ( + none() + or + result = getImmediateChildOfLocatable(e, index - b, partialPredicateCall) + ) + ) + } + private Element getImmediateChildOfUnknownFile( UnknownFile e, int index, string partialPredicateCall ) { @@ -165,32 +180,18 @@ private module Impl { ) } - private Element getImmediateChildOfUnresolvedElement( - UnresolvedElement e, int index, string partialPredicateCall - ) { - exists(int b, int bLocatable, int n | - b = 0 and - bLocatable = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfLocatable(e, i, _)) | i) and - n = bLocatable and - ( - none() - or - result = getImmediateChildOfLocatable(e, index - b, partialPredicateCall) - ) - ) - } - private Element getImmediateChildOfUnspecifiedElement( UnspecifiedElement e, int index, string partialPredicateCall ) { - exists(int b, int bLocatable, int n | + exists(int b, int bErrorElement, int n | b = 0 and - bLocatable = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfLocatable(e, i, _)) | i) and - n = bLocatable and + bErrorElement = + b + 1 + max(int i | i = -1 or exists(getImmediateChildOfErrorElement(e, i, _)) | i) and + n = bErrorElement and ( none() or - result = getImmediateChildOfLocatable(e, index - b, partialPredicateCall) + result = getImmediateChildOfErrorElement(e, index - b, partialPredicateCall) ) ) } @@ -1221,14 +1222,18 @@ private module Impl { } private Element getImmediateChildOfErrorExpr(ErrorExpr e, int index, string partialPredicateCall) { - exists(int b, int bExpr, int n | + exists(int b, int bExpr, int bErrorElement, int n | b = 0 and bExpr = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfExpr(e, i, _)) | i) and - n = bExpr and + bErrorElement = + bExpr + 1 + max(int i | i = -1 or exists(getImmediateChildOfErrorElement(e, i, _)) | i) and + n = bErrorElement and ( none() or result = getImmediateChildOfExpr(e, index - b, partialPredicateCall) + or + result = getImmediateChildOfErrorElement(e, index - bExpr, partialPredicateCall) ) ) } @@ -1598,14 +1603,18 @@ private module Impl { private Element getImmediateChildOfOverloadedDeclRefExpr( OverloadedDeclRefExpr e, int index, string partialPredicateCall ) { - exists(int b, int bExpr, int n | + exists(int b, int bExpr, int bErrorElement, int n | b = 0 and bExpr = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfExpr(e, i, _)) | i) and - n = bExpr and + bErrorElement = + bExpr + 1 + max(int i | i = -1 or exists(getImmediateChildOfErrorElement(e, i, _)) | i) and + n = bErrorElement and ( none() or result = getImmediateChildOfExpr(e, index - b, partialPredicateCall) + or + result = getImmediateChildOfErrorElement(e, index - bExpr, partialPredicateCall) ) ) } @@ -1763,18 +1772,18 @@ private module Impl { private Element getImmediateChildOfUnresolvedDeclRefExpr( UnresolvedDeclRefExpr e, int index, string partialPredicateCall ) { - exists(int b, int bExpr, int bUnresolvedElement, int n | + exists(int b, int bExpr, int bErrorElement, int n | b = 0 and bExpr = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfExpr(e, i, _)) | i) and - bUnresolvedElement = - bExpr + 1 + max(int i | i = -1 or exists(getImmediateChildOfUnresolvedElement(e, i, _)) | i) and - n = bUnresolvedElement and + bErrorElement = + bExpr + 1 + max(int i | i = -1 or exists(getImmediateChildOfErrorElement(e, i, _)) | i) and + n = bErrorElement and ( none() or result = getImmediateChildOfExpr(e, index - b, partialPredicateCall) or - result = getImmediateChildOfUnresolvedElement(e, index - bExpr, partialPredicateCall) + result = getImmediateChildOfErrorElement(e, index - bExpr, partialPredicateCall) ) ) } @@ -1782,19 +1791,19 @@ private module Impl { private Element getImmediateChildOfUnresolvedDotExpr( UnresolvedDotExpr e, int index, string partialPredicateCall ) { - exists(int b, int bExpr, int bUnresolvedElement, int n, int nBase | + exists(int b, int bExpr, int bErrorElement, int n, int nBase | b = 0 and bExpr = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfExpr(e, i, _)) | i) and - bUnresolvedElement = - bExpr + 1 + max(int i | i = -1 or exists(getImmediateChildOfUnresolvedElement(e, i, _)) | i) and - n = bUnresolvedElement and + bErrorElement = + bExpr + 1 + max(int i | i = -1 or exists(getImmediateChildOfErrorElement(e, i, _)) | i) and + n = bErrorElement and nBase = n + 1 and ( none() or result = getImmediateChildOfExpr(e, index - b, partialPredicateCall) or - result = getImmediateChildOfUnresolvedElement(e, index - bExpr, partialPredicateCall) + result = getImmediateChildOfErrorElement(e, index - bExpr, partialPredicateCall) or index = n and result = e.getImmediateBase() and partialPredicateCall = "Base()" ) @@ -1804,18 +1813,18 @@ private module Impl { private Element getImmediateChildOfUnresolvedMemberExpr( UnresolvedMemberExpr e, int index, string partialPredicateCall ) { - exists(int b, int bExpr, int bUnresolvedElement, int n | + exists(int b, int bExpr, int bErrorElement, int n | b = 0 and bExpr = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfExpr(e, i, _)) | i) and - bUnresolvedElement = - bExpr + 1 + max(int i | i = -1 or exists(getImmediateChildOfUnresolvedElement(e, i, _)) | i) and - n = bUnresolvedElement and + bErrorElement = + bExpr + 1 + max(int i | i = -1 or exists(getImmediateChildOfErrorElement(e, i, _)) | i) and + n = bErrorElement and ( none() or result = getImmediateChildOfExpr(e, index - b, partialPredicateCall) or - result = getImmediateChildOfUnresolvedElement(e, index - bExpr, partialPredicateCall) + result = getImmediateChildOfErrorElement(e, index - bExpr, partialPredicateCall) ) ) } @@ -1823,19 +1832,19 @@ private module Impl { private Element getImmediateChildOfUnresolvedPatternExpr( UnresolvedPatternExpr e, int index, string partialPredicateCall ) { - exists(int b, int bExpr, int bUnresolvedElement, int n, int nSubPattern | + exists(int b, int bExpr, int bErrorElement, int n, int nSubPattern | b = 0 and bExpr = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfExpr(e, i, _)) | i) and - bUnresolvedElement = - bExpr + 1 + max(int i | i = -1 or exists(getImmediateChildOfUnresolvedElement(e, i, _)) | i) and - n = bUnresolvedElement and + bErrorElement = + bExpr + 1 + max(int i | i = -1 or exists(getImmediateChildOfErrorElement(e, i, _)) | i) and + n = bErrorElement and nSubPattern = n + 1 and ( none() or result = getImmediateChildOfExpr(e, index - b, partialPredicateCall) or - result = getImmediateChildOfUnresolvedElement(e, index - bExpr, partialPredicateCall) + result = getImmediateChildOfErrorElement(e, index - bExpr, partialPredicateCall) or index = n and result = e.getImmediateSubPattern() and partialPredicateCall = "SubPattern()" ) @@ -1845,19 +1854,19 @@ private module Impl { private Element getImmediateChildOfUnresolvedSpecializeExpr( UnresolvedSpecializeExpr e, int index, string partialPredicateCall ) { - exists(int b, int bExpr, int bUnresolvedElement, int n, int nSubExpr | + exists(int b, int bExpr, int bErrorElement, int n, int nSubExpr | b = 0 and bExpr = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfExpr(e, i, _)) | i) and - bUnresolvedElement = - bExpr + 1 + max(int i | i = -1 or exists(getImmediateChildOfUnresolvedElement(e, i, _)) | i) and - n = bUnresolvedElement and + bErrorElement = + bExpr + 1 + max(int i | i = -1 or exists(getImmediateChildOfErrorElement(e, i, _)) | i) and + n = bErrorElement and nSubExpr = n + 1 and ( none() or result = getImmediateChildOfExpr(e, index - b, partialPredicateCall) or - result = getImmediateChildOfUnresolvedElement(e, index - bExpr, partialPredicateCall) + result = getImmediateChildOfErrorElement(e, index - bExpr, partialPredicateCall) or index = n and result = e.getImmediateSubExpr() and partialPredicateCall = "SubExpr()" ) @@ -2841,21 +2850,20 @@ private module Impl { private Element getImmediateChildOfUnresolvedMemberChainResultExpr( UnresolvedMemberChainResultExpr e, int index, string partialPredicateCall ) { - exists(int b, int bIdentityExpr, int bUnresolvedElement, int n | + exists(int b, int bIdentityExpr, int bErrorElement, int n | b = 0 and bIdentityExpr = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfIdentityExpr(e, i, _)) | i) and - bUnresolvedElement = + bErrorElement = bIdentityExpr + 1 + - max(int i | i = -1 or exists(getImmediateChildOfUnresolvedElement(e, i, _)) | i) and - n = bUnresolvedElement and + max(int i | i = -1 or exists(getImmediateChildOfErrorElement(e, i, _)) | i) and + n = bErrorElement and ( none() or result = getImmediateChildOfIdentityExpr(e, index - b, partialPredicateCall) or - result = - getImmediateChildOfUnresolvedElement(e, index - bIdentityExpr, partialPredicateCall) + result = getImmediateChildOfErrorElement(e, index - bIdentityExpr, partialPredicateCall) ) ) } @@ -2863,23 +2871,22 @@ private module Impl { private Element getImmediateChildOfUnresolvedTypeConversionExpr( UnresolvedTypeConversionExpr e, int index, string partialPredicateCall ) { - exists(int b, int bImplicitConversionExpr, int bUnresolvedElement, int n | + exists(int b, int bImplicitConversionExpr, int bErrorElement, int n | b = 0 and bImplicitConversionExpr = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfImplicitConversionExpr(e, i, _)) | i) and - bUnresolvedElement = + bErrorElement = bImplicitConversionExpr + 1 + - max(int i | i = -1 or exists(getImmediateChildOfUnresolvedElement(e, i, _)) | i) and - n = bUnresolvedElement and + max(int i | i = -1 or exists(getImmediateChildOfErrorElement(e, i, _)) | i) and + n = bErrorElement and ( none() or result = getImmediateChildOfImplicitConversionExpr(e, index - b, partialPredicateCall) or result = - getImmediateChildOfUnresolvedElement(e, index - bImplicitConversionExpr, - partialPredicateCall) + getImmediateChildOfErrorElement(e, index - bImplicitConversionExpr, partialPredicateCall) ) ) } @@ -3874,14 +3881,18 @@ private module Impl { } private Element getImmediateChildOfErrorType(ErrorType e, int index, string partialPredicateCall) { - exists(int b, int bType, int n | + exists(int b, int bType, int bErrorElement, int n | b = 0 and bType = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfType(e, i, _)) | i) and - n = bType and + bErrorElement = + bType + 1 + max(int i | i = -1 or exists(getImmediateChildOfErrorElement(e, i, _)) | i) and + n = bErrorElement and ( none() or result = getImmediateChildOfType(e, index - b, partialPredicateCall) + or + result = getImmediateChildOfErrorElement(e, index - bType, partialPredicateCall) ) ) } @@ -4145,18 +4156,18 @@ private module Impl { private Element getImmediateChildOfUnresolvedType( UnresolvedType e, int index, string partialPredicateCall ) { - exists(int b, int bType, int bUnresolvedElement, int n | + exists(int b, int bType, int bErrorElement, int n | b = 0 and bType = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfType(e, i, _)) | i) and - bUnresolvedElement = - bType + 1 + max(int i | i = -1 or exists(getImmediateChildOfUnresolvedElement(e, i, _)) | i) and - n = bUnresolvedElement and + bErrorElement = + bType + 1 + max(int i | i = -1 or exists(getImmediateChildOfErrorElement(e, i, _)) | i) and + n = bErrorElement and ( none() or result = getImmediateChildOfType(e, index - b, partialPredicateCall) or - result = getImmediateChildOfUnresolvedElement(e, index - bType, partialPredicateCall) + result = getImmediateChildOfErrorElement(e, index - bType, partialPredicateCall) ) ) } diff --git a/swift/ql/lib/codeql/swift/generated/Raw.qll b/swift/ql/lib/codeql/swift/generated/Raw.qll index f394b699d4d..f8acf03bbdc 100644 --- a/swift/ql/lib/codeql/swift/generated/Raw.qll +++ b/swift/ql/lib/codeql/swift/generated/Raw.qll @@ -57,9 +57,9 @@ module Raw { int getKind() { diagnostics(this, _, result) } } - class UnresolvedElement extends @unresolved_element, Locatable { } + class ErrorElement extends @error_element, Locatable { } - class UnspecifiedElement extends @unspecified_element, Locatable { + class UnspecifiedElement extends @unspecified_element, ErrorElement { override string toString() { result = "UnspecifiedElement" } Element getParent() { unspecified_element_parents(this, result) } @@ -431,7 +431,7 @@ module Raw { EnumElementDecl getElement() { enum_is_case_exprs(this, _, result) } } - class ErrorExpr extends @error_expr, Expr { + class ErrorExpr extends @error_expr, Expr, ErrorElement { override string toString() { result = "ErrorExpr" } } @@ -553,7 +553,7 @@ module Raw { ConstructorDecl getConstructorDecl() { other_constructor_decl_ref_exprs(this, result) } } - class OverloadedDeclRefExpr extends @overloaded_decl_ref_expr, Expr { + class OverloadedDeclRefExpr extends @overloaded_decl_ref_expr, Expr, ErrorElement { override string toString() { result = "OverloadedDeclRefExpr" } ValueDecl getPossibleDeclaration(int index) { @@ -623,13 +623,13 @@ module Raw { TypeRepr getTypeRepr() { type_expr_type_reprs(this, result) } } - class UnresolvedDeclRefExpr extends @unresolved_decl_ref_expr, Expr, UnresolvedElement { + class UnresolvedDeclRefExpr extends @unresolved_decl_ref_expr, Expr, ErrorElement { override string toString() { result = "UnresolvedDeclRefExpr" } string getName() { unresolved_decl_ref_expr_names(this, result) } } - class UnresolvedDotExpr extends @unresolved_dot_expr, Expr, UnresolvedElement { + class UnresolvedDotExpr extends @unresolved_dot_expr, Expr, ErrorElement { override string toString() { result = "UnresolvedDotExpr" } Expr getBase() { unresolved_dot_exprs(this, result, _) } @@ -637,19 +637,19 @@ module Raw { string getName() { unresolved_dot_exprs(this, _, result) } } - class UnresolvedMemberExpr extends @unresolved_member_expr, Expr, UnresolvedElement { + class UnresolvedMemberExpr extends @unresolved_member_expr, Expr, ErrorElement { override string toString() { result = "UnresolvedMemberExpr" } string getName() { unresolved_member_exprs(this, result) } } - class UnresolvedPatternExpr extends @unresolved_pattern_expr, Expr, UnresolvedElement { + class UnresolvedPatternExpr extends @unresolved_pattern_expr, Expr, ErrorElement { override string toString() { result = "UnresolvedPatternExpr" } Pattern getSubPattern() { unresolved_pattern_exprs(this, result) } } - class UnresolvedSpecializeExpr extends @unresolved_specialize_expr, Expr, UnresolvedElement { + class UnresolvedSpecializeExpr extends @unresolved_specialize_expr, Expr, ErrorElement { override string toString() { result = "UnresolvedSpecializeExpr" } Expr getSubExpr() { unresolved_specialize_exprs(this, result) } @@ -931,12 +931,12 @@ module Raw { } class UnresolvedMemberChainResultExpr extends @unresolved_member_chain_result_expr, IdentityExpr, - UnresolvedElement { + ErrorElement { override string toString() { result = "UnresolvedMemberChainResultExpr" } } class UnresolvedTypeConversionExpr extends @unresolved_type_conversion_expr, - ImplicitConversionExpr, UnresolvedElement { + ImplicitConversionExpr, ErrorElement { override string toString() { result = "UnresolvedTypeConversionExpr" } } @@ -1292,7 +1292,7 @@ module Raw { Type getStaticSelfType() { dynamic_self_types(this, result) } } - class ErrorType extends @error_type, Type { + class ErrorType extends @error_type, Type, ErrorElement { override string toString() { result = "ErrorType" } } @@ -1378,7 +1378,7 @@ module Raw { override string toString() { result = "TypeVariableType" } } - class UnresolvedType extends @unresolved_type, Type, UnresolvedElement { + class UnresolvedType extends @unresolved_type, Type, ErrorElement { override string toString() { result = "UnresolvedType" } } diff --git a/swift/ql/lib/codeql/swift/generated/Synth.qll b/swift/ql/lib/codeql/swift/generated/Synth.qll index cf410fa74ae..ec06535bbaa 100644 --- a/swift/ql/lib/codeql/swift/generated/Synth.qll +++ b/swift/ql/lib/codeql/swift/generated/Synth.qll @@ -321,18 +321,18 @@ module Synth { class TCallable = TAbstractClosureExpr or TAbstractFunctionDecl; + class TErrorElement = + TErrorExpr or TErrorType or TOverloadedDeclRefExpr or TUnresolvedDeclRefExpr or + TUnresolvedDotExpr or TUnresolvedMemberChainResultExpr or TUnresolvedMemberExpr or + TUnresolvedPatternExpr or TUnresolvedSpecializeExpr or TUnresolvedType or + TUnresolvedTypeConversionExpr or TUnspecifiedElement; + class TFile = TDbFile or TUnknownFile; - class TLocatable = - TArgument or TAstNode or TComment or TDiagnostics or TUnresolvedElement or TUnspecifiedElement; + class TLocatable = TArgument or TAstNode or TComment or TDiagnostics or TErrorElement; class TLocation = TDbLocation or TUnknownLocation; - class TUnresolvedElement = - TUnresolvedDeclRefExpr or TUnresolvedDotExpr or TUnresolvedMemberChainResultExpr or - TUnresolvedMemberExpr or TUnresolvedPatternExpr or TUnresolvedSpecializeExpr or - TUnresolvedType or TUnresolvedTypeConversionExpr; - class TAbstractFunctionDecl = TConstructorDecl or TDestructorDecl or TFuncDecl; class TAbstractStorageDecl = TSubscriptDecl or TVarDecl; @@ -1457,6 +1457,33 @@ module Synth { result = convertTypeFromRaw(e) } + cached + TErrorElement convertErrorElementFromRaw(Raw::Element e) { + result = convertErrorExprFromRaw(e) + or + result = convertErrorTypeFromRaw(e) + or + result = convertOverloadedDeclRefExprFromRaw(e) + or + result = convertUnresolvedDeclRefExprFromRaw(e) + or + result = convertUnresolvedDotExprFromRaw(e) + or + result = convertUnresolvedMemberChainResultExprFromRaw(e) + or + result = convertUnresolvedMemberExprFromRaw(e) + or + result = convertUnresolvedPatternExprFromRaw(e) + or + result = convertUnresolvedSpecializeExprFromRaw(e) + or + result = convertUnresolvedTypeFromRaw(e) + or + result = convertUnresolvedTypeConversionExprFromRaw(e) + or + result = convertUnspecifiedElementFromRaw(e) + } + cached TFile convertFileFromRaw(Raw::Element e) { result = convertDbFileFromRaw(e) @@ -1474,9 +1501,7 @@ module Synth { or result = convertDiagnosticsFromRaw(e) or - result = convertUnresolvedElementFromRaw(e) - or - result = convertUnspecifiedElementFromRaw(e) + result = convertErrorElementFromRaw(e) } cached @@ -1486,25 +1511,6 @@ module Synth { result = convertUnknownLocationFromRaw(e) } - cached - TUnresolvedElement convertUnresolvedElementFromRaw(Raw::Element e) { - result = convertUnresolvedDeclRefExprFromRaw(e) - or - result = convertUnresolvedDotExprFromRaw(e) - or - result = convertUnresolvedMemberChainResultExprFromRaw(e) - or - result = convertUnresolvedMemberExprFromRaw(e) - or - result = convertUnresolvedPatternExprFromRaw(e) - or - result = convertUnresolvedSpecializeExprFromRaw(e) - or - result = convertUnresolvedTypeFromRaw(e) - or - result = convertUnresolvedTypeConversionExprFromRaw(e) - } - cached TAbstractFunctionDecl convertAbstractFunctionDeclFromRaw(Raw::Element e) { result = convertConstructorDeclFromRaw(e) @@ -3162,6 +3168,33 @@ module Synth { result = convertTypeToRaw(e) } + cached + Raw::Element convertErrorElementToRaw(TErrorElement e) { + result = convertErrorExprToRaw(e) + or + result = convertErrorTypeToRaw(e) + or + result = convertOverloadedDeclRefExprToRaw(e) + or + result = convertUnresolvedDeclRefExprToRaw(e) + or + result = convertUnresolvedDotExprToRaw(e) + or + result = convertUnresolvedMemberChainResultExprToRaw(e) + or + result = convertUnresolvedMemberExprToRaw(e) + or + result = convertUnresolvedPatternExprToRaw(e) + or + result = convertUnresolvedSpecializeExprToRaw(e) + or + result = convertUnresolvedTypeToRaw(e) + or + result = convertUnresolvedTypeConversionExprToRaw(e) + or + result = convertUnspecifiedElementToRaw(e) + } + cached Raw::Element convertFileToRaw(TFile e) { result = convertDbFileToRaw(e) @@ -3179,9 +3212,7 @@ module Synth { or result = convertDiagnosticsToRaw(e) or - result = convertUnresolvedElementToRaw(e) - or - result = convertUnspecifiedElementToRaw(e) + result = convertErrorElementToRaw(e) } cached @@ -3191,25 +3222,6 @@ module Synth { result = convertUnknownLocationToRaw(e) } - cached - Raw::Element convertUnresolvedElementToRaw(TUnresolvedElement e) { - result = convertUnresolvedDeclRefExprToRaw(e) - or - result = convertUnresolvedDotExprToRaw(e) - or - result = convertUnresolvedMemberChainResultExprToRaw(e) - or - result = convertUnresolvedMemberExprToRaw(e) - or - result = convertUnresolvedPatternExprToRaw(e) - or - result = convertUnresolvedSpecializeExprToRaw(e) - or - result = convertUnresolvedTypeToRaw(e) - or - result = convertUnresolvedTypeConversionExprToRaw(e) - } - cached Raw::Element convertAbstractFunctionDeclToRaw(TAbstractFunctionDecl e) { result = convertConstructorDeclToRaw(e) diff --git a/swift/ql/lib/codeql/swift/generated/UnresolvedElement.qll b/swift/ql/lib/codeql/swift/generated/UnresolvedElement.qll deleted file mode 100644 index 54f2b4005be..00000000000 --- a/swift/ql/lib/codeql/swift/generated/UnresolvedElement.qll +++ /dev/null @@ -1,8 +0,0 @@ -// generated by codegen/codegen.py -private import codeql.swift.generated.Synth -private import codeql.swift.generated.Raw -import codeql.swift.elements.Locatable - -module Generated { - class UnresolvedElement extends Synth::TUnresolvedElement, Locatable { } -} diff --git a/swift/ql/lib/codeql/swift/generated/UnspecifiedElement.qll b/swift/ql/lib/codeql/swift/generated/UnspecifiedElement.qll index 8f6efd926d8..c37d4d35515 100644 --- a/swift/ql/lib/codeql/swift/generated/UnspecifiedElement.qll +++ b/swift/ql/lib/codeql/swift/generated/UnspecifiedElement.qll @@ -2,10 +2,10 @@ private import codeql.swift.generated.Synth private import codeql.swift.generated.Raw import codeql.swift.elements.Element -import codeql.swift.elements.Locatable +import codeql.swift.elements.ErrorElement module Generated { - class UnspecifiedElement extends Synth::TUnspecifiedElement, Locatable { + class UnspecifiedElement extends Synth::TUnspecifiedElement, ErrorElement { override string getAPrimaryQlClass() { result = "UnspecifiedElement" } /** diff --git a/swift/ql/lib/codeql/swift/generated/expr/ErrorExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/ErrorExpr.qll index 0c0ab658ba4..f89f4da3e40 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/ErrorExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/ErrorExpr.qll @@ -1,10 +1,11 @@ // generated by codegen/codegen.py private import codeql.swift.generated.Synth private import codeql.swift.generated.Raw +import codeql.swift.elements.ErrorElement import codeql.swift.elements.expr.Expr module Generated { - class ErrorExpr extends Synth::TErrorExpr, Expr { + class ErrorExpr extends Synth::TErrorExpr, Expr, ErrorElement { override string getAPrimaryQlClass() { result = "ErrorExpr" } } } diff --git a/swift/ql/lib/codeql/swift/generated/expr/OverloadedDeclRefExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/OverloadedDeclRefExpr.qll index 8c9b17d8abd..8347c6daa1b 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/OverloadedDeclRefExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/OverloadedDeclRefExpr.qll @@ -1,6 +1,7 @@ // generated by codegen/codegen.py private import codeql.swift.generated.Synth private import codeql.swift.generated.Raw +import codeql.swift.elements.ErrorElement import codeql.swift.elements.expr.Expr import codeql.swift.elements.decl.ValueDecl @@ -9,7 +10,7 @@ module Generated { * An ambiguous expression that might refer to multiple declarations. This will be present only * for failing compilations. */ - class OverloadedDeclRefExpr extends Synth::TOverloadedDeclRefExpr, Expr { + class OverloadedDeclRefExpr extends Synth::TOverloadedDeclRefExpr, Expr, ErrorElement { override string getAPrimaryQlClass() { result = "OverloadedDeclRefExpr" } /** diff --git a/swift/ql/lib/codeql/swift/generated/expr/UnresolvedDeclRefExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/UnresolvedDeclRefExpr.qll index 69503e9e1d2..44714ddc239 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/UnresolvedDeclRefExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/UnresolvedDeclRefExpr.qll @@ -1,11 +1,11 @@ // generated by codegen/codegen.py private import codeql.swift.generated.Synth private import codeql.swift.generated.Raw +import codeql.swift.elements.ErrorElement import codeql.swift.elements.expr.Expr -import codeql.swift.elements.UnresolvedElement module Generated { - class UnresolvedDeclRefExpr extends Synth::TUnresolvedDeclRefExpr, Expr, UnresolvedElement { + class UnresolvedDeclRefExpr extends Synth::TUnresolvedDeclRefExpr, Expr, ErrorElement { override string getAPrimaryQlClass() { result = "UnresolvedDeclRefExpr" } /** diff --git a/swift/ql/lib/codeql/swift/generated/expr/UnresolvedDotExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/UnresolvedDotExpr.qll index c1babf745ef..94da3aa05e3 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/UnresolvedDotExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/UnresolvedDotExpr.qll @@ -1,11 +1,11 @@ // generated by codegen/codegen.py private import codeql.swift.generated.Synth private import codeql.swift.generated.Raw +import codeql.swift.elements.ErrorElement import codeql.swift.elements.expr.Expr -import codeql.swift.elements.UnresolvedElement module Generated { - class UnresolvedDotExpr extends Synth::TUnresolvedDotExpr, Expr, UnresolvedElement { + class UnresolvedDotExpr extends Synth::TUnresolvedDotExpr, Expr, ErrorElement { override string getAPrimaryQlClass() { result = "UnresolvedDotExpr" } /** diff --git a/swift/ql/lib/codeql/swift/generated/expr/UnresolvedMemberChainResultExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/UnresolvedMemberChainResultExpr.qll index d35b1436f8a..c7e62054345 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/UnresolvedMemberChainResultExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/UnresolvedMemberChainResultExpr.qll @@ -1,12 +1,12 @@ // generated by codegen/codegen.py private import codeql.swift.generated.Synth private import codeql.swift.generated.Raw +import codeql.swift.elements.ErrorElement import codeql.swift.elements.expr.IdentityExpr -import codeql.swift.elements.UnresolvedElement module Generated { class UnresolvedMemberChainResultExpr extends Synth::TUnresolvedMemberChainResultExpr, - IdentityExpr, UnresolvedElement { + IdentityExpr, ErrorElement { override string getAPrimaryQlClass() { result = "UnresolvedMemberChainResultExpr" } } } diff --git a/swift/ql/lib/codeql/swift/generated/expr/UnresolvedMemberExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/UnresolvedMemberExpr.qll index ecce559fa41..3dcb91353b7 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/UnresolvedMemberExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/UnresolvedMemberExpr.qll @@ -1,11 +1,11 @@ // generated by codegen/codegen.py private import codeql.swift.generated.Synth private import codeql.swift.generated.Raw +import codeql.swift.elements.ErrorElement import codeql.swift.elements.expr.Expr -import codeql.swift.elements.UnresolvedElement module Generated { - class UnresolvedMemberExpr extends Synth::TUnresolvedMemberExpr, Expr, UnresolvedElement { + class UnresolvedMemberExpr extends Synth::TUnresolvedMemberExpr, Expr, ErrorElement { override string getAPrimaryQlClass() { result = "UnresolvedMemberExpr" } /** diff --git a/swift/ql/lib/codeql/swift/generated/expr/UnresolvedPatternExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/UnresolvedPatternExpr.qll index 3edfc8bf084..ae7b16de2d3 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/UnresolvedPatternExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/UnresolvedPatternExpr.qll @@ -1,12 +1,12 @@ // generated by codegen/codegen.py private import codeql.swift.generated.Synth private import codeql.swift.generated.Raw +import codeql.swift.elements.ErrorElement import codeql.swift.elements.expr.Expr import codeql.swift.elements.pattern.Pattern -import codeql.swift.elements.UnresolvedElement module Generated { - class UnresolvedPatternExpr extends Synth::TUnresolvedPatternExpr, Expr, UnresolvedElement { + class UnresolvedPatternExpr extends Synth::TUnresolvedPatternExpr, Expr, ErrorElement { override string getAPrimaryQlClass() { result = "UnresolvedPatternExpr" } /** diff --git a/swift/ql/lib/codeql/swift/generated/expr/UnresolvedSpecializeExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/UnresolvedSpecializeExpr.qll index 461f41c97fb..60c7379bda8 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/UnresolvedSpecializeExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/UnresolvedSpecializeExpr.qll @@ -1,11 +1,11 @@ // generated by codegen/codegen.py private import codeql.swift.generated.Synth private import codeql.swift.generated.Raw +import codeql.swift.elements.ErrorElement import codeql.swift.elements.expr.Expr -import codeql.swift.elements.UnresolvedElement module Generated { - class UnresolvedSpecializeExpr extends Synth::TUnresolvedSpecializeExpr, Expr, UnresolvedElement { + class UnresolvedSpecializeExpr extends Synth::TUnresolvedSpecializeExpr, Expr, ErrorElement { override string getAPrimaryQlClass() { result = "UnresolvedSpecializeExpr" } /** diff --git a/swift/ql/lib/codeql/swift/generated/expr/UnresolvedTypeConversionExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/UnresolvedTypeConversionExpr.qll index 10d3fc7a0d5..c6db91fb04c 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/UnresolvedTypeConversionExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/UnresolvedTypeConversionExpr.qll @@ -1,12 +1,12 @@ // generated by codegen/codegen.py private import codeql.swift.generated.Synth private import codeql.swift.generated.Raw +import codeql.swift.elements.ErrorElement import codeql.swift.elements.expr.ImplicitConversionExpr -import codeql.swift.elements.UnresolvedElement module Generated { class UnresolvedTypeConversionExpr extends Synth::TUnresolvedTypeConversionExpr, - ImplicitConversionExpr, UnresolvedElement { + ImplicitConversionExpr, ErrorElement { override string getAPrimaryQlClass() { result = "UnresolvedTypeConversionExpr" } } } diff --git a/swift/ql/lib/codeql/swift/generated/type/ErrorType.qll b/swift/ql/lib/codeql/swift/generated/type/ErrorType.qll index d347a8dc501..1c2a276b1ae 100644 --- a/swift/ql/lib/codeql/swift/generated/type/ErrorType.qll +++ b/swift/ql/lib/codeql/swift/generated/type/ErrorType.qll @@ -1,10 +1,11 @@ // generated by codegen/codegen.py private import codeql.swift.generated.Synth private import codeql.swift.generated.Raw +import codeql.swift.elements.ErrorElement import codeql.swift.elements.type.Type module Generated { - class ErrorType extends Synth::TErrorType, Type { + class ErrorType extends Synth::TErrorType, Type, ErrorElement { override string getAPrimaryQlClass() { result = "ErrorType" } } } diff --git a/swift/ql/lib/codeql/swift/generated/type/UnresolvedType.qll b/swift/ql/lib/codeql/swift/generated/type/UnresolvedType.qll index 4cad38bace3..2e6848734bf 100644 --- a/swift/ql/lib/codeql/swift/generated/type/UnresolvedType.qll +++ b/swift/ql/lib/codeql/swift/generated/type/UnresolvedType.qll @@ -1,11 +1,11 @@ // generated by codegen/codegen.py private import codeql.swift.generated.Synth private import codeql.swift.generated.Raw +import codeql.swift.elements.ErrorElement import codeql.swift.elements.type.Type -import codeql.swift.elements.UnresolvedElement module Generated { - class UnresolvedType extends Synth::TUnresolvedType, Type, UnresolvedElement { + class UnresolvedType extends Synth::TUnresolvedType, Type, ErrorElement { override string getAPrimaryQlClass() { result = "UnresolvedType" } } } diff --git a/swift/ql/lib/swift.dbscheme b/swift/ql/lib/swift.dbscheme index ef2edbcfec7..702cbd2eef6 100644 --- a/swift/ql/lib/swift.dbscheme +++ b/swift/ql/lib/swift.dbscheme @@ -66,8 +66,7 @@ files( | @ast_node | @comment | @diagnostics -| @unresolved_element -| @unspecified_element +| @error_element ; #keyset[id] @@ -121,16 +120,11 @@ diagnostics( int kind: int ref ); -unknown_files( - unique int id: @unknown_file -); - -unknown_locations( - unique int id: @unknown_location -); - -@unresolved_element = - @unresolved_decl_ref_expr +@error_element = + @error_expr +| @error_type +| @overloaded_decl_ref_expr +| @unresolved_decl_ref_expr | @unresolved_dot_expr | @unresolved_member_chain_result_expr | @unresolved_member_expr @@ -138,8 +132,17 @@ unknown_locations( | @unresolved_specialize_expr | @unresolved_type | @unresolved_type_conversion_expr +| @unspecified_element ; +unknown_files( + unique int id: @unknown_file +); + +unknown_locations( + unique int id: @unknown_location +); + unspecified_elements( unique int id: @unspecified_element, string property: string ref, diff --git a/swift/ql/test/extractor-tests/generated/UnresolvedElement/UnresolvedElement.ql b/swift/ql/test/extractor-tests/generated/UnresolvedElement/UnresolvedElement.ql deleted file mode 100644 index 40c564990f9..00000000000 --- a/swift/ql/test/extractor-tests/generated/UnresolvedElement/UnresolvedElement.ql +++ /dev/null @@ -1,7 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements -import TestUtils - -from UnresolvedElement x -where toBeTested(x) and not x.isUnknown() -select x, x.getPrimaryQlClasses() diff --git a/swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement.ql b/swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement.ql deleted file mode 100644 index dc762fbbeb0..00000000000 --- a/swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement.ql +++ /dev/null @@ -1,11 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements -import TestUtils - -from UnspecifiedElement x, string getProperty, string getError -where - toBeTested(x) and - not x.isUnknown() and - getProperty = x.getProperty() and - getError = x.getError() -select x, "getProperty:", getProperty, "getError:", getError diff --git a/swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement_getIndex.ql b/swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement_getIndex.ql deleted file mode 100644 index d8d2a7fab69..00000000000 --- a/swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement_getIndex.ql +++ /dev/null @@ -1,7 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements -import TestUtils - -from UnspecifiedElement x -where toBeTested(x) and not x.isUnknown() -select x, x.getIndex() diff --git a/swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement_getParent.ql b/swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement_getParent.ql deleted file mode 100644 index 7cfdb4bb609..00000000000 --- a/swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement_getParent.ql +++ /dev/null @@ -1,7 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements -import TestUtils - -from UnspecifiedElement x -where toBeTested(x) and not x.isUnknown() -select x, x.getParent() diff --git a/swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr.ql b/swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr.ql deleted file mode 100644 index 628d289832b..00000000000 --- a/swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr.ql +++ /dev/null @@ -1,7 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements -import TestUtils - -from OverloadedDeclRefExpr x -where toBeTested(x) and not x.isUnknown() -select x diff --git a/swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr_getPossibleDeclaration.ql b/swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr_getPossibleDeclaration.ql deleted file mode 100644 index 88e95b436e6..00000000000 --- a/swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr_getPossibleDeclaration.ql +++ /dev/null @@ -1,7 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements -import TestUtils - -from OverloadedDeclRefExpr x, int index -where toBeTested(x) and not x.isUnknown() -select x, index, x.getPossibleDeclaration(index) diff --git a/swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr_getType.ql b/swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr_getType.ql deleted file mode 100644 index ce159bba3b3..00000000000 --- a/swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr_getType.ql +++ /dev/null @@ -1,7 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements -import TestUtils - -from OverloadedDeclRefExpr x -where toBeTested(x) and not x.isUnknown() -select x, x.getType() diff --git a/swift/ql/test/extractor-tests/generated/type/ErrorType/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/type/ErrorType/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/type/ErrorType/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/schema.py b/swift/schema.py index d180eab8738..68675d3d17c 100644 --- a/swift/schema.py +++ b/swift/schema.py @@ -33,11 +33,14 @@ class Locatable(Element): location: optional[Location] | cpp.skip | doc("location associated with this element in the code") @qltest.collapse_hierarchy -class UnresolvedElement(Locatable): +@qltest.skip +class ErrorElement(Locatable): + """The superclass of all elements indicating some kind of error.""" pass + @use_for_null -class UnspecifiedElement(Locatable): +class UnspecifiedElement(ErrorElement): parent: optional[Element] property: string index: optional[int] @@ -326,7 +329,7 @@ class EnumIsCaseExpr(Expr): element: EnumElementDecl @qltest.skip -class ErrorExpr(Expr): +class ErrorExpr(Expr, ErrorElement): pass class ExplicitCastExpr(Expr): @@ -433,20 +436,20 @@ class TupleExpr(Expr): class TypeExpr(Expr): type_repr: optional["TypeRepr"] | child -class UnresolvedDeclRefExpr(Expr, UnresolvedElement): +class UnresolvedDeclRefExpr(Expr, ErrorElement): name: optional[string] -class UnresolvedDotExpr(Expr, UnresolvedElement): +class UnresolvedDotExpr(Expr, ErrorElement): base: Expr | child name: string -class UnresolvedMemberExpr(Expr, UnresolvedElement): +class UnresolvedMemberExpr(Expr, ErrorElement): name: string -class UnresolvedPatternExpr(Expr, UnresolvedElement): +class UnresolvedPatternExpr(Expr, ErrorElement): sub_pattern: Pattern | child -class UnresolvedSpecializeExpr(Expr, UnresolvedElement): +class UnresolvedSpecializeExpr(Expr, ErrorElement): sub_expr: Expr | child class VarargExpansionExpr(Expr): @@ -594,7 +597,7 @@ class ObjectLiteralExpr(LiteralExpr): class OptionalTryExpr(AnyTryExpr): pass -class OverloadedDeclRefExpr(Expr): +class OverloadedDeclRefExpr(Expr, ErrorElement): """ An ambiguous expression that might refer to multiple declarations. This will be present only for failing compilations. @@ -640,10 +643,10 @@ class UnderlyingToOpaqueExpr(ImplicitConversionExpr): class UnevaluatedInstanceExpr(ImplicitConversionExpr): pass -class UnresolvedMemberChainResultExpr(IdentityExpr, UnresolvedElement): +class UnresolvedMemberChainResultExpr(IdentityExpr, ErrorElement): pass -class UnresolvedTypeConversionExpr(ImplicitConversionExpr, UnresolvedElement): +class UnresolvedTypeConversionExpr(ImplicitConversionExpr, ErrorElement): pass class BooleanLiteralExpr(BuiltinLiteralExpr): @@ -852,7 +855,7 @@ class DependentMemberType(Type): class DynamicSelfType(Type): static_self_type: Type -class ErrorType(Type): +class ErrorType(Type, ErrorElement): pass class ExistentialType(Type): @@ -901,7 +904,7 @@ class TupleType(Type): class TypeVariableType(Type): pass -class UnresolvedType(Type, UnresolvedElement): +class UnresolvedType(Type, ErrorElement): pass class AnyBuiltinIntegerType(BuiltinType): From 6a816ba700a972c1a1c05713cdf4ec335ee88471 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Thu, 10 Nov 2022 10:23:34 +0100 Subject: [PATCH 0154/1420] Swift: create common error test directory --- .../ql/lib/codeql/swift/elements/ErrorElement.qll | 4 ++++ .../ql/lib/codeql/swift/generated/ErrorElement.qll | 11 +++++++++++ .../ql/test/extractor-tests/errors/Errors.expected | 14 ++++++++++++++ swift/ql/test/extractor-tests/errors/Errors.ql | 5 +++++ .../errors/UnspecifiedElementParents.expected | 9 +++++++++ .../errors/UnspecifiedElementParents.ql | 5 +++++ .../overloaded.swift | 0 .../UnresolvedElement => errors}/unresolved.swift | 0 .../wrong.swift => errors/unspecified.swift} | 0 9 files changed, 48 insertions(+) create mode 100644 swift/ql/lib/codeql/swift/elements/ErrorElement.qll create mode 100644 swift/ql/lib/codeql/swift/generated/ErrorElement.qll create mode 100644 swift/ql/test/extractor-tests/errors/Errors.expected create mode 100644 swift/ql/test/extractor-tests/errors/Errors.ql create mode 100644 swift/ql/test/extractor-tests/errors/UnspecifiedElementParents.expected create mode 100644 swift/ql/test/extractor-tests/errors/UnspecifiedElementParents.ql rename swift/ql/test/extractor-tests/{generated/expr/OverloadedDeclRefExpr => errors}/overloaded.swift (100%) rename swift/ql/test/extractor-tests/{generated/UnresolvedElement => errors}/unresolved.swift (100%) rename swift/ql/test/extractor-tests/{generated/UnspecifiedElement/wrong.swift => errors/unspecified.swift} (100%) diff --git a/swift/ql/lib/codeql/swift/elements/ErrorElement.qll b/swift/ql/lib/codeql/swift/elements/ErrorElement.qll new file mode 100644 index 00000000000..75d60aef9c9 --- /dev/null +++ b/swift/ql/lib/codeql/swift/elements/ErrorElement.qll @@ -0,0 +1,4 @@ +// generated by codegen/codegen.py, remove this comment if you wish to edit this file +private import codeql.swift.generated.ErrorElement + +class ErrorElement extends Generated::ErrorElement { } diff --git a/swift/ql/lib/codeql/swift/generated/ErrorElement.qll b/swift/ql/lib/codeql/swift/generated/ErrorElement.qll new file mode 100644 index 00000000000..726669e4a97 --- /dev/null +++ b/swift/ql/lib/codeql/swift/generated/ErrorElement.qll @@ -0,0 +1,11 @@ +// generated by codegen/codegen.py +private import codeql.swift.generated.Synth +private import codeql.swift.generated.Raw +import codeql.swift.elements.Locatable + +module Generated { + /** + * The superclass of all elements indicating some kind of error. + */ + class ErrorElement extends Synth::TErrorElement, Locatable { } +} diff --git a/swift/ql/test/extractor-tests/errors/Errors.expected b/swift/ql/test/extractor-tests/errors/Errors.expected new file mode 100644 index 00000000000..a527c7b28c1 --- /dev/null +++ b/swift/ql/test/extractor-tests/errors/Errors.expected @@ -0,0 +1,14 @@ +| file://:0:0:0:0 | (no string representation) | ErrorType | +| file://:0:0:0:0 | ... .combine(_:) | UnresolvedDotExpr | +| overloaded.swift:6:5:6:5 | OverloadedDeclRefExpr | OverloadedDeclRefExpr | +| unresolved.swift:5:1:5:14 | UnresolvedSpecializeExpr | UnresolvedSpecializeExpr | +| unspecified.swift:3:1:3:23 | missing extended_type_decl from ExtensionDecl | UnspecifiedElement | +| unspecified.swift:9:9:9:9 | missing fallthrough_dest from FallthroughStmt | UnspecifiedElement | +| unspecified.swift:9:9:9:9 | missing fallthrough_source from FallthroughStmt | UnspecifiedElement | +| unspecified.swift:12:18:12:21 | missing element from EnumElementPattern | UnspecifiedElement | +| unspecified.swift:14:18:14:26 | missing element from EnumElementPattern | UnspecifiedElement | +| unspecified.swift:19:18:19:19 | missing element from EnumElementPattern | UnspecifiedElement | +| unspecified.swift:22:13:22:13 | missing fallthrough_dest from FallthroughStmt | UnspecifiedElement | +| unspecified.swift:22:13:22:13 | missing fallthrough_source from FallthroughStmt | UnspecifiedElement | +| unspecified.swift:25:16:25:16 | ErrorExpr | ErrorExpr | +| unspecified.swift:26:18:26:19 | missing element from EnumElementPattern | UnspecifiedElement | diff --git a/swift/ql/test/extractor-tests/errors/Errors.ql b/swift/ql/test/extractor-tests/errors/Errors.ql new file mode 100644 index 00000000000..3738f9a83dd --- /dev/null +++ b/swift/ql/test/extractor-tests/errors/Errors.ql @@ -0,0 +1,5 @@ +import swift + +from Locatable e +where e instanceof ErrorElement +select e, e.getPrimaryQlClasses() diff --git a/swift/ql/test/extractor-tests/errors/UnspecifiedElementParents.expected b/swift/ql/test/extractor-tests/errors/UnspecifiedElementParents.expected new file mode 100644 index 00000000000..6f603d045ce --- /dev/null +++ b/swift/ql/test/extractor-tests/errors/UnspecifiedElementParents.expected @@ -0,0 +1,9 @@ +| unspecified.swift:3:1:3:23 | extension | unspecified.swift:3:1:3:23 | missing extended_type_decl from ExtensionDecl | +| unspecified.swift:9:9:9:9 | fallthrough | unspecified.swift:9:9:9:9 | missing fallthrough_dest from FallthroughStmt | +| unspecified.swift:9:9:9:9 | fallthrough | unspecified.swift:9:9:9:9 | missing fallthrough_source from FallthroughStmt | +| unspecified.swift:12:18:12:21 | (no string representation) | unspecified.swift:12:18:12:21 | missing element from EnumElementPattern | +| unspecified.swift:14:18:14:26 | (no string representation) | unspecified.swift:14:18:14:26 | missing element from EnumElementPattern | +| unspecified.swift:19:18:19:19 | (no string representation) | unspecified.swift:19:18:19:19 | missing element from EnumElementPattern | +| unspecified.swift:22:13:22:13 | fallthrough | unspecified.swift:22:13:22:13 | missing fallthrough_dest from FallthroughStmt | +| unspecified.swift:22:13:22:13 | fallthrough | unspecified.swift:22:13:22:13 | missing fallthrough_source from FallthroughStmt | +| unspecified.swift:26:18:26:19 | (no string representation) | unspecified.swift:26:18:26:19 | missing element from EnumElementPattern | diff --git a/swift/ql/test/extractor-tests/errors/UnspecifiedElementParents.ql b/swift/ql/test/extractor-tests/errors/UnspecifiedElementParents.ql new file mode 100644 index 00000000000..4f848750b7a --- /dev/null +++ b/swift/ql/test/extractor-tests/errors/UnspecifiedElementParents.ql @@ -0,0 +1,5 @@ +import swift + +from UnspecifiedElement e, Locatable parent +where parent = e.getParent() +select parent, e diff --git a/swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/overloaded.swift b/swift/ql/test/extractor-tests/errors/overloaded.swift similarity index 100% rename from swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/overloaded.swift rename to swift/ql/test/extractor-tests/errors/overloaded.swift diff --git a/swift/ql/test/extractor-tests/generated/UnresolvedElement/unresolved.swift b/swift/ql/test/extractor-tests/errors/unresolved.swift similarity index 100% rename from swift/ql/test/extractor-tests/generated/UnresolvedElement/unresolved.swift rename to swift/ql/test/extractor-tests/errors/unresolved.swift diff --git a/swift/ql/test/extractor-tests/generated/UnspecifiedElement/wrong.swift b/swift/ql/test/extractor-tests/errors/unspecified.swift similarity index 100% rename from swift/ql/test/extractor-tests/generated/UnspecifiedElement/wrong.swift rename to swift/ql/test/extractor-tests/errors/unspecified.swift From a54853d3b4d0f3e840537954a8ec6e7fa57e062f Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Thu, 10 Nov 2022 10:37:19 +0100 Subject: [PATCH 0155/1420] Swift: cleanup orphaned test files --- .../UnresolvedElement.expected | 1 - .../UnspecifiedElement.expected | 9 ------- .../UnspecifiedElement_getIndex.expected | 0 .../UnspecifiedElement_getParent.expected | 9 ------- .../IfConfigDecl_getClause.expected | 3 --- ...duleDecl_getDefaultImportedModule.expected | 7 ------ .../BridgeToObjCExpr/bridge_to_objc.swift | 0 .../generated/expr/BridgeToObjCExpr/objc.h | 2 -- .../OverloadedDeclRefExpr.expected | 1 - ...eclRefExpr_getPossibleDeclaration.expected | 2 -- .../OverloadedDeclRefExpr_getType.expected | 0 .../NestedArchetypeType.expected | 4 ---- .../NestedArchetypeType_getProtocol.expected | 4 ---- ...NestedArchetypeType_getSuperclass.expected | 2 -- .../nested_archetypes.swift | 24 ------------------- 15 files changed, 68 deletions(-) delete mode 100644 swift/ql/test/extractor-tests/generated/UnresolvedElement/UnresolvedElement.expected delete mode 100644 swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement.expected delete mode 100644 swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement_getIndex.expected delete mode 100644 swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement_getParent.expected delete mode 100644 swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl_getClause.expected delete mode 100644 swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl_getDefaultImportedModule.expected delete mode 100644 swift/ql/test/extractor-tests/generated/expr/BridgeToObjCExpr/bridge_to_objc.swift delete mode 100644 swift/ql/test/extractor-tests/generated/expr/BridgeToObjCExpr/objc.h delete mode 100644 swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr.expected delete mode 100644 swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr_getPossibleDeclaration.expected delete mode 100644 swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr_getType.expected delete mode 100644 swift/ql/test/extractor-tests/generated/type/NestedArchetypeType/NestedArchetypeType.expected delete mode 100644 swift/ql/test/extractor-tests/generated/type/NestedArchetypeType/NestedArchetypeType_getProtocol.expected delete mode 100644 swift/ql/test/extractor-tests/generated/type/NestedArchetypeType/NestedArchetypeType_getSuperclass.expected delete mode 100644 swift/ql/test/extractor-tests/generated/type/NestedArchetypeType/nested_archetypes.swift diff --git a/swift/ql/test/extractor-tests/generated/UnresolvedElement/UnresolvedElement.expected b/swift/ql/test/extractor-tests/generated/UnresolvedElement/UnresolvedElement.expected deleted file mode 100644 index c777fe0ebb5..00000000000 --- a/swift/ql/test/extractor-tests/generated/UnresolvedElement/UnresolvedElement.expected +++ /dev/null @@ -1 +0,0 @@ -| unresolved.swift:5:1:5:14 | UnresolvedSpecializeExpr | UnresolvedSpecializeExpr | diff --git a/swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement.expected b/swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement.expected deleted file mode 100644 index 25fac0d1236..00000000000 --- a/swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement.expected +++ /dev/null @@ -1,9 +0,0 @@ -| wrong.swift:3:1:3:23 | missing extended_type_decl from ExtensionDecl | getProperty: | extended_type_decl | getError: | element was unspecified by the extractor | -| wrong.swift:9:9:9:9 | missing fallthrough_dest from FallthroughStmt | getProperty: | fallthrough_dest | getError: | element was unspecified by the extractor | -| wrong.swift:9:9:9:9 | missing fallthrough_source from FallthroughStmt | getProperty: | fallthrough_source | getError: | element was unspecified by the extractor | -| wrong.swift:12:18:12:21 | missing element from EnumElementPattern | getProperty: | element | getError: | element was unspecified by the extractor | -| wrong.swift:14:18:14:26 | missing element from EnumElementPattern | getProperty: | element | getError: | element was unspecified by the extractor | -| wrong.swift:19:18:19:19 | missing element from EnumElementPattern | getProperty: | element | getError: | element was unspecified by the extractor | -| wrong.swift:22:13:22:13 | missing fallthrough_dest from FallthroughStmt | getProperty: | fallthrough_dest | getError: | element was unspecified by the extractor | -| wrong.swift:22:13:22:13 | missing fallthrough_source from FallthroughStmt | getProperty: | fallthrough_source | getError: | element was unspecified by the extractor | -| wrong.swift:26:18:26:19 | missing element from EnumElementPattern | getProperty: | element | getError: | element was unspecified by the extractor | diff --git a/swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement_getIndex.expected b/swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement_getIndex.expected deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement_getParent.expected b/swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement_getParent.expected deleted file mode 100644 index 870a99df44f..00000000000 --- a/swift/ql/test/extractor-tests/generated/UnspecifiedElement/UnspecifiedElement_getParent.expected +++ /dev/null @@ -1,9 +0,0 @@ -| wrong.swift:3:1:3:23 | missing extended_type_decl from ExtensionDecl | extension | -| wrong.swift:9:9:9:9 | missing fallthrough_dest from FallthroughStmt | fallthrough | -| wrong.swift:9:9:9:9 | missing fallthrough_source from FallthroughStmt | fallthrough | -| wrong.swift:12:18:12:21 | missing element from EnumElementPattern | (no string representation) | -| wrong.swift:14:18:14:26 | missing element from EnumElementPattern | (no string representation) | -| wrong.swift:19:18:19:19 | missing element from EnumElementPattern | (no string representation) | -| wrong.swift:22:13:22:13 | missing fallthrough_dest from FallthroughStmt | fallthrough | -| wrong.swift:22:13:22:13 | missing fallthrough_source from FallthroughStmt | fallthrough | -| wrong.swift:26:18:26:19 | missing element from EnumElementPattern | (no string representation) | diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl_getClause.expected b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl_getClause.expected deleted file mode 100644 index d90ba458397..00000000000 --- a/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl_getClause.expected +++ /dev/null @@ -1,3 +0,0 @@ -| if_config.swift:1:1:10:1 | #if ... | 0 | if_config.swift:1:1:1:1 | IfConfigClause | -| if_config.swift:1:1:10:1 | #if ... | 1 | if_config.swift:4:1:4:1 | IfConfigClause | -| if_config.swift:1:1:10:1 | #if ... | 2 | if_config.swift:7:1:7:1 | IfConfigClause | diff --git a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl_getDefaultImportedModule.expected b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl_getDefaultImportedModule.expected deleted file mode 100644 index 7e29a9ce479..00000000000 --- a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl_getDefaultImportedModule.expected +++ /dev/null @@ -1,7 +0,0 @@ -| file://:0:0:0:0 | Foo | 0 | file://:0:0:0:0 | Swift | -| file://:0:0:0:0 | Foo | 1 | file://:0:0:0:0 | _Concurrency | -| file://:0:0:0:0 | Foo | 2 | file://:0:0:0:0 | SwiftOnoneSupport | -| file://:0:0:0:0 | __ObjC | 0 | file://:0:0:0:0 | Swift | -| file://:0:0:0:0 | default_module_name | 0 | file://:0:0:0:0 | Swift | -| file://:0:0:0:0 | default_module_name | 1 | file://:0:0:0:0 | _Concurrency | -| file://:0:0:0:0 | default_module_name | 2 | file://:0:0:0:0 | SwiftOnoneSupport | diff --git a/swift/ql/test/extractor-tests/generated/expr/BridgeToObjCExpr/bridge_to_objc.swift b/swift/ql/test/extractor-tests/generated/expr/BridgeToObjCExpr/bridge_to_objc.swift deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/swift/ql/test/extractor-tests/generated/expr/BridgeToObjCExpr/objc.h b/swift/ql/test/extractor-tests/generated/expr/BridgeToObjCExpr/objc.h deleted file mode 100644 index 588ee87cfcd..00000000000 --- a/swift/ql/test/extractor-tests/generated/expr/BridgeToObjCExpr/objc.h +++ /dev/null @@ -1,2 +0,0 @@ -@interface MyClass : NSObject -@property int foo @end diff --git a/swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr.expected b/swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr.expected deleted file mode 100644 index 055ecc345f7..00000000000 --- a/swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr.expected +++ /dev/null @@ -1 +0,0 @@ -| overloaded.swift:6:5:6:5 | OverloadedDeclRefExpr | diff --git a/swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr_getPossibleDeclaration.expected b/swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr_getPossibleDeclaration.expected deleted file mode 100644 index a78a3a4bfeb..00000000000 --- a/swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr_getPossibleDeclaration.expected +++ /dev/null @@ -1,2 +0,0 @@ -| overloaded.swift:6:5:6:5 | OverloadedDeclRefExpr | 0 | overloaded.swift:3:1:3:13 | foo() | -| overloaded.swift:6:5:6:5 | OverloadedDeclRefExpr | 1 | overloaded.swift:4:1:4:19 | foo(_:) | diff --git a/swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr_getType.expected b/swift/ql/test/extractor-tests/generated/expr/OverloadedDeclRefExpr/OverloadedDeclRefExpr_getType.expected deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/swift/ql/test/extractor-tests/generated/type/NestedArchetypeType/NestedArchetypeType.expected b/swift/ql/test/extractor-tests/generated/type/NestedArchetypeType/NestedArchetypeType.expected deleted file mode 100644 index 0575e43769c..00000000000 --- a/swift/ql/test/extractor-tests/generated/type/NestedArchetypeType/NestedArchetypeType.expected +++ /dev/null @@ -1,4 +0,0 @@ -| Impl1.Associated | getName: | Impl1.Associated | getCanonicalType: | Impl1.Associated | getInterfaceType: | Impl1.Associated | getParent: | Impl1 | getAssociatedTypeDeclaration: | nested_archetypes.swift:2:5:2:20 | Associated | -| Impl2.AssociatedWithProtocols | getName: | Impl2.AssociatedWithProtocols | getCanonicalType: | Impl2.AssociatedWithProtocols | getInterfaceType: | Impl2.AssociatedWithProtocols | getParent: | Impl2 | getAssociatedTypeDeclaration: | nested_archetypes.swift:6:5:6:57 | AssociatedWithProtocols | -| Impl3.AssociatedWithSuperclass | getName: | Impl3.AssociatedWithSuperclass | getCanonicalType: | Impl3.AssociatedWithSuperclass | getInterfaceType: | Impl3.AssociatedWithSuperclass | getParent: | Impl3 | getAssociatedTypeDeclaration: | nested_archetypes.swift:12:5:12:47 | AssociatedWithSuperclass | -| Impl4.AssociatedWithSuperclassAndProtocols | getName: | Impl4.AssociatedWithSuperclassAndProtocols | getCanonicalType: | Impl4.AssociatedWithSuperclassAndProtocols | getInterfaceType: | Impl4.AssociatedWithSuperclassAndProtocols | getParent: | Impl4 | getAssociatedTypeDeclaration: | nested_archetypes.swift:16:5:16:73 | AssociatedWithSuperclassAndProtocols | diff --git a/swift/ql/test/extractor-tests/generated/type/NestedArchetypeType/NestedArchetypeType_getProtocol.expected b/swift/ql/test/extractor-tests/generated/type/NestedArchetypeType/NestedArchetypeType_getProtocol.expected deleted file mode 100644 index 54fc57b3876..00000000000 --- a/swift/ql/test/extractor-tests/generated/type/NestedArchetypeType/NestedArchetypeType_getProtocol.expected +++ /dev/null @@ -1,4 +0,0 @@ -| Impl2.AssociatedWithProtocols | 0 | file://:0:0:0:0 | Equatable | -| Impl2.AssociatedWithProtocols | 1 | nested_archetypes.swift:1:1:3:1 | P1 | -| Impl4.AssociatedWithSuperclassAndProtocols | 0 | file://:0:0:0:0 | Equatable | -| Impl4.AssociatedWithSuperclassAndProtocols | 1 | nested_archetypes.swift:1:1:3:1 | P1 | diff --git a/swift/ql/test/extractor-tests/generated/type/NestedArchetypeType/NestedArchetypeType_getSuperclass.expected b/swift/ql/test/extractor-tests/generated/type/NestedArchetypeType/NestedArchetypeType_getSuperclass.expected deleted file mode 100644 index 9e4e38b0920..00000000000 --- a/swift/ql/test/extractor-tests/generated/type/NestedArchetypeType/NestedArchetypeType_getSuperclass.expected +++ /dev/null @@ -1,2 +0,0 @@ -| Impl3.AssociatedWithSuperclass | S | -| Impl4.AssociatedWithSuperclassAndProtocols | S | diff --git a/swift/ql/test/extractor-tests/generated/type/NestedArchetypeType/nested_archetypes.swift b/swift/ql/test/extractor-tests/generated/type/NestedArchetypeType/nested_archetypes.swift deleted file mode 100644 index f84af17e0ef..00000000000 --- a/swift/ql/test/extractor-tests/generated/type/NestedArchetypeType/nested_archetypes.swift +++ /dev/null @@ -1,24 +0,0 @@ -protocol P1 { - associatedtype Associated -} - -protocol P2 { - associatedtype AssociatedWithProtocols : Equatable, P1 -} - -class S {} - -protocol P3 { - associatedtype AssociatedWithSuperclass : S -} - -protocol P4 { - associatedtype AssociatedWithSuperclassAndProtocols : S, Equatable, P1 -} - -class A { - func foo(_: Impl1.Associated, - _: Impl2.AssociatedWithProtocols, - _: Impl3.AssociatedWithSuperclass, - _: Impl4.AssociatedWithSuperclassAndProtocols) {} -} From b91b3148a4854bf8a47169f42fce044c027fdcc2 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 10 Nov 2022 15:26:42 +0000 Subject: [PATCH 0156/1420] Ruby: add missing qldoc comments for SQL injection query --- .../ql/lib/codeql/ruby/security/SqlInjectionCustomizations.qll | 3 +++ ruby/ql/lib/codeql/ruby/security/SqlInjectionQuery.qll | 3 +++ 2 files changed, 6 insertions(+) diff --git a/ruby/ql/lib/codeql/ruby/security/SqlInjectionCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/SqlInjectionCustomizations.qll index 175d0935fad..66d3b0d4afd 100644 --- a/ruby/ql/lib/codeql/ruby/security/SqlInjectionCustomizations.qll +++ b/ruby/ql/lib/codeql/ruby/security/SqlInjectionCustomizations.qll @@ -13,10 +13,13 @@ private import codeql.ruby.dataflow.RemoteFlowSources * vulnerabilities, as well as extension points for adding your own. */ module SqlInjection { + /** A data flow source for SQL injection vulnerabilities. */ abstract class Source extends DataFlow::Node { } + /** A data flow sink for SQL injection vulnerabilities. */ abstract class Sink extends DataFlow::Node { } + /** A sanitizer for SQL injection vulnerabilities. */ abstract class Sanitizer extends DataFlow::Node { } /** diff --git a/ruby/ql/lib/codeql/ruby/security/SqlInjectionQuery.qll b/ruby/ql/lib/codeql/ruby/security/SqlInjectionQuery.qll index e2dd327ccbd..f74e919ffe5 100644 --- a/ruby/ql/lib/codeql/ruby/security/SqlInjectionQuery.qll +++ b/ruby/ql/lib/codeql/ruby/security/SqlInjectionQuery.qll @@ -7,6 +7,9 @@ private import codeql.ruby.DataFlow private import codeql.ruby.TaintTracking import SqlInjectionCustomizations::SqlInjection +/** + * A taint-tracking configuration for detecting SQL injection vulnerabilities. + */ class Configuration extends TaintTracking::Configuration { Configuration() { this = "SqlInjectionConfiguration" } From d97682991df5a8ac0b5b72c654fe5e4ea8cea767 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 1 Nov 2022 15:35:50 +0000 Subject: [PATCH 0157/1420] Swift: Add Alamofire sink for cpp/cleartext-transmission. --- .../codeql/swift/elements/expr/ApplyExpr.qll | 8 ++++++++ .../Security/CWE-311/CleartextTransmission.ql | 19 +++++++++++++++++++ .../CWE-311/CleartextTransmission.expected | 12 ++++++++++++ .../Security/CWE-311/testAlamofire.swift | 6 +++--- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/swift/ql/lib/codeql/swift/elements/expr/ApplyExpr.qll b/swift/ql/lib/codeql/swift/elements/expr/ApplyExpr.qll index 14bc6302c2b..cf11158df92 100644 --- a/swift/ql/lib/codeql/swift/elements/expr/ApplyExpr.qll +++ b/swift/ql/lib/codeql/swift/elements/expr/ApplyExpr.qll @@ -19,6 +19,14 @@ class ApplyExpr extends Generated::ApplyExpr { /** Gets the method qualifier, if this is applying a method */ Expr getQualifier() { none() } + /** + * Gets the argument of this `ApplyExpr` called `label` (if any). + */ + final Argument getArgumentWithLabel(string label) { + result = getAnArgument() and + result.getLabel() = label + } + override string toString() { result = "call to " + this.getStaticTarget().toString() or diff --git a/swift/ql/src/queries/Security/CWE-311/CleartextTransmission.ql b/swift/ql/src/queries/Security/CWE-311/CleartextTransmission.ql index 92977f5cfd5..21c1c538462 100644 --- a/swift/ql/src/queries/Security/CWE-311/CleartextTransmission.ql +++ b/swift/ql/src/queries/Security/CWE-311/CleartextTransmission.ql @@ -54,6 +54,25 @@ class Url extends Transmitted { } } +/** + * An `Expr` that transmitted through the Alamofire library. + */ +class AlamofireTransmitted extends Transmitted { + AlamofireTransmitted() { + // sinks are the first argument containing the URL, and the `parameters` + // and `headers` arguments to appropriate methods of `Session`. + exists(CallExpr call, string fName | + call.getStaticTarget().(MethodDecl).hasQualifiedName("Session", fName) and + fName.regexpMatch("(request|streamRequest|download)\\(.*") and + ( + call.getArgument(0).getExpr() = this or + call.getArgumentWithLabel("parameters").getExpr() = this or + call.getArgumentWithLabel("headers").getExpr() = this + ) + ) + } +} + /** * A taint configuration from sensitive information to expressions that are * transmitted over a network. diff --git a/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected b/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected index de02d0db461..d8328252a1c 100644 --- a/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected +++ b/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected @@ -1,4 +1,7 @@ edges +| testAlamofire.swift:150:45:150:45 | password : | testAlamofire.swift:150:13:150:45 | ... .+(_:_:) ... | +| testAlamofire.swift:152:51:152:51 | password : | testAlamofire.swift:152:19:152:51 | ... .+(_:_:) ... | +| testAlamofire.swift:154:38:154:38 | email : | testAlamofire.swift:154:14:154:46 | ... .+(_:_:) ... | | testSend.swift:41:10:41:18 | data : | testSend.swift:41:45:41:45 | data : | | testSend.swift:45:13:45:13 | password : | testSend.swift:52:27:52:27 | str1 | | testSend.swift:46:13:46:13 | password : | testSend.swift:53:27:53:27 | str2 | @@ -8,6 +11,12 @@ edges | testURL.swift:13:54:13:54 | passwd : | testURL.swift:13:22:13:54 | ... .+(_:_:) ... | | testURL.swift:16:55:16:55 | credit_card_no : | testURL.swift:16:22:16:55 | ... .+(_:_:) ... | nodes +| testAlamofire.swift:150:13:150:45 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... | +| testAlamofire.swift:150:45:150:45 | password : | semmle.label | password : | +| testAlamofire.swift:152:19:152:51 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... | +| testAlamofire.swift:152:51:152:51 | password : | semmle.label | password : | +| testAlamofire.swift:154:14:154:46 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... | +| testAlamofire.swift:154:38:154:38 | email : | semmle.label | email : | | testSend.swift:29:19:29:19 | passwordPlain | semmle.label | passwordPlain | | testSend.swift:41:10:41:18 | data : | semmle.label | data : | | testSend.swift:41:45:41:45 | data : | semmle.label | data : | @@ -26,6 +35,9 @@ nodes subpaths | testSend.swift:47:17:47:17 | password : | testSend.swift:41:10:41:18 | data : | testSend.swift:41:45:41:45 | data : | testSend.swift:47:13:47:25 | call to pad(_:) : | #select +| testAlamofire.swift:150:13:150:45 | ... .+(_:_:) ... | testAlamofire.swift:150:45:150:45 | password : | testAlamofire.swift:150:13:150:45 | ... .+(_:_:) ... | This operation transmits '... .+(_:_:) ...', which may contain unencrypted sensitive data from $@. | testAlamofire.swift:150:45:150:45 | password : | password | +| testAlamofire.swift:152:19:152:51 | ... .+(_:_:) ... | testAlamofire.swift:152:51:152:51 | password : | testAlamofire.swift:152:19:152:51 | ... .+(_:_:) ... | This operation transmits '... .+(_:_:) ...', which may contain unencrypted sensitive data from $@. | testAlamofire.swift:152:51:152:51 | password : | password | +| testAlamofire.swift:154:14:154:46 | ... .+(_:_:) ... | testAlamofire.swift:154:38:154:38 | email : | testAlamofire.swift:154:14:154:46 | ... .+(_:_:) ... | This operation transmits '... .+(_:_:) ...', which may contain unencrypted sensitive data from $@. | testAlamofire.swift:154:38:154:38 | email : | email | | testSend.swift:29:19:29:19 | passwordPlain | testSend.swift:29:19:29:19 | passwordPlain | testSend.swift:29:19:29:19 | passwordPlain | This operation transmits 'passwordPlain', which may contain unencrypted sensitive data from $@. | testSend.swift:29:19:29:19 | passwordPlain | passwordPlain | | testSend.swift:52:27:52:27 | str1 | testSend.swift:45:13:45:13 | password : | testSend.swift:52:27:52:27 | str1 | This operation transmits 'str1', which may contain unencrypted sensitive data from $@. | testSend.swift:45:13:45:13 | password : | password | | testSend.swift:53:27:53:27 | str2 | testSend.swift:46:13:46:13 | password : | testSend.swift:53:27:53:27 | str2 | This operation transmits 'str2', which may contain unencrypted sensitive data from $@. | testSend.swift:46:13:46:13 | password : | password | diff --git a/swift/ql/test/query-tests/Security/CWE-311/testAlamofire.swift b/swift/ql/test/query-tests/Security/CWE-311/testAlamofire.swift index 9056eebdbf1..3a50c2cf249 100644 --- a/swift/ql/test/query-tests/Security/CWE-311/testAlamofire.swift +++ b/swift/ql/test/query-tests/Security/CWE-311/testAlamofire.swift @@ -147,11 +147,11 @@ struct MyEncodable: Encodable { func test1(username: String, password: String, email: String, harmless: String) { // sensitive data in URL - AF.request("http://example.com/login?p=" + password) // BAD [NOT DETECTED] + AF.request("http://example.com/login?p=" + password) // BAD AF.request("http://example.com/login?h=" + harmless) // GOOD (not sensitive) - AF.streamRequest("http://example.com/login?p=" + password) // BAD [NOT DETECTED] + AF.streamRequest("http://example.com/login?p=" + password) // BAD AF.streamRequest("http://example.com/login?h=" + harmless) // GOOD (not sensitive) - AF.download("http://example.com/" + email + ".html") // BAD [NOT DETECTED] + AF.download("http://example.com/" + email + ".html") // BAD AF.download("http://example.com/" + harmless + ".html") // GOOD (not sensitive) // sensitive data in parameters From 458fb3a4a278903259d7b8ce3b1d1b64202981d5 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Thu, 10 Nov 2022 16:48:02 +0100 Subject: [PATCH 0158/1420] Swift: fix printing of unextracted entities This was still printing explicitly ignored classes. --- swift/extractor/print_unextracted/main.cpp | 30 ++++++++++---------- swift/extractor/translators/TranslatorBase.h | 30 +++++++++++++++++--- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/swift/extractor/print_unextracted/main.cpp b/swift/extractor/print_unextracted/main.cpp index 9496abd752e..09832ce992e 100644 --- a/swift/extractor/print_unextracted/main.cpp +++ b/swift/extractor/print_unextracted/main.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include "swift/extractor/translators/DeclTranslator.h" #include "swift/extractor/translators/ExprTranslator.h" @@ -8,37 +10,35 @@ using namespace codeql; -#define CHECK_CLASS(KIND, CLASS, PARENT) \ - if (!detail::HasTranslate##CLASS##KIND::value && \ - !detail::HasTranslate##PARENT::value) { \ - std::cout << " " #CLASS #KIND "\n"; \ - } - int main() { - std::cout << "Unextracted Decls:\n"; + std::map> unextracted; + +#define CHECK_CLASS(KIND, CLASS, PARENT) \ + if (KIND##Translator::getPolicyFor##CLASS##KIND() == TranslatorPolicy::emitUnknown) { \ + unextracted[#KIND].push_back(#CLASS #KIND); \ + } #define DECL(CLASS, PARENT) CHECK_CLASS(Decl, CLASS, PARENT) #include "swift/AST/DeclNodes.def" - std::cout << "\nUnextracted Stmts:\n"; - #define STMT(CLASS, PARENT) CHECK_CLASS(Stmt, CLASS, PARENT) #include "swift/AST/StmtNodes.def" - std::cout << "\nUnextracted Exprs:\n"; - #define EXPR(CLASS, PARENT) CHECK_CLASS(Expr, CLASS, PARENT) #include "swift/AST/ExprNodes.def" - std::cout << "\nUnextracted Patterns:\n"; - #define PATTERN(CLASS, PARENT) CHECK_CLASS(Pattern, CLASS, PARENT) #include "swift/AST/PatternNodes.def" - std::cout << "\nUnextracted Types:\n"; - #define TYPE(CLASS, PARENT) CHECK_CLASS(Type, CLASS, PARENT) #include "swift/AST/TypeNodes.def" + for (const auto& [kind, classes] : unextracted) { + std::cout << "Unextracted " << kind << " subclasses:\n"; + for (auto cls : classes) { + std::cout << " " << cls << '\n'; + } + std::cout << '\n'; + } return 0; } diff --git a/swift/extractor/translators/TranslatorBase.h b/swift/extractor/translators/TranslatorBase.h index 0f08442a865..0f7d4cacd3d 100644 --- a/swift/extractor/translators/TranslatorBase.h +++ b/swift/extractor/translators/TranslatorBase.h @@ -58,6 +58,13 @@ DEFINE_TRANSLATE_CHECKER(TypeRepr, , ) #include "swift/AST/TypeReprNodes.def" } // namespace detail +enum class TranslatorPolicy { + ignore, + translate, + translateParent, + emitUnknown, +}; + // we want to override the default swift visitor behaviour of chaining calls to immediate // superclasses by default and instead provide our own TBD default (using the exact type). // Moreover, if the implementation class has translate##CLASS##KIND (that uses generated C++ @@ -66,15 +73,30 @@ DEFINE_TRANSLATE_CHECKER(TypeRepr, , ) // A special case is for explicitly ignored classes marked with void, which we should never // encounter. #define DEFINE_VISIT(KIND, CLASS, PARENT) \ - void visit##CLASS##KIND(swift::CLASS##KIND* e) { \ + public: \ + static constexpr TranslatorPolicy getPolicyFor##CLASS##KIND() { \ if constexpr (std::is_same_v) { \ + return TranslatorPolicy::ignore; \ + } else if constexpr (detail::HasTranslate##CLASS##KIND::value) { \ + return TranslatorPolicy::translate; \ + } else if constexpr (detail::HasTranslate##PARENT::value) { \ + return TranslatorPolicy::translateParent; \ + } else { \ + return TranslatorPolicy::emitUnknown; \ + } \ + } \ + \ + private: \ + void visit##CLASS##KIND(swift::CLASS##KIND* e) { \ + constexpr auto policy = getPolicyFor##CLASS##KIND(); \ + if constexpr (policy == TranslatorPolicy::ignore) { \ std::cerr << "Unexpected " #CLASS #KIND "\n"; \ return; \ - } else if constexpr (detail::HasTranslate##CLASS##KIND::value) { \ + } else if constexpr (policy == TranslatorPolicy::translate) { \ dispatcher.emit(static_cast(this)->translate##CLASS##KIND(*e)); \ - } else if constexpr (detail::HasTranslate##PARENT::value) { \ + } else if constexpr (policy == TranslatorPolicy::translateParent) { \ dispatcher.emit(static_cast(this)->translate##PARENT(*e)); \ - } else { \ + } else if constexpr (policy == TranslatorPolicy::emitUnknown) { \ dispatcher.emitUnknown(e); \ } \ } From 887d1893e755c38c1f834b5e45126b707fb1c3e6 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 10 Nov 2022 15:51:02 +0000 Subject: [PATCH 0159/1420] Swift: Make ql-for-ql happy. --- swift/ql/lib/codeql/swift/elements/expr/ApplyExpr.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/ql/lib/codeql/swift/elements/expr/ApplyExpr.qll b/swift/ql/lib/codeql/swift/elements/expr/ApplyExpr.qll index cf11158df92..5c82dc32afa 100644 --- a/swift/ql/lib/codeql/swift/elements/expr/ApplyExpr.qll +++ b/swift/ql/lib/codeql/swift/elements/expr/ApplyExpr.qll @@ -23,7 +23,7 @@ class ApplyExpr extends Generated::ApplyExpr { * Gets the argument of this `ApplyExpr` called `label` (if any). */ final Argument getArgumentWithLabel(string label) { - result = getAnArgument() and + result = this.getAnArgument() and result.getLabel() = label } From 20f76e50c3e2582f87c3b6f77c5de386919a12ae Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 10 Nov 2022 15:53:04 +0000 Subject: [PATCH 0160/1420] Ruby: actually call the isPublic() predicate I added --- ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll index 2da79661c21..a586abc977c 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll @@ -48,7 +48,7 @@ module ActionCable { // database not m = getActionCableChannelBase().asModule().getAnInstanceMethod() and // and as long as it's public - m.asCallableAstNode().isPublic() and + m.isPublic() and // and is not called `subscribed` or `unsubscribed`. not m.getMethodName() = ["subscribed", "unsubscribed"] | From 04d042308b17e1e14f2308b3481b236ecb1ed7f6 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Thu, 10 Nov 2022 17:05:03 +0100 Subject: [PATCH 0161/1420] Swift: fix QL compilation --- swift/ql/lib/codeql/swift/elements/UnspecifiedElement.qll | 1 + 1 file changed, 1 insertion(+) diff --git a/swift/ql/lib/codeql/swift/elements/UnspecifiedElement.qll b/swift/ql/lib/codeql/swift/elements/UnspecifiedElement.qll index 7680fa7cca9..cbafea24532 100644 --- a/swift/ql/lib/codeql/swift/elements/UnspecifiedElement.qll +++ b/swift/ql/lib/codeql/swift/elements/UnspecifiedElement.qll @@ -1,5 +1,6 @@ private import codeql.swift.generated.UnspecifiedElement import codeql.swift.elements.Location +import codeql.swift.elements.Locatable class UnspecifiedElement extends Generated::UnspecifiedElement { override string toString() { From 88dc65cb3c0eac7cfdafd74fd50d3159b938840d Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Thu, 10 Nov 2022 17:35:32 +0100 Subject: [PATCH 0162/1420] Swift: extract or ignore last remaining types --- swift/extractor/infra/SwiftTagTraits.h | 3 ++ .../extractor/translators/TypeTranslator.cpp | 16 ++++++ swift/extractor/translators/TypeTranslator.h | 4 ++ swift/ql/lib/codeql/swift/elements.qll | 2 - .../elements/type/SequenceArchetypeType.qll | 4 -- .../type/SequenceArchetypeTypeConstructor.qll | 4 -- .../swift/elements/type/TypeVariableType.qll | 4 -- .../type/TypeVariableTypeConstructor.qll | 4 -- .../codeql/swift/generated/ParentChild.qll | 35 ------------- swift/ql/lib/codeql/swift/generated/Raw.qll | 12 ++--- swift/ql/lib/codeql/swift/generated/Synth.qll | 33 +----------- .../swift/generated/SynthConstructors.qll | 2 - .../type/ParameterizedProtocolType.qll | 52 +++++++++++++++++++ .../generated/type/SequenceArchetypeType.qll | 10 ---- .../swift/generated/type/TypeVariableType.qll | 10 ---- swift/ql/lib/swift.dbscheme | 25 +++++---- swift/ql/test/TestUtils.qll | 2 + .../MISSING_SOURCE.txt | 4 -- .../ParameterizedProtocolType.expected | 1 + .../ParameterizedProtocolType.ql | 12 +++++ .../ParameterizedProtocolType_getArg.expected | 2 + .../ParameterizedProtocolType_getArg.ql | 7 +++ .../parametrized_protocol_type.swift | 6 +++ .../SequenceArchetypeType/MISSING_SOURCE.txt | 4 -- .../type/TypeVariableType/MISSING_SOURCE.txt | 4 -- swift/schema.py | 14 ++--- 26 files changed, 132 insertions(+), 144 deletions(-) delete mode 100644 swift/ql/lib/codeql/swift/elements/type/SequenceArchetypeType.qll delete mode 100644 swift/ql/lib/codeql/swift/elements/type/SequenceArchetypeTypeConstructor.qll delete mode 100644 swift/ql/lib/codeql/swift/elements/type/TypeVariableType.qll delete mode 100644 swift/ql/lib/codeql/swift/elements/type/TypeVariableTypeConstructor.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/type/SequenceArchetypeType.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/type/TypeVariableType.qll delete mode 100644 swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/MISSING_SOURCE.txt create mode 100644 swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/ParameterizedProtocolType.expected create mode 100644 swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/ParameterizedProtocolType.ql create mode 100644 swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/ParameterizedProtocolType_getArg.expected create mode 100644 swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/ParameterizedProtocolType_getArg.ql create mode 100644 swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/parametrized_protocol_type.swift delete mode 100644 swift/ql/test/extractor-tests/generated/type/SequenceArchetypeType/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/type/TypeVariableType/MISSING_SOURCE.txt diff --git a/swift/extractor/infra/SwiftTagTraits.h b/swift/extractor/infra/SwiftTagTraits.h index 72be292f9c7..78b7bcc530e 100644 --- a/swift/extractor/infra/SwiftTagTraits.h +++ b/swift/extractor/infra/SwiftTagTraits.h @@ -25,6 +25,7 @@ using PackExprTag = void; using PackTypeTag = void; using ReifyPackExprTag = void; using PackExpansionTypeTag = void; +using SequenceArchetypeTypeTag = void; // Placeholder types appear in ambiguous types but are anyway transformed to UnresolvedType using PlaceholderTypeTag = void; // SIL types that cannot really appear in the frontend run @@ -32,6 +33,8 @@ using SILBlockStorageTypeTag = void; using SILBoxTypeTag = void; using SILFunctionTypeTag = void; using SILTokenTypeTag = void; +// This is created during type checking and is only used for constraint checking +using TypeVariableTypeTag = void; #define MAP_TYPE_TO_TAG(TYPE, TAG) \ template <> \ diff --git a/swift/extractor/translators/TypeTranslator.cpp b/swift/extractor/translators/TypeTranslator.cpp index cb8e97415b3..f86aa7f72de 100644 --- a/swift/extractor/translators/TypeTranslator.cpp +++ b/swift/extractor/translators/TypeTranslator.cpp @@ -258,4 +258,20 @@ codeql::OpaqueTypeArchetypeType TypeTranslator::translateOpaqueTypeArchetypeType entry.declaration = dispatcher.fetchLabel(type.getDecl()); return entry; } + +codeql::ErrorType TypeTranslator::translateErrorType(const swift::ErrorType& type) { + return createTypeEntry(type); +} + +codeql::UnresolvedType TypeTranslator::translateUnresolvedType(const swift::UnresolvedType& type) { + return createTypeEntry(type); +} + +codeql::ParameterizedProtocolType TypeTranslator::translateParameterizedProtocolType( + const swift::ParameterizedProtocolType& type) { + auto entry = createTypeEntry(type); + entry.base = dispatcher.fetchLabel(type.getBaseType()); + entry.args = dispatcher.fetchRepeatedLabels(type.getArgs()); + return entry; +} } // namespace codeql diff --git a/swift/extractor/translators/TypeTranslator.h b/swift/extractor/translators/TypeTranslator.h index 95ad6a8b6b9..40b9db04d49 100644 --- a/swift/extractor/translators/TypeTranslator.h +++ b/swift/extractor/translators/TypeTranslator.h @@ -71,6 +71,10 @@ class TypeTranslator : public TypeTranslatorBase { codeql::ModuleType translateModuleType(const swift::ModuleType& type); codeql::OpaqueTypeArchetypeType translateOpaqueTypeArchetypeType( const swift::OpaqueTypeArchetypeType& type); + codeql::ErrorType translateErrorType(const swift::ErrorType& type); + codeql::UnresolvedType translateUnresolvedType(const swift::UnresolvedType& type); + codeql::ParameterizedProtocolType translateParameterizedProtocolType( + const swift::ParameterizedProtocolType& type); private: void fillType(const swift::TypeBase& type, codeql::Type& entry); diff --git a/swift/ql/lib/codeql/swift/elements.qll b/swift/ql/lib/codeql/swift/elements.qll index 085736fe713..1d925d597b4 100644 --- a/swift/ql/lib/codeql/swift/elements.qll +++ b/swift/ql/lib/codeql/swift/elements.qll @@ -263,7 +263,6 @@ import codeql.swift.elements.type.PrimaryArchetypeType import codeql.swift.elements.type.ProtocolCompositionType import codeql.swift.elements.type.ProtocolType import codeql.swift.elements.type.ReferenceStorageType -import codeql.swift.elements.type.SequenceArchetypeType import codeql.swift.elements.type.StructType import codeql.swift.elements.type.SubstitutableType import codeql.swift.elements.type.SugarType @@ -272,7 +271,6 @@ import codeql.swift.elements.type.TupleType import codeql.swift.elements.type.Type import codeql.swift.elements.type.TypeAliasType import codeql.swift.elements.type.TypeRepr -import codeql.swift.elements.type.TypeVariableType import codeql.swift.elements.type.UnarySyntaxSugarType import codeql.swift.elements.type.UnboundGenericType import codeql.swift.elements.type.UnmanagedStorageType diff --git a/swift/ql/lib/codeql/swift/elements/type/SequenceArchetypeType.qll b/swift/ql/lib/codeql/swift/elements/type/SequenceArchetypeType.qll deleted file mode 100644 index be02ac063a1..00000000000 --- a/swift/ql/lib/codeql/swift/elements/type/SequenceArchetypeType.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.type.SequenceArchetypeType - -class SequenceArchetypeType extends Generated::SequenceArchetypeType { } diff --git a/swift/ql/lib/codeql/swift/elements/type/SequenceArchetypeTypeConstructor.qll b/swift/ql/lib/codeql/swift/elements/type/SequenceArchetypeTypeConstructor.qll deleted file mode 100644 index 322836a71fc..00000000000 --- a/swift/ql/lib/codeql/swift/elements/type/SequenceArchetypeTypeConstructor.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.Raw - -predicate constructSequenceArchetypeType(Raw::SequenceArchetypeType id) { any() } diff --git a/swift/ql/lib/codeql/swift/elements/type/TypeVariableType.qll b/swift/ql/lib/codeql/swift/elements/type/TypeVariableType.qll deleted file mode 100644 index 116d03f7877..00000000000 --- a/swift/ql/lib/codeql/swift/elements/type/TypeVariableType.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.type.TypeVariableType - -class TypeVariableType extends Generated::TypeVariableType { } diff --git a/swift/ql/lib/codeql/swift/elements/type/TypeVariableTypeConstructor.qll b/swift/ql/lib/codeql/swift/elements/type/TypeVariableTypeConstructor.qll deleted file mode 100644 index cf01c8fe855..00000000000 --- a/swift/ql/lib/codeql/swift/elements/type/TypeVariableTypeConstructor.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.Raw - -predicate constructTypeVariableType(Raw::TypeVariableType id) { any() } diff --git a/swift/ql/lib/codeql/swift/generated/ParentChild.qll b/swift/ql/lib/codeql/swift/generated/ParentChild.qll index b56253575e2..79b949743a1 100644 --- a/swift/ql/lib/codeql/swift/generated/ParentChild.qll +++ b/swift/ql/lib/codeql/swift/generated/ParentChild.qll @@ -3953,21 +3953,6 @@ private module Impl { ) } - private Element getImmediateChildOfTypeVariableType( - TypeVariableType e, int index, string partialPredicateCall - ) { - exists(int b, int bType, int n | - b = 0 and - bType = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfType(e, i, _)) | i) and - n = bType and - ( - none() - or - result = getImmediateChildOfType(e, index - b, partialPredicateCall) - ) - ) - } - private Element getImmediateChildOfUnresolvedType( UnresolvedType e, int index, string partialPredicateCall ) { @@ -4522,22 +4507,6 @@ private module Impl { ) } - private Element getImmediateChildOfSequenceArchetypeType( - SequenceArchetypeType e, int index, string partialPredicateCall - ) { - exists(int b, int bArchetypeType, int n | - b = 0 and - bArchetypeType = - b + 1 + max(int i | i = -1 or exists(getImmediateChildOfArchetypeType(e, i, _)) | i) and - n = bArchetypeType and - ( - none() - or - result = getImmediateChildOfArchetypeType(e, index - b, partialPredicateCall) - ) - ) - } - private Element getImmediateChildOfUnarySyntaxSugarType( UnarySyntaxSugarType e, int index, string partialPredicateCall ) { @@ -5088,8 +5057,6 @@ private module Impl { or result = getImmediateChildOfTupleType(e, index, partialAccessor) or - result = getImmediateChildOfTypeVariableType(e, index, partialAccessor) - or result = getImmediateChildOfUnresolvedType(e, index, partialAccessor) or result = getImmediateChildOfBuiltinBridgeObjectType(e, index, partialAccessor) @@ -5146,8 +5113,6 @@ private module Impl { or result = getImmediateChildOfPrimaryArchetypeType(e, index, partialAccessor) or - result = getImmediateChildOfSequenceArchetypeType(e, index, partialAccessor) - or result = getImmediateChildOfArraySliceType(e, index, partialAccessor) or result = getImmediateChildOfBoundGenericClassType(e, index, partialAccessor) diff --git a/swift/ql/lib/codeql/swift/generated/Raw.qll b/swift/ql/lib/codeql/swift/generated/Raw.qll index 2730e58e09b..a330bbec67a 100644 --- a/swift/ql/lib/codeql/swift/generated/Raw.qll +++ b/swift/ql/lib/codeql/swift/generated/Raw.qll @@ -1302,6 +1302,10 @@ module Raw { class ParameterizedProtocolType extends @parameterized_protocol_type, Type { override string toString() { result = "ParameterizedProtocolType" } + + ProtocolType getBase() { parameterized_protocol_types(this, result) } + + Type getArg(int index) { parameterized_protocol_type_args(this, index, result) } } class ProtocolCompositionType extends @protocol_composition_type, Type { @@ -1326,10 +1330,6 @@ module Raw { string getName(int index) { tuple_type_names(this, index, result) } } - class TypeVariableType extends @type_variable_type, Type { - override string toString() { result = "TypeVariableType" } - } - class UnresolvedType extends @unresolved_type, Type, UnresolvedElement { override string toString() { result = "UnresolvedType" } } @@ -1479,10 +1479,6 @@ module Raw { override string toString() { result = "PrimaryArchetypeType" } } - class SequenceArchetypeType extends @sequence_archetype_type, ArchetypeType { - override string toString() { result = "SequenceArchetypeType" } - } - class UnarySyntaxSugarType extends @unary_syntax_sugar_type, SyntaxSugarType { Type getBaseType() { unary_syntax_sugar_types(this, result) } } diff --git a/swift/ql/lib/codeql/swift/generated/Synth.qll b/swift/ql/lib/codeql/swift/generated/Synth.qll index 997b279c70f..aa00e1ba1ab 100644 --- a/swift/ql/lib/codeql/swift/generated/Synth.qll +++ b/swift/ql/lib/codeql/swift/generated/Synth.qll @@ -290,12 +290,10 @@ module Synth { constructProtocolCompositionType(id) } or TProtocolType(Raw::ProtocolType id) { constructProtocolType(id) } or - TSequenceArchetypeType(Raw::SequenceArchetypeType id) { constructSequenceArchetypeType(id) } or TStructType(Raw::StructType id) { constructStructType(id) } or TTupleType(Raw::TupleType id) { constructTupleType(id) } or TTypeAliasType(Raw::TypeAliasType id) { constructTypeAliasType(id) } or TTypeRepr(Raw::TypeRepr id) { constructTypeRepr(id) } or - TTypeVariableType(Raw::TypeVariableType id) { constructTypeVariableType(id) } or TUnboundGenericType(Raw::UnboundGenericType id) { constructUnboundGenericType(id) } or TUnmanagedStorageType(Raw::UnmanagedStorageType id) { constructUnmanagedStorageType(id) } or TUnownedStorageType(Raw::UnownedStorageType id) { constructUnownedStorageType(id) } or @@ -435,9 +433,7 @@ module Synth { class TAnyMetatypeType = TExistentialMetatypeType or TMetatypeType; - class TArchetypeType = - TOpaqueTypeArchetypeType or TOpenedArchetypeType or TPrimaryArchetypeType or - TSequenceArchetypeType; + class TArchetypeType = TOpaqueTypeArchetypeType or TOpenedArchetypeType or TPrimaryArchetypeType; class TBoundGenericType = TBoundGenericClassType or TBoundGenericEnumType or TBoundGenericStructType; @@ -464,8 +460,7 @@ module Synth { TAnyFunctionType or TAnyGenericType or TAnyMetatypeType or TBuiltinType or TDependentMemberType or TDynamicSelfType or TErrorType or TExistentialType or TInOutType or TLValueType or TModuleType or TParameterizedProtocolType or TProtocolCompositionType or - TReferenceStorageType or TSubstitutableType or TSugarType or TTupleType or - TTypeVariableType or TUnresolvedType; + TReferenceStorageType or TSubstitutableType or TSugarType or TTupleType or TUnresolvedType; class TUnarySyntaxSugarType = TArraySliceType or TOptionalType or TVariadicSequenceType; @@ -1308,11 +1303,6 @@ module Synth { cached TProtocolType convertProtocolTypeFromRaw(Raw::Element e) { result = TProtocolType(e) } - cached - TSequenceArchetypeType convertSequenceArchetypeTypeFromRaw(Raw::Element e) { - result = TSequenceArchetypeType(e) - } - cached TStructType convertStructTypeFromRaw(Raw::Element e) { result = TStructType(e) } @@ -1325,9 +1315,6 @@ module Synth { cached TTypeRepr convertTypeReprFromRaw(Raw::Element e) { result = TTypeRepr(e) } - cached - TTypeVariableType convertTypeVariableTypeFromRaw(Raw::Element e) { result = TTypeVariableType(e) } - cached TUnboundGenericType convertUnboundGenericTypeFromRaw(Raw::Element e) { result = TUnboundGenericType(e) @@ -1970,8 +1957,6 @@ module Synth { result = convertOpenedArchetypeTypeFromRaw(e) or result = convertPrimaryArchetypeTypeFromRaw(e) - or - result = convertSequenceArchetypeTypeFromRaw(e) } cached @@ -2094,8 +2079,6 @@ module Synth { or result = convertTupleTypeFromRaw(e) or - result = convertTypeVariableTypeFromRaw(e) - or result = convertUnresolvedTypeFromRaw(e) } @@ -2945,11 +2928,6 @@ module Synth { cached Raw::Element convertProtocolTypeToRaw(TProtocolType e) { e = TProtocolType(result) } - cached - Raw::Element convertSequenceArchetypeTypeToRaw(TSequenceArchetypeType e) { - e = TSequenceArchetypeType(result) - } - cached Raw::Element convertStructTypeToRaw(TStructType e) { e = TStructType(result) } @@ -2962,9 +2940,6 @@ module Synth { cached Raw::Element convertTypeReprToRaw(TTypeRepr e) { e = TTypeRepr(result) } - cached - Raw::Element convertTypeVariableTypeToRaw(TTypeVariableType e) { e = TTypeVariableType(result) } - cached Raw::Element convertUnboundGenericTypeToRaw(TUnboundGenericType e) { e = TUnboundGenericType(result) @@ -3607,8 +3582,6 @@ module Synth { result = convertOpenedArchetypeTypeToRaw(e) or result = convertPrimaryArchetypeTypeToRaw(e) - or - result = convertSequenceArchetypeTypeToRaw(e) } cached @@ -3731,8 +3704,6 @@ module Synth { or result = convertTupleTypeToRaw(e) or - result = convertTypeVariableTypeToRaw(e) - or result = convertUnresolvedTypeToRaw(e) } diff --git a/swift/ql/lib/codeql/swift/generated/SynthConstructors.qll b/swift/ql/lib/codeql/swift/generated/SynthConstructors.qll index 528c176b4c9..2009999e1d8 100644 --- a/swift/ql/lib/codeql/swift/generated/SynthConstructors.qll +++ b/swift/ql/lib/codeql/swift/generated/SynthConstructors.qll @@ -212,12 +212,10 @@ import codeql.swift.elements.type.ParenTypeConstructor import codeql.swift.elements.type.PrimaryArchetypeTypeConstructor import codeql.swift.elements.type.ProtocolCompositionTypeConstructor import codeql.swift.elements.type.ProtocolTypeConstructor -import codeql.swift.elements.type.SequenceArchetypeTypeConstructor import codeql.swift.elements.type.StructTypeConstructor import codeql.swift.elements.type.TupleTypeConstructor import codeql.swift.elements.type.TypeAliasTypeConstructor import codeql.swift.elements.type.TypeReprConstructor -import codeql.swift.elements.type.TypeVariableTypeConstructor import codeql.swift.elements.type.UnboundGenericTypeConstructor import codeql.swift.elements.type.UnmanagedStorageTypeConstructor import codeql.swift.elements.type.UnownedStorageTypeConstructor diff --git a/swift/ql/lib/codeql/swift/generated/type/ParameterizedProtocolType.qll b/swift/ql/lib/codeql/swift/generated/type/ParameterizedProtocolType.qll index 0a969fd5256..9f09046d424 100644 --- a/swift/ql/lib/codeql/swift/generated/type/ParameterizedProtocolType.qll +++ b/swift/ql/lib/codeql/swift/generated/type/ParameterizedProtocolType.qll @@ -1,10 +1,62 @@ // generated by codegen/codegen.py private import codeql.swift.generated.Synth private import codeql.swift.generated.Raw +import codeql.swift.elements.type.ProtocolType import codeql.swift.elements.type.Type module Generated { + /** + * A sugar type of the form `P` with `P` a protocol. + * + * If `P` has primary associated type `A`, then `T: P` is a shortcut for `T: P where T.A == X`. + */ class ParameterizedProtocolType extends Synth::TParameterizedProtocolType, Type { override string getAPrimaryQlClass() { result = "ParameterizedProtocolType" } + + /** + * Gets the base of this parameterized protocol type. + * + * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the + * behavior of both the `Immediate` and non-`Immediate` versions. + */ + ProtocolType getImmediateBase() { + result = + Synth::convertProtocolTypeFromRaw(Synth::convertParameterizedProtocolTypeToRaw(this) + .(Raw::ParameterizedProtocolType) + .getBase()) + } + + /** + * Gets the base of this parameterized protocol type. + */ + final ProtocolType getBase() { result = getImmediateBase().resolve() } + + /** + * Gets the `index`th argument of this parameterized protocol type (0-based). + * + * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the + * behavior of both the `Immediate` and non-`Immediate` versions. + */ + Type getImmediateArg(int index) { + result = + Synth::convertTypeFromRaw(Synth::convertParameterizedProtocolTypeToRaw(this) + .(Raw::ParameterizedProtocolType) + .getArg(index)) + } + + /** + * Gets the `index`th argument of this parameterized protocol type (0-based). + */ + final Type getArg(int index) { result = getImmediateArg(index).resolve() } + + /** + * Gets any of the arguments of this parameterized protocol type. + */ + final Type getAnArg() { result = getArg(_) } + + /** + * Gets the number of arguments of this parameterized protocol type. + */ + final int getNumberOfArgs() { result = count(getAnArg()) } } } diff --git a/swift/ql/lib/codeql/swift/generated/type/SequenceArchetypeType.qll b/swift/ql/lib/codeql/swift/generated/type/SequenceArchetypeType.qll deleted file mode 100644 index 6621b6f53fe..00000000000 --- a/swift/ql/lib/codeql/swift/generated/type/SequenceArchetypeType.qll +++ /dev/null @@ -1,10 +0,0 @@ -// generated by codegen/codegen.py -private import codeql.swift.generated.Synth -private import codeql.swift.generated.Raw -import codeql.swift.elements.type.ArchetypeType - -module Generated { - class SequenceArchetypeType extends Synth::TSequenceArchetypeType, ArchetypeType { - override string getAPrimaryQlClass() { result = "SequenceArchetypeType" } - } -} diff --git a/swift/ql/lib/codeql/swift/generated/type/TypeVariableType.qll b/swift/ql/lib/codeql/swift/generated/type/TypeVariableType.qll deleted file mode 100644 index 526e156daaf..00000000000 --- a/swift/ql/lib/codeql/swift/generated/type/TypeVariableType.qll +++ /dev/null @@ -1,10 +0,0 @@ -// generated by codegen/codegen.py -private import codeql.swift.generated.Synth -private import codeql.swift.generated.Raw -import codeql.swift.elements.type.Type - -module Generated { - class TypeVariableType extends Synth::TTypeVariableType, Type { - override string getAPrimaryQlClass() { result = "TypeVariableType" } - } -} diff --git a/swift/ql/lib/swift.dbscheme b/swift/ql/lib/swift.dbscheme index 594381ec16e..c0b01e4665b 100644 --- a/swift/ql/lib/swift.dbscheme +++ b/swift/ql/lib/swift.dbscheme @@ -1841,7 +1841,6 @@ while_stmts( //dir=stmt | @substitutable_type | @sugar_type | @tuple_type -| @type_variable_type | @unresolved_type ; @@ -1964,7 +1963,15 @@ module_types( //dir=type ); parameterized_protocol_types( //dir=type - unique int id: @parameterized_protocol_type + unique int id: @parameterized_protocol_type, + int base: @protocol_type_or_none ref +); + +#keyset[id, index] +parameterized_protocol_type_args( //dir=type + int id: @parameterized_protocol_type ref, + int index: int ref, + int arg: @type_or_none ref ); protocol_composition_types( //dir=type @@ -2019,10 +2026,6 @@ tuple_type_names( //dir=type string name: string ref ); -type_variable_types( //dir=type - unique int id: @type_variable_type -); - unresolved_types( //dir=type unique int id: @unresolved_type ); @@ -2036,7 +2039,6 @@ unresolved_types( //dir=type @opaque_type_archetype_type | @opened_archetype_type | @primary_archetype_type -| @sequence_archetype_type ; #keyset[id] @@ -2214,10 +2216,6 @@ primary_archetype_types( //dir=type unique int id: @primary_archetype_type ); -sequence_archetype_types( //dir=type - unique int id: @sequence_archetype_type -); - @unary_syntax_sugar_type = @array_slice_type | @optional_type @@ -2405,6 +2403,11 @@ variadic_sequence_types( //dir=type | @unspecified_element ; +@protocol_type_or_none = + @protocol_type +| @unspecified_element +; + @stmt_or_none = @stmt | @unspecified_element diff --git a/swift/ql/test/TestUtils.qll b/swift/ql/test/TestUtils.qll index 92fcc4de137..020e29efa7f 100644 --- a/swift/ql/test/TestUtils.qll +++ b/swift/ql/test/TestUtils.qll @@ -6,6 +6,8 @@ predicate toBeTested(Element e) { or e instanceof AppliedPropertyWrapperExpr or + e instanceof ParameterizedProtocolType + or exists(ModuleDecl m | m = e and not m.isBuiltinModule() and diff --git a/swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/ParameterizedProtocolType.expected b/swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/ParameterizedProtocolType.expected new file mode 100644 index 00000000000..cb63fdef0c4 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/ParameterizedProtocolType.expected @@ -0,0 +1 @@ +| P | getName: | P | getCanonicalType: | P | getBase: | P | diff --git a/swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/ParameterizedProtocolType.ql b/swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/ParameterizedProtocolType.ql new file mode 100644 index 00000000000..113af23a9f3 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/ParameterizedProtocolType.ql @@ -0,0 +1,12 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ParameterizedProtocolType x, string getName, Type getCanonicalType, ProtocolType getBase +where + toBeTested(x) and + not x.isUnknown() and + getName = x.getName() and + getCanonicalType = x.getCanonicalType() and + getBase = x.getBase() +select x, "getName:", getName, "getCanonicalType:", getCanonicalType, "getBase:", getBase diff --git a/swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/ParameterizedProtocolType_getArg.expected b/swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/ParameterizedProtocolType_getArg.expected new file mode 100644 index 00000000000..95799228658 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/ParameterizedProtocolType_getArg.expected @@ -0,0 +1,2 @@ +| P | 0 | Int | +| P | 1 | String | diff --git a/swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/ParameterizedProtocolType_getArg.ql b/swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/ParameterizedProtocolType_getArg.ql new file mode 100644 index 00000000000..54ef721f65b --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/ParameterizedProtocolType_getArg.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ParameterizedProtocolType x, int index +where toBeTested(x) and not x.isUnknown() +select x, index, x.getArg(index) diff --git a/swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/parametrized_protocol_type.swift b/swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/parametrized_protocol_type.swift new file mode 100644 index 00000000000..29a4727f4bc --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/ParameterizedProtocolType/parametrized_protocol_type.swift @@ -0,0 +1,6 @@ +protocol P { + associatedtype One; + associatedtype Two; +} + +func foo>(_: T) {} diff --git a/swift/ql/test/extractor-tests/generated/type/SequenceArchetypeType/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/type/SequenceArchetypeType/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/type/SequenceArchetypeType/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/type/TypeVariableType/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/type/TypeVariableType/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/type/TypeVariableType/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/schema.py b/swift/schema.py index 97c8cd143e9..b3357c5fbb5 100644 --- a/swift/schema.py +++ b/swift/schema.py @@ -868,9 +868,6 @@ class TupleType(Type): types: list[Type] names: list[optional[string]] -class TypeVariableType(Type): - pass - class UnresolvedType(Type, UnresolvedElement): pass @@ -981,9 +978,6 @@ class OpenedArchetypeType(ArchetypeType): class PrimaryArchetypeType(ArchetypeType): pass -class SequenceArchetypeType(ArchetypeType): - pass - class UnarySyntaxSugarType(SyntaxSugarType): base_type: Type @@ -1018,4 +1012,10 @@ class VariadicSequenceType(UnarySyntaxSugarType): pass class ParameterizedProtocolType(Type): - pass + """ + A sugar type of the form `P` with `P` a protocol. + + If `P` has primary associated type `A`, then `T: P` is a shortcut for `T: P where T.A == X`. + """ + base: ProtocolType + args: list[Type] From fbb2dcf7a8e8746bdee5e8f15a4bf54a96325af5 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Thu, 10 Nov 2022 17:47:14 +0100 Subject: [PATCH 0163/1420] remove langauge specific format checks --- .github/workflows/js-ml-tests.yml | 13 ------------- .github/workflows/ruby-qltest.yml | 7 ------- .github/workflows/swift.yml | 11 +---------- 3 files changed, 1 insertion(+), 30 deletions(-) diff --git a/.github/workflows/js-ml-tests.yml b/.github/workflows/js-ml-tests.yml index c932432530b..d3b5d49f2ba 100644 --- a/.github/workflows/js-ml-tests.yml +++ b/.github/workflows/js-ml-tests.yml @@ -23,19 +23,6 @@ defaults: working-directory: javascript/ql/experimental/adaptivethreatmodeling jobs: - qlformat: - name: Check QL formatting - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - uses: ./.github/actions/fetch-codeql - - - name: Check QL formatting - run: | - find . "(" -name "*.ql" -or -name "*.qll" ")" -print0 | \ - xargs -0 codeql query format --check-only - qlcompile: name: Check QL compilation runs-on: ubuntu-latest diff --git a/.github/workflows/ruby-qltest.yml b/.github/workflows/ruby-qltest.yml index 97235b722ba..125e2694fb0 100644 --- a/.github/workflows/ruby-qltest.yml +++ b/.github/workflows/ruby-qltest.yml @@ -28,13 +28,6 @@ defaults: working-directory: ruby jobs: - qlformat: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: ./.github/actions/fetch-codeql - - name: Check QL formatting - run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only qlcompile: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 873b94d2118..5ec691befb1 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -111,13 +111,4 @@ jobs: - uses: actions/upload-artifact@v3 with: name: swift-generated-cpp-files - path: swift/generated-cpp-files/** - qlformat: - runs-on: ubuntu-latest - needs: changes - if: ${{ needs.changes.outputs.ql == 'true' }} - steps: - - uses: actions/checkout@v3 - - uses: ./.github/actions/fetch-codeql - - name: Check QL formatting - run: find swift/ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only + path: swift/generated-cpp-files/** \ No newline at end of file From f5b198b8b71eab1482f5a35a55727f4d35daa703 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Thu, 10 Nov 2022 17:50:22 +0100 Subject: [PATCH 0164/1420] Swift: fix dangling test reference --- swift/ql/test/library-tests/ast/Missing.ql | 4 ++++ swift/ql/test/library-tests/ast/Missing.qlref | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 swift/ql/test/library-tests/ast/Missing.ql delete mode 100644 swift/ql/test/library-tests/ast/Missing.qlref diff --git a/swift/ql/test/library-tests/ast/Missing.ql b/swift/ql/test/library-tests/ast/Missing.ql new file mode 100644 index 00000000000..1d29f367422 --- /dev/null +++ b/swift/ql/test/library-tests/ast/Missing.ql @@ -0,0 +1,4 @@ +import swift + +from UnspecifiedElement e +select e diff --git a/swift/ql/test/library-tests/ast/Missing.qlref b/swift/ql/test/library-tests/ast/Missing.qlref deleted file mode 100644 index e9db12f988f..00000000000 --- a/swift/ql/test/library-tests/ast/Missing.qlref +++ /dev/null @@ -1 +0,0 @@ -extractor-tests/generated/UnspecifiedElement/UnspecifiedElement.ql From 866e92558c15e56d5fec10fa8eb00f0a9b60f342 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Thu, 10 Nov 2022 17:57:45 +0100 Subject: [PATCH 0165/1420] broaden the file pattern used in the format check to ensure js-ml is included --- .github/workflows/compile-queries.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/compile-queries.yml b/.github/workflows/compile-queries.yml index 73d1e5ac2cc..149cb052de9 100644 --- a/.github/workflows/compile-queries.yml +++ b/.github/workflows/compile-queries.yml @@ -46,7 +46,7 @@ jobs: with: channel: 'release' - name: check formatting - run: codeql query format */ql/{src,lib,test}/**/*.{qll,ql} --check-only + run: codeql query format */ql/**/*.{qll,ql} --check-only - name: compile queries - check-only # run with --check-only if running in a PR (github.sha != main) if : ${{ github.event_name == 'pull_request' }} From 4caaa3a396c65f75a3268d5e1405dbfe7bf9f0cf Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 24 Jun 2022 16:00:40 +0200 Subject: [PATCH 0166/1420] Python: Rewrite call-graph tests to be inline expectation (1/2) This adds inline expectations, next commit will remove old annotations code... but I thought it would be easier to review like this. --- .../CallGraph/InlineCallGraphTest.expected | 4 ++ .../CallGraph/InlineCallGraphTest.ql | 49 +++++++++++++++++++ .../CallGraph/code/class_simple.py | 10 ++-- .../CallGraph/code/runtime_decision.py | 4 +- .../library-tests/CallGraph/code/simple.py | 8 +-- .../code/underscore_prefix_func_name.py | 6 +-- 6 files changed, 67 insertions(+), 14 deletions(-) create mode 100644 python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.expected create mode 100644 python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.ql diff --git a/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.expected b/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.expected new file mode 100644 index 00000000000..2ff4aeb6865 --- /dev/null +++ b/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.expected @@ -0,0 +1,4 @@ +failures +debug_callableNotUnique +| code/class_advanced.py:18:5:18:18 | Function arg | Qualified function name 'B.arg' is not unique. Please fix. | +| code/class_advanced.py:23:5:23:25 | Function arg | Qualified function name 'B.arg' is not unique. Please fix. | diff --git a/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.ql b/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.ql new file mode 100644 index 00000000000..6b59751e43b --- /dev/null +++ b/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.ql @@ -0,0 +1,49 @@ +import python +import TestUtilities.InlineExpectationsTest + +/** Holds when `call` is resolved to `callable` using points-to based call-graph. */ +predicate pointsToCallEdge(CallNode call, Function callable) { + exists(PythonFunctionValue funcValue | + funcValue.getScope() = callable and + call = funcValue.getACall() + ) +} + +/** Holds when `call` is resolved to `callable` using type-tracking based call-graph. */ +predicate typeTrackerCallEdge(CallNode call, Function callable) { none() } + +class CallGraphTest extends InlineExpectationsTest { + CallGraphTest() { this = "CallGraphTest" } + + override string getARelevantTag() { result in ["pt", "tt"] } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + exists(location.getFile().getRelativePath()) and + exists(CallNode call, Function target | + tag = "tt" and + typeTrackerCallEdge(call, target) + or + tag = "pt" and + pointsToCallEdge(call, target) + | + location = call.getLocation() and + element = call.toString() and + ( + // note: `target.getQualifiedName` for Lambdas is just "lambda", so is not very useful :| + not target.isLambda() and + value = target.getQualifiedName() + or + target.isLambda() and + value = + "lambda[" + target.getLocation().getFile().getShortName() + ":" + + target.getLocation().getStartLine() + ":" + target.getLocation().getStartColumn() + "]" + ) + ) + } +} + +query predicate debug_callableNotUnique(Function callable, string message) { + exists(Function f | f != callable and f.getQualifiedName() = callable.getQualifiedName()) and + message = + "Qualified function name '" + callable.getQualifiedName() + "' is not unique. Please fix." +} diff --git a/python/ql/test/experimental/library-tests/CallGraph/code/class_simple.py b/python/ql/test/experimental/library-tests/CallGraph/code/class_simple.py index 7309620b3ec..a679cc5e25c 100644 --- a/python/ql/test/experimental/library-tests/CallGraph/code/class_simple.py +++ b/python/ql/test/experimental/library-tests/CallGraph/code/class_simple.py @@ -25,13 +25,13 @@ class A(object): a = A(42) # calls:A.some_method -a.some_method() +a.some_method() # $ pt=A.some_method # calls:A.some_staticmethod -a.some_staticmethod() +a.some_staticmethod() # $ pt=A.some_staticmethod # calls:A.some_classmethod -a.some_classmethod() +a.some_classmethod() # $ pt=A.some_classmethod # calls:A.some_staticmethod -A.some_staticmethod() +A.some_staticmethod() # $ pt=A.some_staticmethod # calls:A.some_classmethod -A.some_classmethod() +A.some_classmethod() # $ pt=A.some_classmethod diff --git a/python/ql/test/experimental/library-tests/CallGraph/code/runtime_decision.py b/python/ql/test/experimental/library-tests/CallGraph/code/runtime_decision.py index fd2f7773ced..a271cbd9a6f 100644 --- a/python/ql/test/experimental/library-tests/CallGraph/code/runtime_decision.py +++ b/python/ql/test/experimental/library-tests/CallGraph/code/runtime_decision.py @@ -18,7 +18,7 @@ else: func = rd_bar # calls:rd_foo calls:rd_bar -func() +func() # $ pt=rd_foo pt=rd_bar # Random doesn't work with points-to :O if random.random() < 0.5: @@ -27,4 +27,4 @@ else: func2 = rd_bar # calls:rd_foo calls:rd_bar -func2() +func2() # $ pt=rd_foo pt=rd_bar diff --git a/python/ql/test/experimental/library-tests/CallGraph/code/simple.py b/python/ql/test/experimental/library-tests/CallGraph/code/simple.py index d3c39e42fd5..210df4f209e 100644 --- a/python/ql/test/experimental/library-tests/CallGraph/code/simple.py +++ b/python/ql/test/experimental/library-tests/CallGraph/code/simple.py @@ -16,12 +16,12 @@ lam = lambda: print("lambda called") # calls:foo -foo() +foo() # $ pt=foo # calls:foo -indirect_foo() +indirect_foo() # $ pt=foo # calls:bar -bar() +bar() # $ pt=bar # calls:lam -lam() +lam() # $ pt=lambda[simple.py:15:7] # python -m trace --trackcalls simple.py diff --git a/python/ql/test/experimental/library-tests/CallGraph/code/underscore_prefix_func_name.py b/python/ql/test/experimental/library-tests/CallGraph/code/underscore_prefix_func_name.py index 1ec87efd757..1a1efe9d7b6 100644 --- a/python/ql/test/experimental/library-tests/CallGraph/code/underscore_prefix_func_name.py +++ b/python/ql/test/experimental/library-tests/CallGraph/code/underscore_prefix_func_name.py @@ -18,11 +18,11 @@ def _ignored(): def _works_since_called(): print('_works_since_called') # calls:some_function - some_function() + some_function() # $ pt=some_function def works_even_though_not_called(): # calls:some_function - some_function() + some_function() # $ pt=some_function globals()['_ignored']() -_works_since_called() +_works_since_called() # $ pt=_works_since_called From d0dfb4926bce116f96320eb4aa53134fa85e010e Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Wed, 9 Nov 2022 16:09:47 +0000 Subject: [PATCH 0167/1420] Kotlin/Java: Add compilation_info table --- java/ql/lib/config/semmlecode.dbscheme | 6 ++++++ java/ql/lib/semmle/code/java/Compilation.qll | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/java/ql/lib/config/semmlecode.dbscheme b/java/ql/lib/config/semmlecode.dbscheme index 709f1d1fd04..44d61b266be 100644 --- a/java/ql/lib/config/semmlecode.dbscheme +++ b/java/ql/lib/config/semmlecode.dbscheme @@ -31,6 +31,12 @@ compilation_started( int id : @compilation ref ) +compilation_info( + int id : @compilation ref, + string info_key: string ref, + string info_value: string ref +) + /** * The arguments that were passed to the extractor for a compiler * invocation. If `id` is for the compiler invocation diff --git a/java/ql/lib/semmle/code/java/Compilation.qll b/java/ql/lib/semmle/code/java/Compilation.qll index f38dc8ddb6b..c4b846edade 100644 --- a/java/ql/lib/semmle/code/java/Compilation.qll +++ b/java/ql/lib/semmle/code/java/Compilation.qll @@ -143,4 +143,9 @@ class Compilation extends @compilation { * Holds if the extractor encountered non-recoverable errors. */ predicate nonRecoverableErrors() { compilation_finished(this, _, _, 2) } + + /** + * Gets the piece of compilation information with the given key, if any. + */ + string getInfo(string key) { compilation_info(this, key, result) } } From 2fb78565a723fa78cefd3544062d48492e6ca5bd Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Wed, 9 Nov 2022 17:05:49 +0000 Subject: [PATCH 0168/1420] Kotlin: Write version information to the database --- java/kotlin-extractor/build.py | 4 ++++ .../src/main/kotlin/KotlinExtractorExtension.kt | 2 ++ 2 files changed, 6 insertions(+) diff --git a/java/kotlin-extractor/build.py b/java/kotlin-extractor/build.py index 9525522869b..497e6da049e 100755 --- a/java/kotlin-extractor/build.py +++ b/java/kotlin-extractor/build.py @@ -185,6 +185,10 @@ def compile(jars, java_jars, dependency_folder, transform_to_embeddable, output, include_version_folder = tmp_src_dir + '/main/kotlin/utils/versions/to_include' os.makedirs(include_version_folder) + with open(tmp_src_dir + '/main/kotlin/utils/ExtractorName.kt', 'w') as f: + f.write('package com.github.codeql\n') + f.write('val extractor_name: String = "' + output + '"\n') + parsed_current_version = kotlin_plugin_versions.version_string_to_tuple( current_version) diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt b/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt index c11d8569ae4..2ffef600476 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt @@ -131,6 +131,8 @@ class KotlinExtractorExtension( // The interceptor has already defined #compilation = * val compilation: Label = StringLabel("compilation") tw.writeCompilation_started(compilation) + tw.writeCompilation_info(compilation, "Kotlin Compiler Version", KotlinCompilerVersion.getVersion() ?: "") + tw.writeCompilation_info(compilation, "Kotlin Extractor Name", extractor_name) if (compilationStartTime != null) { tw.writeCompilation_compiler_times(compilation, -1.0, (System.currentTimeMillis()-compilationStartTime)/1000.0) } From a6b8f4b674a6215ebddec9bfdef7316dfeabe9f2 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Thu, 10 Nov 2022 11:13:13 +0000 Subject: [PATCH 0169/1420] Java/Kotlin: Update stats --- java/ql/lib/config/semmlecode.dbscheme.stats | 8805 +++++++++--------- 1 file changed, 4613 insertions(+), 4192 deletions(-) diff --git a/java/ql/lib/config/semmlecode.dbscheme.stats b/java/ql/lib/config/semmlecode.dbscheme.stats index 21f4ae2aa28..7ebc6e0b93e 100644 --- a/java/ql/lib/config/semmlecode.dbscheme.stats +++ b/java/ql/lib/config/semmlecode.dbscheme.stats @@ -6,11 +6,11 @@ @kotlincompilation - 6806 + 7683 @diagnostic - 631661 + 61632 @externalDataElement @@ -26,27 +26,27 @@ @file - 7997871 + 9158642 @folder - 1275575 + 1415670 @location_default - 430051334 + 602747369 @package - 611241 - - - @modifier - 13613 + 580098 @primitive - 12252 + 17287 + + + @modifier + 26891 @errortype @@ -54,87 +54,87 @@ @class - 12548830 + 12618104 @kt_nullable_type - 1361 + 1920 @kt_notnull_type - 191419 + 172391 @kt_type_alias - 2060 + 1821 @interface - 21501109 + 23200100 @fielddecl - 398872 + 199475 @field - 27509955 + 19350704 @constructor - 6869320 + 8006128 @method - 93308954 + 109719302 @param - 101015498 + 120852585 @exception - 1228169 + 1232644 @typevariable - 5105024 + 6288883 @wildcard - 3629331 + 3338447 @typebound - 4383514 + 4229725 @array - 1116298 + 1375332 @import - 368532 + 368550 @block - 845392 + 756817 @ifstmt - 188296 + 188285 @forstmt - 52508 + 52504 @enhancedforstmt - 18506 + 18520 @whilestmt - 21684 + 13266 @dostmt @@ -142,7 +142,7 @@ @trystmt - 58627 + 58624 @switchstmt @@ -150,19 +150,19 @@ @synchronizedstmt - 18703 + 18206 @returnstmt - 674863 + 532846 @throwstmt - 35919 + 35917 @breakstmt - 35331 + 35329 @continuestmt @@ -170,23 +170,23 @@ @emptystmt - 1562 + 1561 @exprstmt - 942161 + 942102 @assertstmt - 10816 + 10815 @localvariabledeclstmt - 318709 + 318689 @localtypedeclstmt - 4057 + 3841 @constructorinvocationstmt @@ -194,19 +194,19 @@ @superconstructorinvocationstmt - 223641 + 201354 @case - 107943 + 107945 @catchclause - 55205 + 55201 @labeledstmt - 2560 + 2342 @yieldstmt @@ -218,27 +218,27 @@ @whenbranch - 234276 + 225101 @arrayaccess - 409706 + 409681 @arraycreationexpr - 69251 + 69247 @arrayinit - 405408 + 405405 @assignexpr - 465437 + 465407 @assignaddexpr - 17017 + 17016 @assignsubexpr @@ -258,63 +258,63 @@ @assignorexpr - 14529 + 14528 @booleanliteral - 589884 + 589912 @integerliteral - 1151527 + 1151458 @longliteral - 185903 + 185904 @floatingpointliteral - 2825166 + 2824996 @doubleliteral - 486650 + 486619 @characterliteral - 40018 + 40016 @stringliteral - 1262892 + 1262818 @nullliteral - 355828 + 434113 @mulexpr - 204593 + 204580 @divexpr - 36267 + 36264 @remexpr - 3896 + 3904 @addexpr - 176997 + 176986 @subexpr - 84390 + 84385 @lshiftexpr - 8737 + 8736 @rshiftexpr @@ -326,11 +326,11 @@ @andbitexpr - 29091 + 110212 @orbitexpr - 8626 + 12590 @xorbitexpr @@ -338,47 +338,47 @@ @andlogicalexpr - 41339 + 36742 @orlogicalexpr - 31152 + 31150 @ltexpr - 66254 + 66249 @gtexpr - 17204 + 17203 @leexpr - 10530 + 10529 @geexpr - 13412 + 13411 @eqexpr - 104281 + 128429 @neexpr - 60978 + 60975 @postincexpr - 29634 + 29632 @postdecexpr - 11684 + 11683 @preincexpr - 23716 + 23714 @predecexpr @@ -386,75 +386,75 @@ @minusexpr - 744431 + 744386 @plusexpr - 53010 + 53007 @bitnotexpr - 8187 + 8186 @lognotexpr - 40113 + 40111 @castexpr - 93193 + 93187 @newexpr - 252099 + 252083 @conditionalexpr - 16048 + 16047 @instanceofexpr - 31040 + 29542 @localvariabledeclexpr - 385297 + 385272 @typeliteral - 148289 + 144350 @thisaccess - 949960 + 756915 @superaccess - 22195 + 99884 @varaccess - 2434432 + 2434277 @methodaccess - 1578817 + 1512385 @unannotatedtypeaccess - 2861937 + 2608638 @arraytypeaccess - 120735 + 120727 @wildcardtypeaccess - 63960 + 64091 @declannotation - 6745248 + 6740832 @assignremexpr @@ -462,7 +462,7 @@ @assignxorexpr - 1101 + 1102 @assignlshiftexpr @@ -490,19 +490,19 @@ @lambdaexpr - 183936 + 167982 @memberref - 23858 + 23859 @annotatedtypeaccess - 1280 + 1281 @typeannotation - 1280 + 1281 @intersectiontypeaccess @@ -518,43 +518,43 @@ @whenexpr - 172064 + 152111 @getclassexpr - 1361 + 1920 @safecastexpr - 6932 + 6402 @implicitcastexpr - 32918 + 30316 @implicitnotnullexpr - 241262 + 216630 @implicitcoerciontounitexpr - 91329 + 81396 @notinstanceofexpr - 19576 + 17306 @stmtexpr - 57687 + 55704 @stringtemplateexpr - 55814 + 59546 @notnullexpr - 20290 + 18820 @unsafecoerceexpr @@ -562,23 +562,23 @@ @valueeqexpr - 102712 + 93060 @valueneexpr - 108184 + 95639 @propertyref - 9642 + 8878 @localvar - 385297 + 385272 @module - 7964 + 7965 @requires @@ -586,7 +586,7 @@ @exports - 35011 + 35013 @opens @@ -594,7 +594,7 @@ @uses - 10785 + 10786 @provides @@ -602,15 +602,15 @@ @javadoc - 985153 + 985091 @javadocTag - 335830 + 335808 @javadocText - 2503007 + 2502848 @xmldtd @@ -618,23 +618,23 @@ @xmlelement - 106507143 + 150282022 @xmlattribute - 129551904 + 182798274 @xmlnamespace - 8168 + 11525 @xmlcomment - 107198704 + 151257816 @xmlcharacters - 101302741 + 142938589 @config @@ -650,15 +650,15 @@ @ktcomment - 133411 + 188243 @ktcommentsection - 59896 + 52611 @kt_property - 30236718 + 21895839 @@ -880,30 +880,146 @@ compilation_started - 6806 + 7683 id - 6806 + 7683 - compilation_args - 157915 + compilation_info + 15366 id - 6806 + 7683 + + + info_key + 3841 + + + info_value + 3841 + + + + + id + info_key + + + 12 + + + 2 + 3 + 7683 + + + + + + + id + info_value + + + 12 + + + 2 + 3 + 7683 + + + + + + + info_key + id + + + 12 + + + 4 + 5 + 3841 + + + + + + + info_key + info_value + + + 12 + + + 1 + 2 + 3841 + + + + + + + info_value + id + + + 12 + + + 4 + 5 + 3841 + + + + + + + info_value + info_key + + + 12 + + + 1 + 2 + 3841 + + + + + + + + + compilation_args + 169035 + + + id + 7683 num - 38117 + 48021 arg - 89848 + 90280 @@ -917,22 +1033,17 @@ 20 21 - 2722 + 3841 23 24 - 1361 + 1920 25 26 - 1361 - - - 28 - 29 - 1361 + 1920 @@ -948,22 +1059,17 @@ 20 21 - 2722 + 3841 23 24 - 1361 + 1920 25 26 - 1361 - - - 27 - 28 - 1361 + 1920 @@ -979,22 +1085,17 @@ 1 2 - 4084 + 3841 2 3 - 2722 + 5762 - 3 - 4 - 4084 - - - 5 - 6 - 27226 + 4 + 5 + 38417 @@ -1010,27 +1111,22 @@ 1 2 - 8168 + 9604 2 3 - 5445 + 17287 3 4 - 10890 + 11525 4 5 - 6806 - - - 5 - 6 - 6806 + 9604 @@ -1046,22 +1142,17 @@ 1 2 - 69428 + 61467 2 3 - 2722 + 3841 4 5 - 6806 - - - 5 - 6 - 10890 + 24971 @@ -1077,17 +1168,17 @@ 1 2 - 72151 + 67229 2 3 - 14974 + 19208 - 4 - 5 - 2722 + 3 + 4 + 3841 @@ -1097,19 +1188,19 @@ compilation_compiling_files - 61119 + 59495 id - 2337 + 2275 num - 18035 + 17556 file - 51099 + 49742 @@ -1123,22 +1214,22 @@ 1 2 - 333 + 325 2 3 - 667 + 650 35 36 - 667 + 650 54 55 - 667 + 650 @@ -1154,22 +1245,22 @@ 1 2 - 333 + 325 2 3 - 667 + 650 35 36 - 667 + 650 54 55 - 667 + 650 @@ -1185,17 +1276,17 @@ 2 3 - 6345 + 6177 4 5 - 11021 + 10728 6 8 - 667 + 650 @@ -1211,17 +1302,17 @@ 2 3 - 6345 + 6177 3 4 - 10019 + 9753 4 8 - 1669 + 1625 @@ -1237,12 +1328,12 @@ 1 2 - 41080 + 39989 2 3 - 10019 + 9753 @@ -1258,7 +1349,7 @@ 1 2 - 51099 + 49742 @@ -1268,19 +1359,19 @@ compilation_compiling_files_completed - 61119 + 59495 id - 2337 + 2275 num - 18035 + 17556 result - 667 + 325 @@ -1294,22 +1385,22 @@ 1 2 - 333 + 325 2 3 - 667 + 650 35 36 - 667 + 650 54 55 - 667 + 650 @@ -1325,12 +1416,7 @@ 1 2 - 1669 - - - 2 - 3 - 667 + 2275 @@ -1346,17 +1432,17 @@ 2 3 - 6345 + 6177 4 5 - 11021 + 10728 6 8 - 667 + 650 @@ -1372,12 +1458,7 @@ 1 2 - 17701 - - - 2 - 3 - 333 + 17556 @@ -1390,15 +1471,10 @@ 12 - - 2 - 3 - 333 - 7 8 - 333 + 325 @@ -1411,15 +1487,10 @@ 12 - - 1 - 2 - 333 - 54 55 - 333 + 325 @@ -1429,7 +1500,7 @@ compilation_time - 196462 + 196471 id @@ -1437,7 +1508,7 @@ num - 5143 + 5144 kind @@ -1445,7 +1516,7 @@ seconds - 89768 + 89772 @@ -1658,7 +1729,7 @@ 4 5 - 5143 + 5144 @@ -1793,7 +1864,7 @@ 1 2 - 89602 + 89606 52 @@ -1814,7 +1885,7 @@ 1 2 - 89602 + 89606 31 @@ -1835,7 +1906,7 @@ 1 2 - 89602 + 89606 3 @@ -1850,23 +1921,23 @@ diagnostic_for - 631661 + 61632 diagnostic - 631661 + 61632 compilation - 6806 + 574 file_number - 8168 + 14797 file_number_diagnostic_number - 59898 + 2154 @@ -1880,7 +1951,7 @@ 1 2 - 631661 + 61632 @@ -1896,7 +1967,7 @@ 1 2 - 631661 + 61632 @@ -1912,7 +1983,7 @@ 1 2 - 631661 + 61632 @@ -1926,24 +1997,24 @@ 12 - 73 - 74 - 1361 + 14 + 15 + 143 - 84 - 85 - 1361 + 28 + 29 + 143 - 100 - 101 - 2722 + 123 + 124 + 143 - 107 - 108 - 1361 + 264 + 265 + 143 @@ -1957,24 +2028,24 @@ 12 - 3 - 4 - 2722 + 7 + 8 + 143 - 4 - 5 - 1361 + 14 + 15 + 143 - 5 - 6 - 1361 + 45 + 46 + 143 - 6 - 7 - 1361 + 103 + 104 + 143 @@ -1988,29 +2059,19 @@ 12 - 29 - 30 - 1361 + 2 + 3 + 287 - 34 - 35 - 1361 + 14 + 15 + 143 - 40 - 41 - 1361 - - - 42 - 43 - 1361 - - - 44 - 45 - 1361 + 15 + 16 + 143 @@ -2019,47 +2080,6 @@ file_number diagnostic - - - 12 - - - 6 - 7 - 1361 - - - 35 - 36 - 1361 - - - 49 - 50 - 1361 - - - 101 - 102 - 1361 - - - 129 - 130 - 1361 - - - 144 - 145 - 1361 - - - - - - - file_number - compilation 12 @@ -2067,22 +2087,73 @@ 1 2 - 1361 + 143 2 3 - 1361 + 6464 3 4 - 1361 + 718 + + + 4 + 5 + 3160 5 6 - 4084 + 1292 + + + 6 + 8 + 1149 + + + 8 + 11 + 1292 + + + 12 + 21 + 574 + + + + + + + file_number + compilation + + + 12 + + + 1 + 2 + 8332 + + + 2 + 3 + 4453 + + + 3 + 4 + 1005 + + + 4 + 5 + 1005 @@ -2096,34 +2167,34 @@ 12 - 6 - 7 - 1361 + 1 + 2 + 143 - 15 + 2 + 3 + 10056 + + + 3 + 4 + 2011 + + + 4 + 5 + 1005 + + + 5 + 8 + 1149 + + + 10 16 - 1361 - - - 34 - 35 - 1361 - - - 40 - 41 - 1361 - - - 42 - 43 - 1361 - - - 44 - 45 - 1361 + 430 @@ -2138,58 +2209,53 @@ 1 - 3 - 5445 + 2 + 143 - 4 - 5 - 6806 + 2 + 3 + 574 + + + 3 + 4 + 430 5 6 - 1361 + 143 7 8 - 6806 + 143 - 8 - 9 - 4084 - - - 9 - 10 - 9529 - - - 10 - 14 - 5445 - - - 15 - 16 - 2722 - - - 16 - 17 - 8168 + 11 + 12 + 143 18 - 20 - 4084 + 19 + 143 - 20 - 22 - 5445 + 33 + 34 + 143 + + + 168 + 169 + 143 + + + 169 + 170 + 143 @@ -2204,23 +2270,18 @@ 1 - 3 - 5445 + 2 + 143 - 3 - 4 - 8168 + 2 + 3 + 1723 4 5 - 6806 - - - 5 - 6 - 39478 + 287 @@ -2235,28 +2296,53 @@ 1 + 2 + 143 + + + 2 3 - 5445 + 574 3 4 - 8168 - - - 4 - 5 - 25865 + 430 5 6 - 12252 + 143 - 6 - 7 - 8168 + 7 + 8 + 143 + + + 11 + 12 + 143 + + + 18 + 19 + 143 + + + 32 + 33 + 143 + + + 102 + 103 + 143 + + + 103 + 104 + 143 @@ -2266,19 +2352,19 @@ compilation_compiler_times - 6806 + 7683 id - 6806 + 7683 cpu_seconds - 1361 + 1920 elapsed_seconds - 6806 + 7683 @@ -2292,7 +2378,7 @@ 1 2 - 6806 + 7683 @@ -2308,7 +2394,7 @@ 1 2 - 6806 + 7683 @@ -2322,9 +2408,9 @@ 12 - 5 - 6 - 1361 + 4 + 5 + 1920 @@ -2338,9 +2424,9 @@ 12 - 5 - 6 - 1361 + 4 + 5 + 1920 @@ -2356,7 +2442,7 @@ 1 2 - 6806 + 7683 @@ -2372,7 +2458,7 @@ 1 2 - 6806 + 7683 @@ -2598,35 +2684,35 @@ diagnostics - 631661 + 61632 id - 631661 + 61632 generated_by - 1361 + 143 severity - 1361 + 143 error_tag - 1361 + 143 error_message - 96655 + 1580 full_error_message - 520031 + 40944 location - 1361 + 143 @@ -2640,7 +2726,7 @@ 1 2 - 631661 + 61632 @@ -2656,7 +2742,7 @@ 1 2 - 631661 + 61632 @@ -2672,7 +2758,7 @@ 1 2 - 631661 + 61632 @@ -2688,7 +2774,7 @@ 1 2 - 631661 + 61632 @@ -2704,7 +2790,7 @@ 1 2 - 631661 + 61632 @@ -2720,7 +2806,7 @@ 1 2 - 631661 + 61632 @@ -2734,9 +2820,9 @@ 12 - 464 - 465 - 1361 + 429 + 430 + 143 @@ -2752,7 +2838,7 @@ 1 2 - 1361 + 143 @@ -2768,7 +2854,7 @@ 1 2 - 1361 + 143 @@ -2782,9 +2868,9 @@ 12 - 71 - 72 - 1361 + 11 + 12 + 143 @@ -2798,9 +2884,9 @@ 12 - 382 - 383 - 1361 + 285 + 286 + 143 @@ -2816,7 +2902,7 @@ 1 2 - 1361 + 143 @@ -2830,9 +2916,9 @@ 12 - 464 - 465 - 1361 + 429 + 430 + 143 @@ -2848,7 +2934,7 @@ 1 2 - 1361 + 143 @@ -2864,7 +2950,7 @@ 1 2 - 1361 + 143 @@ -2878,9 +2964,9 @@ 12 - 71 - 72 - 1361 + 11 + 12 + 143 @@ -2894,9 +2980,9 @@ 12 - 382 - 383 - 1361 + 285 + 286 + 143 @@ -2912,7 +2998,7 @@ 1 2 - 1361 + 143 @@ -2926,9 +3012,9 @@ 12 - 464 - 465 - 1361 + 429 + 430 + 143 @@ -2944,7 +3030,7 @@ 1 2 - 1361 + 143 @@ -2960,7 +3046,7 @@ 1 2 - 1361 + 143 @@ -2974,9 +3060,9 @@ 12 - 71 - 72 - 1361 + 11 + 12 + 143 @@ -2990,9 +3076,9 @@ 12 - 382 - 383 - 1361 + 285 + 286 + 143 @@ -3008,7 +3094,7 @@ 1 2 - 1361 + 143 @@ -3024,62 +3110,47 @@ 1 2 - 19058 + 143 2 3 - 5445 + 143 3 4 - 12252 - - - 4 - 5 - 5445 - - - 5 - 6 - 5445 + 143 6 7 - 8168 - - - 7 - 8 - 2722 - - - 8 - 9 - 9529 + 143 9 10 - 6806 + 143 - 10 - 11 - 8168 + 12 + 13 + 287 - 14 - 17 - 6806 + 24 + 25 + 143 - 17 - 21 - 6806 + 28 + 29 + 143 + + + 166 + 167 + 287 @@ -3095,7 +3166,7 @@ 1 2 - 96655 + 1580 @@ -3111,7 +3182,7 @@ 1 2 - 96655 + 1580 @@ -3127,7 +3198,7 @@ 1 2 - 96655 + 1580 @@ -3143,57 +3214,52 @@ 1 2 - 21781 + 143 2 3 - 5445 + 143 3 4 - 12252 - - - 4 - 5 - 5445 - - - 5 - 6 - 4084 + 143 6 7 - 8168 - - - 7 - 8 - 12252 - - - 8 - 9 - 6806 + 143 9 - 11 - 8168 - - - 11 - 12 - 6806 + 10 + 143 12 - 14 - 5445 + 13 + 287 + + + 22 + 23 + 143 + + + 24 + 25 + 143 + + + 28 + 29 + 143 + + + 166 + 167 + 143 @@ -3209,7 +3275,7 @@ 1 2 - 96655 + 1580 @@ -3225,17 +3291,12 @@ 1 2 - 441074 + 38358 2 - 3 - 51730 - - - 3 - 5 - 27226 + 25 + 2585 @@ -3251,7 +3312,7 @@ 1 2 - 520031 + 40944 @@ -3267,7 +3328,7 @@ 1 2 - 520031 + 40944 @@ -3283,7 +3344,7 @@ 1 2 - 520031 + 40944 @@ -3299,7 +3360,7 @@ 1 2 - 520031 + 40944 @@ -3315,7 +3376,7 @@ 1 2 - 520031 + 40944 @@ -3329,9 +3390,9 @@ 12 - 464 - 465 - 1361 + 429 + 430 + 143 @@ -3347,7 +3408,7 @@ 1 2 - 1361 + 143 @@ -3363,7 +3424,7 @@ 1 2 - 1361 + 143 @@ -3379,7 +3440,7 @@ 1 2 - 1361 + 143 @@ -3393,9 +3454,9 @@ 12 - 71 - 72 - 1361 + 11 + 12 + 143 @@ -3409,9 +3470,9 @@ 12 - 382 - 383 - 1361 + 285 + 286 + 143 @@ -3576,11 +3637,11 @@ sourceLocationPrefix - 1361 + 1920 prefix - 1361 + 1920 @@ -4867,31 +4928,31 @@ locations_default - 430051334 + 602747369 id - 430051334 + 602747369 file - 7997871 + 9158642 beginLine - 2794830 + 3943517 beginColumn - 175612 + 247790 endLine - 2796191 + 3945438 endColumn - 619409 + 873989 @@ -4905,7 +4966,7 @@ 1 2 - 430051334 + 602747369 @@ -4921,7 +4982,7 @@ 1 2 - 430051334 + 602747369 @@ -4937,7 +4998,7 @@ 1 2 - 430051334 + 602747369 @@ -4953,7 +5014,7 @@ 1 2 - 430051334 + 602747369 @@ -4969,7 +5030,7 @@ 1 2 - 430051334 + 602747369 @@ -4985,17 +5046,17 @@ 1 2 - 7159286 + 7986919 2 - 20 - 603073 + 11 + 714558 - 20 + 11 3605 - 235511 + 457163 @@ -5011,17 +5072,17 @@ 1 2 - 7159286 + 7986919 2 - 15 - 600350 + 9 + 712637 - 15 + 9 1830 - 238234 + 459084 @@ -5037,17 +5098,17 @@ 1 2 - 7159286 + 7986919 2 - 7 - 627577 + 5 + 776025 - 7 + 5 105 - 211007 + 395696 @@ -5063,17 +5124,17 @@ 1 2 - 7159286 + 7986919 2 - 18 - 600350 + 10 + 693429 - 18 + 10 1834 - 238234 + 478293 @@ -5089,17 +5150,17 @@ 1 2 - 7159286 + 7986919 2 - 15 - 603073 + 9 + 695349 - 15 + 9 205 - 235511 + 476372 @@ -5115,67 +5176,67 @@ 1 14 - 221898 + 313099 14 125 - 215091 + 301574 125 142 - 215091 + 307336 142 152 - 223259 + 316941 152 159 - 249125 + 359200 159 - 165 - 257293 + 164 + 272761 - 165 - 170 - 225982 + 164 + 169 + 343833 - 170 - 174 - 216453 + 169 + 173 + 299653 - 174 - 179 - 228705 + 173 + 178 + 332308 - 179 - 185 - 213730 + 178 + 184 + 347674 - 185 - 194 - 221898 + 184 + 193 + 316941 - 194 - 215 - 215091 + 193 + 211 + 297732 - 215 - 5876 - 91209 + 211 + 4769 + 134459 @@ -5191,67 +5252,72 @@ 1 7 - 228705 + 322703 7 65 - 212369 + 299653 65 73 - 216453 + 307336 73 78 - 206923 + 295811 78 81 - 190587 + 265078 81 84 - 249125 + 357279 84 86 - 215091 + 299653 86 - 88 - 257293 + 87 + 188243 - 88 - 90 - 221898 + 87 + 89 + 357279 - 90 - 93 - 247763 + 89 + 91 + 259315 - 93 - 97 - 217814 + 91 + 94 + 324624 - 97 - 106 - 213730 + 94 + 99 + 328466 - 106 - 5876 - 117075 + 99 + 141 + 295811 + + + 141 + 4769 + 42258 @@ -5267,62 +5333,62 @@ 1 5 - 221898 + 313099 5 17 - 196032 + 280444 17 19 - 167444 + 251632 19 20 - 213730 + 309257 20 21 - 268183 + 403379 21 22 - 283158 + 420667 22 23 - 329444 + 476372 23 24 - 303578 + 457163 24 25 - 235511 + 339991 25 26 - 170167 + 213215 26 - 28 - 212369 + 29 + 361120 - 28 - 45 - 193310 + 29 + 40 + 117172 @@ -5338,32 +5404,32 @@ 1 2 - 902568 + 1273527 2 3 - 894400 + 1265844 3 4 - 481914 + 674220 4 5 - 211007 + 299653 5 11 - 220537 + 307336 11 97 - 84403 + 122934 @@ -5379,72 +5445,72 @@ 1 13 - 219175 + 309257 13 60 - 209646 + 307336 60 64 - 223259 + 311178 64 66 - 209646 + 311178 66 68 - 258654 + 353437 68 69 - 130688 + 194006 69 70 - 155192 + 217056 70 - 71 - 134772 + 72 + 359200 - 71 - 73 - 235511 + 72 + 74 + 322703 - 73 - 75 - 212369 + 74 + 76 + 245869 - 75 - 78 - 234150 + 76 + 79 + 330387 - 78 - 82 - 251847 + 79 + 83 + 295811 - 82 - 89 - 213730 + 83 + 91 + 316941 - 89 - 104 - 106184 + 91 + 103 + 69150 @@ -5460,67 +5526,67 @@ 1 11 - 14974 + 21129 15 - 34 - 14974 + 24 + 21129 - 36 - 58 - 13613 + 28 + 57 + 19208 - 63 - 88 - 13613 + 57 + 79 + 19208 - 89 - 132 - 13613 + 87 + 119 + 19208 - 141 - 196 - 14974 + 130 + 177 + 19208 - 210 - 285 - 13613 + 195 + 269 + 19208 - 316 - 468 - 13613 + 270 + 436 + 19208 - 496 - 853 - 13613 + 443 + 835 + 19208 - 899 - 1420 - 13613 + 844 + 1367 + 19208 - 1472 - 2256 - 13613 + 1419 + 2155 + 19208 - 2300 - 2526 - 13613 + 2252 + 2517 + 19208 - 2589 - 226687 - 8168 + 2521 + 226452 + 13445 @@ -5536,72 +5602,72 @@ 1 9 - 9529 + 17287 9 11 - 13613 + 21129 - 12 - 16 - 8168 + 11 + 15 + 15366 - 16 + 15 19 - 13613 + 21129 - 19 - 31 - 13613 + 23 + 68 + 19208 - 35 - 73 - 13613 + 69 + 78 + 19208 - 73 - 83 - 13613 + 79 + 100 + 13445 - 85 + 100 104 - 14974 + 21129 104 - 110 - 10890 + 109 + 19208 - 110 - 114 - 14974 + 109 + 112 + 13445 + + + 112 + 115 + 19208 115 - 119 - 13613 + 117 + 19208 - 119 - 121 - 12252 + 117 + 123 + 19208 - 121 - 126 - 13613 - - - 126 - 5876 - 9529 + 145 + 4769 + 9604 @@ -5617,67 +5683,67 @@ 1 10 - 14974 + 21129 10 - 29 - 13613 + 22 + 21129 - 29 - 43 - 13613 + 23 + 39 + 19208 - 45 + 41 58 - 13613 + 19208 58 - 85 - 13613 + 84 + 19208 - 86 + 84 106 - 13613 + 19208 108 - 167 - 13613 + 166 + 19208 - 171 - 227 - 13613 + 167 + 225 + 19208 - 231 - 379 - 13613 + 230 + 376 + 19208 - 383 - 650 - 13613 + 381 + 647 + 19208 - 651 - 891 - 13613 + 657 + 941 + 19208 - 940 - 1086 - 13613 + 941 + 1090 + 19208 - 1093 + 1102 2051 - 10890 + 13445 @@ -5693,67 +5759,67 @@ 1 10 - 14974 + 21129 10 - 30 - 13613 + 22 + 21129 - 30 - 43 - 13613 + 23 + 39 + 19208 - 46 + 41 59 - 13613 + 19208 - 60 - 87 - 13613 + 59 + 86 + 19208 - 87 + 86 109 - 13613 + 19208 - 115 - 173 - 13613 + 114 + 168 + 19208 - 174 - 226 - 13613 + 170 + 224 + 19208 - 230 - 380 - 13613 + 229 + 379 + 19208 - 383 - 650 - 13613 + 382 + 647 + 19208 - 653 - 892 - 13613 + 658 + 941 + 19208 - 940 - 1084 - 13613 + 941 + 1089 + 19208 - 1092 + 1102 2051 - 10890 + 13445 @@ -5769,72 +5835,67 @@ 1 8 - 14974 + 21129 8 - 17 - 13613 + 16 + 21129 - 18 - 25 - 10890 + 16 + 23 + 21129 - 25 - 30 - 13613 + 24 + 31 + 21129 - 30 - 36 - 13613 + 32 + 37 + 21129 - 36 - 44 - 13613 + 37 + 50 + 19208 - 45 - 56 - 12252 + 50 + 60 + 19208 - 57 - 64 - 13613 + 60 + 68 + 19208 - 64 - 73 - 13613 + 68 + 80 + 19208 - 73 - 86 - 13613 + 81 + 101 + 19208 - 86 - 106 - 13613 + 101 + 121 + 19208 - 107 - 129 - 13613 + 126 + 158 + 19208 - 129 - 197 - 13613 - - - 392 + 159 393 - 1361 + 7683 @@ -5850,67 +5911,67 @@ 1 14 - 220537 + 309257 14 124 - 215091 + 305416 124 143 - 217814 + 309257 143 152 - 235511 + 334228 152 159 - 223259 + 322703 159 - 165 - 257293 + 164 + 299653 - 165 - 170 - 239595 + 164 + 169 + 341912 - 170 - 174 - 221898 + 169 + 173 + 309257 - 174 - 179 - 221898 + 173 + 178 + 338070 - 179 - 185 - 211007 + 178 + 184 + 305416 - 185 - 194 - 216453 + 184 + 193 + 315020 - 194 - 217 - 212369 + 193 + 212 + 301574 - 217 - 5876 - 103461 + 212 + 4769 + 153668 @@ -5926,67 +5987,67 @@ 1 7 - 231427 + 324624 7 66 - 224621 + 318862 66 74 - 227343 + 320782 74 80 - 250486 + 355358 80 83 - 240957 + 339991 83 85 - 185142 + 268919 85 87 - 246402 + 343833 87 89 - 246402 + 355358 89 91 - 187864 + 266999 91 94 - 247763 + 345754 94 99 - 225982 + 324624 99 - 127 - 212369 + 130 + 301574 - 127 - 5876 - 69428 + 131 + 4769 + 78755 @@ -6002,32 +6063,32 @@ 1 2 - 709258 + 1000766 2 3 - 986971 + 1392620 3 4 - 642552 + 908564 4 6 - 236873 + 336149 6 - 18 - 212369 + 19 + 299653 - 18 + 19 22 - 8168 + 7683 @@ -6043,62 +6104,62 @@ 1 5 - 219175 + 309257 5 17 - 198755 + 284286 17 19 - 163360 + 232423 19 20 - 191948 + 280444 20 21 - 280436 + 420667 21 22 - 272267 + 407221 22 23 - 329444 + 482134 23 24 - 306301 + 437955 24 25 - 224621 + 334228 25 26 - 183780 + 259315 26 - 28 - 223259 + 29 + 363041 - 28 - 44 - 202839 + 29 + 39 + 134459 @@ -6114,72 +6175,72 @@ 1 13 - 221898 + 313099 13 - 61 - 250486 + 60 + 305416 - 61 + 60 64 - 172890 + 305416 64 66 - 217814 + 303495 66 68 - 250486 + 357279 68 69 - 137495 + 197848 69 70 - 137495 + 201689 70 71 - 157915 + 218977 71 73 - 231427 + 326545 73 75 - 191948 + 263157 75 77 - 183780 + 257394 77 80 - 211007 + 299653 80 85 - 227343 + 318862 85 119 - 204200 + 276603 @@ -6195,57 +6256,57 @@ 1 2 - 145663 + 205531 2 3 - 63982 + 90280 3 5 - 50369 + 71071 5 13 - 50369 + 71071 13 53 - 47646 + 67229 53 - 146 - 47646 + 138 + 67229 - 146 - 351 - 47646 + 142 + 346 + 67229 357 - 997 - 47646 + 967 + 67229 - 1053 - 2396 - 47646 + 1050 + 2386 + 67229 - 2407 - 4957 - 47646 + 2392 + 4902 + 67229 - 5022 - 5934 - 23142 + 4949 + 5933 + 32654 @@ -6261,57 +6322,57 @@ 1 2 - 151108 + 213215 2 3 - 61260 + 86438 3 5 - 53092 + 74913 5 13 - 50369 + 71071 13 42 - 47646 + 67229 42 77 - 47646 + 67229 77 - 103 - 51730 + 102 + 69150 - 103 - 116 - 47646 + 102 + 114 + 67229 - 116 - 144 - 49008 + 114 + 139 + 67229 - 144 - 181 - 47646 + 139 + 169 + 69150 - 181 - 5876 - 12252 + 173 + 4769 + 21129 @@ -6327,57 +6388,57 @@ 1 2 - 153831 + 217056 2 3 - 62621 + 88359 3 5 - 49008 + 69150 5 13 - 49008 + 69150 13 - 52 - 49008 + 50 + 67229 - 52 - 115 - 47646 + 50 + 113 + 67229 - 123 - 271 - 47646 + 114 + 266 + 67229 - 272 - 658 - 47646 + 269 + 636 + 67229 - 669 - 1200 - 47646 + 648 + 1197 + 67229 - 1219 + 1198 1635 - 47646 + 69150 1639 1722 - 17697 + 24971 @@ -6393,47 +6454,47 @@ 1 2 - 194671 + 274682 2 3 - 74873 + 105647 3 6 - 54453 + 76834 6 14 - 54453 + 76834 14 - 26 - 47646 + 25 + 74913 - 26 + 25 36 - 49008 + 71071 36 47 - 49008 + 67229 47 - 55 - 47646 + 54 + 67229 - 55 - 68 - 47646 + 54 + 65 + 59546 @@ -6449,57 +6510,57 @@ 1 2 - 153831 + 217056 2 3 - 61260 + 86438 3 5 - 49008 + 69150 5 13 - 49008 + 69150 13 - 53 - 50369 + 51 + 67229 - 53 - 115 - 47646 + 51 + 112 + 67229 - 123 - 271 - 47646 + 112 + 262 + 67229 - 280 - 656 - 47646 + 262 + 630 + 67229 - 669 - 1200 - 47646 + 637 + 1186 + 67229 - 1217 - 1638 - 47646 + 1197 + 1625 + 67229 - 1640 + 1632 1722 - 17697 + 28812 @@ -6509,15 +6570,15 @@ hasLocation - 316413492 + 340930835 locatableid - 316270551 + 340658074 id - 11518296 + 12195515 @@ -6531,12 +6592,12 @@ 1 2 - 316127611 + 340385312 2 3 - 142940 + 272761 @@ -6552,62 +6613,57 @@ 1 2 - 2084211 + 2091812 2 3 - 996500 + 1333074 3 4 - 716064 + 772184 4 6 - 984248 + 1085283 6 - 7 - 771879 + 8 + 1062233 - 7 - 9 - 1014198 + 8 + 11 + 1100650 - 9 - 12 - 848114 + 11 + 15 + 1048787 - 12 - 16 - 928433 + 15 + 21 + 964269 - 16 - 23 - 868534 + 21 + 32 + 916248 - 23 - 39 - 894400 + 32 + 64 + 920090 - 39 - 89 - 873980 - - - 89 - 9992 - 537729 + 64 + 9549 + 900881 @@ -6617,23 +6673,23 @@ numlines - 214506316 + 303305105 element_id - 214506316 + 303305105 num_lines - 431544 + 618515 num_code - 432906 + 612753 num_comment - 1342281 + 1893964 @@ -6647,7 +6703,7 @@ 1 2 - 214506316 + 303305105 @@ -6663,7 +6719,7 @@ 1 2 - 214506316 + 303305105 @@ -6679,7 +6735,7 @@ 1 2 - 214506316 + 303305105 @@ -6695,37 +6751,37 @@ 1 2 - 231427 + 320782 2 3 - 53092 + 78755 3 4 - 44924 + 61467 4 7 - 34033 + 49942 7 14 - 32672 + 48021 15 - 839 - 32672 + 194 + 48021 - 3519 - 149603 - 2722 + 320 + 149659 + 11525 @@ -6741,12 +6797,17 @@ 1 2 - 422015 + 509026 2 3 - 9529 + 69150 + + + 3 + 6 + 40337 @@ -6762,27 +6823,27 @@ 1 2 - 273629 + 380329 2 3 - 69428 + 97963 3 4 - 36756 + 51863 4 6 - 34033 + 53783 6 987 - 17697 + 34575 @@ -6798,37 +6859,37 @@ 1 2 - 231427 + 316941 2 3 - 53092 + 78755 3 4 - 44924 + 61467 4 7 - 34033 + 51863 7 - 14 - 32672 + 15 + 46100 - 15 - 468 - 32672 + 16 + 214 + 46100 - 495 + 325 78746 - 4084 + 11525 @@ -6844,12 +6905,17 @@ 1 2 - 431544 + 516710 - 7 + 2 + 3 + 51863 + + + 3 8 - 1361 + 44179 @@ -6865,27 +6931,27 @@ 1 2 - 273629 + 370725 2 3 - 69428 + 101805 3 4 - 36756 + 53783 4 6 - 34033 + 46100 6 987 - 19058 + 40337 @@ -6901,77 +6967,77 @@ 1 7 - 108907 + 153668 7 49 - 100739 + 142143 49 71 - 104823 + 147905 71 78 - 107545 + 151747 78 83 - 103461 + 145985 83 87 - 115713 + 163272 87 89 - 99377 + 140222 89 91 - 95293 + 134459 91 92 - 57176 + 80675 92 93 - 68066 + 96042 93 94 - 74873 + 105647 94 95 - 69428 + 97963 95 97 - 108907 + 151747 97 119 - 100739 + 144064 - 119 - 75115 - 27226 + 120 + 75134 + 38417 @@ -6987,22 +7053,22 @@ 1 2 - 1101323 + 1550130 2 3 - 114352 + 165193 3 - 6 - 102100 + 7 + 145985 - 6 + 7 120 - 24504 + 32654 @@ -7018,22 +7084,22 @@ 1 2 - 1101323 + 1550130 2 3 - 114352 + 165193 3 - 6 - 100739 + 7 + 145985 - 6 - 121 - 25865 + 7 + 122 + 32654 @@ -7043,15 +7109,15 @@ files - 7997871 + 9158642 id - 7997871 + 9158642 name - 7997871 + 9158642 @@ -7065,7 +7131,7 @@ 1 2 - 7997871 + 9158642 @@ -7081,7 +7147,7 @@ 1 2 - 7997871 + 9158642 @@ -7091,15 +7157,15 @@ folders - 1275575 + 1415670 id - 1275575 + 1415670 name - 1275575 + 1415670 @@ -7113,7 +7179,7 @@ 1 2 - 1275575 + 1415670 @@ -7129,7 +7195,7 @@ 1 2 - 1275575 + 1415670 @@ -7139,15 +7205,15 @@ containerparent - 9270724 + 10570471 parent - 1316415 + 1463692 child - 9270724 + 10570471 @@ -7161,37 +7227,32 @@ 1 2 - 710619 + 843255 2 3 - 140218 + 149826 3 - 4 - 78957 + 5 + 128697 - 4 - 7 - 112991 + 5 + 11 + 124855 - 7 - 14 - 111629 + 11 + 21 + 113330 - 14 - 29 - 102100 - - - 29 + 21 194 - 59898 + 103726 @@ -7207,7 +7268,7 @@ 1 2 - 9270724 + 10570471 @@ -7217,15 +7278,15 @@ cupackage - 7140227 + 7979236 id - 7140227 + 7979236 packageid - 609880 + 576256 @@ -7239,7 +7300,7 @@ 1 2 - 7140227 + 7979236 @@ -7255,52 +7316,52 @@ 1 2 - 148386 + 121013 2 3 - 80319 + 80675 3 4 - 55814 + 42258 4 - 5 - 42201 + 6 + 49942 - 5 - 7 - 46285 + 6 + 9 + 49942 - 7 - 10 - 50369 + 9 + 12 + 51863 - 10 - 15 - 50369 + 12 + 17 + 48021 - 15 - 21 - 49008 + 17 + 23 + 46100 - 21 - 36 - 47646 + 24 + 43 + 44179 - 39 + 43 187 - 39478 + 42258 @@ -7310,19 +7371,19 @@ jarManifestMain - 172733 + 172742 fileid - 13274 + 13275 keyName - 12610 + 12611 value - 89768 + 89772 @@ -7407,7 +7468,7 @@ 5 6 - 2488 + 2489 6 @@ -7473,7 +7534,7 @@ 1 2 - 5143 + 5144 2 @@ -7580,7 +7641,7 @@ 1 2 - 75996 + 75999 2 @@ -7611,7 +7672,7 @@ 1 2 - 75664 + 75668 2 @@ -7621,7 +7682,7 @@ 3 6 - 5309 + 5310 @@ -7844,7 +7905,7 @@ 1 2 - 30112 + 30113 2 @@ -7886,7 +7947,7 @@ 1 2 - 30124 + 30125 3 @@ -8005,7 +8066,7 @@ 1 2 - 30161 + 30162 2 @@ -8026,7 +8087,7 @@ 1 2 - 30161 + 30162 11 @@ -8062,15 +8123,15 @@ packages - 611241 + 580098 id - 611241 + 580098 nodeName - 611241 + 580098 @@ -8084,7 +8145,7 @@ 1 2 - 611241 + 580098 @@ -8100,7 +8161,7 @@ 1 2 - 611241 + 580098 @@ -8110,15 +8171,15 @@ primitives - 12252 + 17287 id - 12252 + 17287 nodeName - 12252 + 17287 @@ -8132,7 +8193,7 @@ 1 2 - 12252 + 17287 @@ -8148,7 +8209,7 @@ 1 2 - 12252 + 17287 @@ -8158,15 +8219,15 @@ modifiers - 13613 + 26891 id - 13613 + 26891 nodeName - 13613 + 26891 @@ -8180,7 +8241,7 @@ 1 2 - 13613 + 26891 @@ -8196,7 +8257,7 @@ 1 2 - 13613 + 26891 @@ -8217,23 +8278,23 @@ classes - 12548830 + 12618104 id - 12548830 + 12618104 nodeName - 6855707 + 6732600 parentid - 442435 + 455242 sourceid - 4506034 + 5253541 @@ -8247,7 +8308,7 @@ 1 2 - 12548830 + 12618104 @@ -8263,7 +8324,7 @@ 1 2 - 12548830 + 12618104 @@ -8279,7 +8340,7 @@ 1 2 - 12548830 + 12618104 @@ -8295,17 +8356,17 @@ 1 2 - 5728517 + 5601216 2 3 - 741930 + 726083 3 - 236 - 385259 + 204 + 405300 @@ -8321,17 +8382,17 @@ 1 2 - 6243104 + 5950812 2 3 - 544535 + 731846 3 52 - 68066 + 49942 @@ -8347,17 +8408,17 @@ 1 2 - 6219961 + 5921999 2 3 - 533645 + 714558 3 160 - 102100 + 96042 @@ -8373,57 +8434,62 @@ 1 2 - 107545 + 94121 2 3 - 54453 + 46100 3 4 - 32672 + 30733 4 5 - 29949 + 26891 5 - 7 - 34033 + 6 + 24971 - 7 - 11 - 40840 + 6 + 8 + 40337 - 11 - 17 - 34033 + 8 + 12 + 32654 - 17 + 12 + 18 + 38417 + + + 18 23 - 34033 + 34575 23 40 - 36756 + 36496 40 - 707 - 34033 + 76 + 34575 - 1013 - 1414 - 4084 + 83 + 891 + 15366 @@ -8439,52 +8505,57 @@ 1 2 - 107545 + 96042 2 3 - 54453 + 46100 3 4 - 35394 + 34575 4 5 - 34033 + 28812 5 - 7 - 38117 + 6 + 36496 - 7 - 11 - 40840 + 6 + 9 + 40337 - 11 - 17 - 36756 + 9 + 13 + 40337 - 17 - 23 - 34033 + 13 + 18 + 26891 - 23 - 40 - 35394 + 18 + 25 + 38417 - 40 - 830 - 25865 + 26 + 41 + 36496 + + + 41 + 479 + 30733 @@ -8500,52 +8571,57 @@ 1 2 - 118436 + 99884 2 3 - 63982 + 57625 3 4 - 36756 + 36496 4 5 - 34033 + 32654 5 - 7 - 35394 + 6 + 32654 - 7 - 11 - 40840 + 6 + 8 + 34575 - 11 - 17 - 34033 + 8 + 12 + 38417 - 17 - 26 - 34033 + 12 + 18 + 32654 - 26 - 56 - 34033 + 18 + 25 + 34575 - 64 + 25 + 47 + 34575 + + + 51 138 - 10890 + 21129 @@ -8561,17 +8637,17 @@ 1 2 - 4040456 + 4694572 2 11 - 341696 + 407221 11 - 1358 - 123881 + 426 + 151747 @@ -8587,17 +8663,17 @@ 1 2 - 4040456 + 4694572 2 6 - 359393 + 426430 6 - 783 - 106184 + 224 + 132539 @@ -8613,7 +8689,7 @@ 1 2 - 4506034 + 5253541 @@ -8623,26 +8699,26 @@ file_class - 14974 + 17287 id - 14974 + 17287 class_object - 122520 + 163272 id - 122520 + 163272 instance - 122520 + 163272 @@ -8656,7 +8732,7 @@ 1 2 - 122520 + 163272 @@ -8672,7 +8748,7 @@ 1 2 - 122520 + 163272 @@ -8682,19 +8758,19 @@ type_companion_object - 217814 + 307336 id - 217814 + 307336 instance - 217814 + 307336 companion_object - 217814 + 307336 @@ -8708,7 +8784,7 @@ 1 2 - 217814 + 307336 @@ -8724,7 +8800,7 @@ 1 2 - 217814 + 307336 @@ -8740,7 +8816,7 @@ 1 2 - 217814 + 307336 @@ -8756,7 +8832,7 @@ 1 2 - 217814 + 307336 @@ -8772,7 +8848,7 @@ 1 2 - 217814 + 307336 @@ -8788,7 +8864,7 @@ 1 2 - 217814 + 307336 @@ -8798,15 +8874,15 @@ kt_nullable_types - 1361 + 1920 id - 1361 + 1920 classid - 1361 + 1920 @@ -8820,7 +8896,7 @@ 1 2 - 1361 + 1920 @@ -8836,7 +8912,7 @@ 1 2 - 1361 + 1920 @@ -8846,15 +8922,15 @@ kt_notnull_types - 191419 + 172391 id - 191419 + 172391 classid - 191419 + 172391 @@ -8868,7 +8944,7 @@ 1 2 - 191419 + 172391 @@ -8884,7 +8960,7 @@ 1 2 - 191419 + 172391 @@ -8894,19 +8970,19 @@ kt_type_alias - 2060 + 1821 id - 2060 + 1821 name - 2060 + 1821 kttypeid - 1030 + 910 @@ -8920,7 +8996,7 @@ 1 2 - 2060 + 1821 @@ -8936,7 +9012,7 @@ 1 2 - 2060 + 1821 @@ -8952,7 +9028,7 @@ 1 2 - 2060 + 1821 @@ -8968,7 +9044,7 @@ 1 2 - 2060 + 1821 @@ -8984,7 +9060,7 @@ 2 3 - 1030 + 910 @@ -9000,7 +9076,7 @@ 2 3 - 1030 + 910 @@ -9021,23 +9097,23 @@ interfaces - 21501109 + 23200100 id - 21501109 + 23200100 nodeName - 5448969 + 8705320 parentid - 350 + 430271 sourceid - 2074 + 2787162 @@ -9051,7 +9127,7 @@ 1 2 - 21501109 + 23200100 @@ -9067,7 +9143,7 @@ 1 2 - 21501109 + 23200100 @@ -9083,7 +9159,7 @@ 1 2 - 21501109 + 23200100 @@ -9099,27 +9175,22 @@ 1 2 - 3495545 + 6834406 2 3 - 811460 + 1079521 3 - 5 - 410487 + 25 + 655011 - 5 - 12 - 426139 - - - 12 - 9655 - 305336 + 25 + 345 + 136380 @@ -9135,231 +9206,221 @@ 1 2 - 5448745 + 7996524 2 - 3 - 224 - - - - - - - nodeName - sourceid - - - 12 - - - 1 - 2 - 5448653 - - - 2 - 6 - 316 - - - - - - - parentid - id - - - 12 - - - 1 - 2 - 57 - - - 2 - 3 - 34 - - - 3 4 - 28 + 695349 4 - 6 - 28 - - - 6 - 9 - 28 - - - 10 - 16 - 28 - - - 16 - 20 - 28 - - - 21 - 212 - 28 - - - 288 - 1113 - 28 - - - 1315 - 27761 - 28 - - - 36093 - 2073873 - 28 - - - - - - - parentid - nodeName - - - 12 - - - 1 - 2 - 57 - - - 2 - 3 - 34 - - - 3 - 4 - 28 - - - 4 - 5 - 5 - - - 5 - 6 - 28 - - - 6 - 11 - 28 - - - 11 - 14 - 28 - - - 15 - 20 - 28 - - - 21 - 107 - 28 - - - 153 - 381 - 28 - - - 3060 - 15096 - 28 - - - 36080 - 500307 - 22 - - - - - - - parentid - sourceid - - - 12 - - - 1 - 2 - 97 - - - 2 - 3 - 40 - - - 3 - 4 - 34 - - - 4 - 6 - 28 - - - 6 7 - 11 + 13445 + + + + + + + nodeName + sourceid + + + 12 + + + 1 + 2 + 7967711 - 7 + 2 + 4 + 718400 + + + 4 + 7 + 19208 + + + + + + + parentid + id + + + 12 + + + 1 + 2 + 113330 + + + 2 + 3 + 55704 + + + 3 + 4 + 48021 + + + 4 + 5 + 26891 + + + 5 8 - 40 + 36496 8 + 13 + 34575 + + + 13 + 18 + 34575 + + + 18 + 41 + 32654 + + + 42 + 182 + 32654 + + + 187 + 4152 + 15366 + + + + + + + parentid + nodeName + + + 12 + + + 1 + 2 + 113330 + + + 2 + 3 + 55704 + + + 3 + 4 + 48021 + + + 4 + 5 + 38417 + + + 5 + 7 + 24971 + + + 7 10 - 22 + 32654 10 - 11 - 28 - - - 11 15 - 28 + 36496 - 19 - 40 - 17 + 15 + 28 + 32654 + + + 30 + 94 + 32654 + + + 100 + 1213 + 15366 + + + + + + + parentid + sourceid + + + 12 + + + 1 + 2 + 140222 + + + 2 + 3 + 65309 + + + 3 + 4 + 44179 + + + 4 + 5 + 34575 + + + 5 + 7 + 28812 + + + 7 + 9 + 34575 + + + 9 + 14 + 34575 + + + 14 + 26 + 32654 + + + 26 + 160 + 15366 @@ -9375,32 +9436,17 @@ 1 2 - 1292 + 2391465 2 - 7 - 166 + 11 + 213215 - 7 - 53 - 160 - - - 55 - 289 - 166 - - - 353 - 3260 - 160 - - - 3665 - 450913 - 126 + 11 + 1565 + 182481 @@ -9416,32 +9462,17 @@ 1 2 - 1292 + 2391465 2 6 - 172 + 217056 6 - 31 - 160 - - - 31 - 110 - 160 - - - 115 - 1202 - 160 - - - 1336 - 101026 - 126 + 492 + 178639 @@ -9457,7 +9488,7 @@ 1 2 - 2074 + 2787162 @@ -9467,15 +9498,15 @@ fielddecls - 398872 + 199475 id - 398872 + 199475 parentid - 59898 + 25503 @@ -9489,7 +9520,7 @@ 1 2 - 398872 + 199475 @@ -9505,32 +9536,57 @@ 1 2 - 29949 + 4554 2 3 - 13613 + 5465 3 4 - 5445 + 1821 4 - 5 - 2722 + 6 + 1821 6 - 16 - 5445 + 7 + 2732 - 40 - 159 - 2722 + 7 + 9 + 1821 + + + 9 + 10 + 910 + + + 10 + 11 + 1821 + + + 13 + 18 + 1821 + + + 19 + 20 + 1821 + + + 57 + 58 + 910 @@ -9540,19 +9596,19 @@ fieldDeclaredIn - 398872 + 199475 fieldId - 398872 + 199475 fieldDeclId - 398872 + 199475 pos - 1361 + 910 @@ -9566,7 +9622,7 @@ 1 2 - 398872 + 199475 @@ -9582,7 +9638,7 @@ 1 2 - 398872 + 199475 @@ -9598,7 +9654,7 @@ 1 2 - 398872 + 199475 @@ -9614,7 +9670,7 @@ 1 2 - 398872 + 199475 @@ -9628,9 +9684,9 @@ 12 - 293 - 294 - 1361 + 219 + 220 + 910 @@ -9644,9 +9700,9 @@ 12 - 293 - 294 - 1361 + 219 + 220 + 910 @@ -9656,27 +9712,27 @@ fields - 27509955 + 19350704 id - 27509955 + 19350704 nodeName - 10900247 + 13628474 typeid - 2728125 + 3038794 parentid - 3851230 + 4089502 sourceid - 27509955 + 19350704 @@ -9690,7 +9746,7 @@ 1 2 - 27509955 + 19350704 @@ -9706,7 +9762,7 @@ 1 2 - 27509955 + 19350704 @@ -9722,7 +9778,7 @@ 1 2 - 27509955 + 19350704 @@ -9738,7 +9794,7 @@ 1 2 - 27509955 + 19350704 @@ -9754,22 +9810,17 @@ 1 2 - 8061854 + 12016876 2 3 - 1433490 + 1123700 3 - 11 - 760988 - - - 11 - 828 - 643913 + 722 + 487897 @@ -9785,17 +9836,17 @@ 1 2 - 9875159 + 12416414 2 - 4 - 850837 + 5 + 1085283 - 4 + 5 160 - 174251 + 126776 @@ -9811,22 +9862,17 @@ 1 2 - 8061854 + 12016876 2 3 - 1433490 + 1123700 3 - 11 - 760988 - - - 11 - 828 - 643913 + 722 + 487897 @@ -9842,22 +9888,17 @@ 1 2 - 8061854 + 12016876 2 3 - 1433490 + 1123700 3 - 11 - 760988 - - - 11 - 828 - 643913 + 722 + 487897 @@ -9873,32 +9914,27 @@ 1 2 - 1732985 + 2059157 2 3 - 338973 + 320782 3 4 - 175612 + 213215 4 - 7 - 208284 + 8 + 245869 - 7 - 24 - 205562 - - - 24 - 7479 - 66705 + 8 + 2588 + 199769 @@ -9914,27 +9950,27 @@ 1 2 - 1894985 + 2120625 2 3 - 302217 + 299653 3 4 - 183780 + 199769 4 9 - 212369 + 255473 9 - 2335 - 134772 + 2016 + 163272 @@ -9950,17 +9986,17 @@ 1 2 - 2326529 + 2706486 2 4 - 228705 + 251632 4 - 1392 - 172890 + 1014 + 80675 @@ -9976,32 +10012,27 @@ 1 2 - 1732985 + 2059157 2 3 - 338973 + 320782 3 4 - 175612 + 213215 4 - 7 - 208284 + 8 + 245869 - 7 - 24 - 205562 - - - 24 - 7479 - 66705 + 8 + 2588 + 199769 @@ -10017,37 +10048,32 @@ 1 2 - 1937186 + 2427962 2 3 - 484636 + 478293 3 4 - 303578 + 303495 4 6 - 341696 + 338070 6 - 10 - 300856 + 13 + 330387 - 10 - 27 - 291326 - - - 27 + 13 1610 - 191948 + 211294 @@ -10063,37 +10089,32 @@ 1 2 - 1937186 + 2427962 2 3 - 484636 + 478293 3 4 - 303578 + 303495 4 6 - 341696 + 338070 6 - 10 - 300856 + 13 + 330387 - 10 - 27 - 291326 - - - 27 + 13 1610 - 191948 + 211294 @@ -10109,27 +10130,22 @@ 1 2 - 2499419 + 3031110 2 3 - 623493 + 595465 3 - 4 - 243679 + 5 + 347674 - 4 - 7 - 338973 - - - 7 + 5 76 - 145663 + 115251 @@ -10145,37 +10161,32 @@ 1 2 - 1937186 + 2427962 2 3 - 484636 + 478293 3 4 - 303578 + 303495 4 6 - 341696 + 338070 6 - 10 - 300856 + 13 + 330387 - 10 - 27 - 291326 - - - 27 + 13 1610 - 191948 + 211294 @@ -10191,7 +10202,7 @@ 1 2 - 27509955 + 19350704 @@ -10207,7 +10218,7 @@ 1 2 - 27509955 + 19350704 @@ -10223,7 +10234,7 @@ 1 2 - 27509955 + 19350704 @@ -10239,7 +10250,7 @@ 1 2 - 27509955 + 19350704 @@ -10249,15 +10260,15 @@ fieldsKotlinType - 27509955 + 19350704 id - 27509955 + 19350704 kttypeid - 1361 + 1920 @@ -10271,7 +10282,7 @@ 1 2 - 27509955 + 19350704 @@ -10285,9 +10296,9 @@ 12 - 20208 - 20209 - 1361 + 10074 + 10075 + 1920 @@ -10297,31 +10308,31 @@ constrs - 6869320 + 8006128 id - 6869320 + 8006128 nodeName - 3746407 + 4333451 signature - 5871458 + 6803672 typeid - 2722 + 3841 parentid - 4835479 + 5639633 sourceid - 5054654 + 5720309 @@ -10335,7 +10346,7 @@ 1 2 - 6869320 + 8006128 @@ -10351,7 +10362,7 @@ 1 2 - 6869320 + 8006128 @@ -10367,7 +10378,7 @@ 1 2 - 6869320 + 8006128 @@ -10383,7 +10394,7 @@ 1 2 - 6869320 + 8006128 @@ -10399,7 +10410,7 @@ 1 2 - 6869320 + 8006128 @@ -10415,22 +10426,58 @@ 1 2 - 2361924 + 2623889 2 3 - 835862 + 1085283 + + + 3 + 4 + 259315 + + + 4 + 11 + 326545 + + + 11 + 36 + 38417 + + + + + + + nodeName + signature + + + 12 + + + 1 + 2 + 2977327 + + + 2 + 3 + 868226 3 5 - 345780 + 341912 5 - 42 - 202839 + 19 + 145985 @@ -10438,7 +10485,7 @@ nodeName - signature + typeid 12 @@ -10446,95 +10493,64 @@ 1 2 - 2628747 + 4333451 + + + + + + + nodeName + parentid + + + 12 + + + 1 + 2 + 3764878 2 3 - 683392 + 401458 + + + 3 + 36 + 167114 + + + + + + + nodeName + sourceid + + + 12 + + + 1 + 2 + 2737220 + + + 2 + 3 + 1083362 3 5 - 302217 + 353437 5 - 19 - 132049 - - - - - - - nodeName - typeid - - - 12 - - - 1 - 2 - 3746407 - - - - - - - nodeName - parentid - - - 12 - - - 1 - 2 - 3287635 - - - 2 - 3 - 314469 - - - 3 - 42 - 144302 - - - - - - - nodeName - sourceid - - - 12 - - - 1 - 2 - 2453134 - - - 2 - 3 - 827694 - - - 3 - 5 - 318553 - - - 5 - 38 - 147024 + 32 + 159431 @@ -10550,12 +10566,12 @@ 1 2 - 5461695 + 6302329 2 - 42 - 409763 + 36 + 501343 @@ -10571,7 +10587,7 @@ 1 2 - 5871458 + 6803672 @@ -10587,7 +10603,7 @@ 1 2 - 5871458 + 6803672 @@ -10603,12 +10619,12 @@ 1 2 - 5461695 + 6302329 2 - 42 - 409763 + 36 + 501343 @@ -10624,12 +10640,12 @@ 1 2 - 5591022 + 6461760 2 - 32 - 280436 + 23 + 341912 @@ -10643,14 +10659,14 @@ 12 - 31 - 32 - 1361 + 22 + 23 + 1920 - 5015 - 5016 - 1361 + 4146 + 4147 + 1920 @@ -10666,12 +10682,12 @@ 1 2 - 1361 + 1920 - 2751 - 2752 - 1361 + 2255 + 2256 + 1920 @@ -10687,12 +10703,12 @@ 1 2 - 1361 + 1920 - 4312 - 4313 - 1361 + 3541 + 3542 + 1920 @@ -10706,14 +10722,14 @@ 12 - 31 - 32 - 1361 + 22 + 23 + 1920 - 3521 - 3522 - 1361 + 2914 + 2915 + 1920 @@ -10727,14 +10743,14 @@ 12 - 31 - 32 - 1361 + 22 + 23 + 1920 - 3682 - 3683 - 1361 + 2956 + 2957 + 1920 @@ -10750,22 +10766,22 @@ 1 2 - 3676978 + 4256617 2 3 - 737846 + 895118 3 6 - 366200 + 434113 6 19 - 54453 + 53783 @@ -10781,7 +10797,7 @@ 1 2 - 4835479 + 5639633 @@ -10797,22 +10813,22 @@ 1 2 - 3676978 + 4256617 2 3 - 737846 + 895118 3 6 - 366200 + 434113 6 19 - 54453 + 53783 @@ -10828,7 +10844,7 @@ 1 2 - 4835479 + 5639633 @@ -10844,22 +10860,22 @@ 1 2 - 3676978 + 4256617 2 3 - 737846 + 895118 3 6 - 366200 + 434113 6 19 - 54453 + 53783 @@ -10875,12 +10891,12 @@ 1 2 - 4742907 + 5357267 2 - 259 - 311746 + 209 + 363041 @@ -10896,12 +10912,12 @@ 1 2 - 4742907 + 5357267 2 - 212 - 311746 + 172 + 363041 @@ -10917,12 +10933,12 @@ 1 2 - 4742907 + 5357267 2 - 212 - 311746 + 172 + 363041 @@ -10938,7 +10954,7 @@ 1 2 - 5054654 + 5720309 @@ -10954,12 +10970,12 @@ 1 2 - 4742907 + 5357267 2 - 259 - 311746 + 209 + 363041 @@ -10969,15 +10985,15 @@ constrsKotlinType - 6869320 + 8006128 id - 6869320 + 8006128 kttypeid - 1361 + 1920 @@ -10991,7 +11007,7 @@ 1 2 - 6869320 + 8006128 @@ -11005,9 +11021,9 @@ 12 - 5046 - 5047 - 1361 + 4168 + 4169 + 1920 @@ -11017,31 +11033,31 @@ methods - 93308954 + 109719302 id - 93308954 + 109719302 nodeName - 20326164 + 22908130 signature - 29772501 + 33369112 typeid - 11583640 + 13338425 parentid - 11270532 + 12810189 sourceid - 58543057 + 67091663 @@ -11055,7 +11071,7 @@ 1 2 - 93308954 + 109719302 @@ -11071,7 +11087,7 @@ 1 2 - 93308954 + 109719302 @@ -11087,7 +11103,7 @@ 1 2 - 93308954 + 109719302 @@ -11103,7 +11119,7 @@ 1 2 - 93308954 + 109719302 @@ -11119,7 +11135,7 @@ 1 2 - 93308954 + 109719302 @@ -11135,32 +11151,32 @@ 1 2 - 11834127 + 13324979 2 3 - 3821280 + 4070294 3 4 - 1315054 + 1573181 4 7 - 1792884 + 2026503 7 - 271 - 1524700 + 56 + 1719166 - 276 - 2134 - 38117 + 57 + 1769 + 194006 @@ -11176,17 +11192,17 @@ 1 2 - 16853387 + 19150935 2 3 - 2115522 + 2228193 3 - 361 - 1357255 + 298 + 1529001 @@ -11202,22 +11218,22 @@ 1 2 - 17082092 + 19179748 2 3 - 1678532 + 1872834 3 - 52 - 1524700 + 25 + 1726849 - 52 - 845 - 40840 + 25 + 741 + 128697 @@ -11233,27 +11249,27 @@ 1 2 - 12555637 + 14028012 2 3 - 3596659 + 3889733 3 4 - 1245625 + 1502109 4 7 - 1577792 + 1817129 7 - 1278 - 1350449 + 1099 + 1671144 @@ -11269,27 +11285,27 @@ 1 2 - 12213940 + 13743725 2 3 - 3951969 + 4283509 3 4 - 1297356 + 1544368 4 7 - 1656750 + 1857467 7 - 928 - 1206147 + 800 + 1479059 @@ -11305,22 +11321,27 @@ 1 2 - 20356114 + 22735253 2 3 - 4957999 + 5434102 3 5 - 2363285 + 2635414 5 - 1275 - 2095101 + 157 + 2502875 + + + 157 + 1096 + 61467 @@ -11336,7 +11357,7 @@ 1 2 - 29772501 + 33369112 @@ -11352,17 +11373,17 @@ 1 2 - 26733991 + 29957672 2 5 - 2383706 + 2650781 5 - 843 - 654804 + 739 + 760659 @@ -11378,22 +11399,27 @@ 1 2 - 20360198 + 22741015 2 3 - 4956638 + 5432181 3 5 - 2361924 + 2631572 5 - 1275 - 2093740 + 157 + 2502875 + + + 157 + 1096 + 61467 @@ -11409,22 +11435,22 @@ 1 2 - 20978246 + 23365294 2 3 - 5076436 + 5656921 3 6 - 2533453 + 2900492 6 - 923 - 1184365 + 795 + 1446404 @@ -11440,32 +11466,32 @@ 1 2 - 5684955 + 6603903 2 3 - 2449050 + 2729536 3 4 - 1089071 + 1208218 4 6 - 925711 + 1119859 6 - 14 - 875341 + 15 + 1019974 - 14 - 11667 - 559510 + 15 + 10335 + 656932 @@ -11481,22 +11507,22 @@ 1 2 - 7613973 + 8714924 2 3 - 2218983 + 2620047 3 6 - 1060483 + 1167880 6 - 3940 - 690199 + 2888 + 835572 @@ -11512,27 +11538,22 @@ 1 2 - 7370293 + 8565097 2 3 - 2274798 + 2602760 3 - 5 - 875341 + 6 + 1210139 - 5 - 17 - 882148 - - - 17 - 5689 - 181058 + 6 + 4142 + 960428 @@ -11548,27 +11569,27 @@ 1 2 - 6786279 + 7714158 2 3 - 2466747 + 2869759 3 4 - 1003307 + 1108333 4 - 9 - 916181 + 8 + 1069916 - 9 - 3420 - 411124 + 8 + 2865 + 576256 @@ -11584,32 +11605,32 @@ 1 2 - 6180482 + 7166714 2 3 - 2262546 + 2524004 3 4 - 985610 + 1096808 4 6 - 910736 + 1094888 6 16 - 890316 + 1016132 16 - 8855 - 353948 + 6435 + 439876 @@ -11625,52 +11646,52 @@ 1 2 - 3190980 + 3630418 2 3 - 1287827 + 1456008 3 4 - 1029172 + 1164038 4 5 - 744652 + 735687 5 7 - 924349 + 1021895 7 10 - 952937 + 1106413 10 13 - 997862 + 1146751 13 - 19 - 908013 + 21 + 1181326 - 19 - 38 - 909375 + 21 + 40 + 1021895 - 38 + 40 319 - 325360 + 345754 @@ -11686,52 +11707,52 @@ 1 2 - 3244072 + 3678439 2 3 - 1308247 + 1457929 3 4 - 1109491 + 1231268 4 5 - 785493 + 791392 5 7 - 887593 + 960428 7 10 - 1015559 + 1183247 10 13 - 942047 + 1102571 13 - 17 - 875341 + 18 + 849018 - 17 - 35 - 848114 + 18 + 26 + 968111 - 35 + 26 290 - 254570 + 587781 @@ -11747,52 +11768,52 @@ 1 2 - 3190980 + 3630418 2 3 - 1287827 + 1456008 3 4 - 1029172 + 1164038 4 5 - 744652 + 735687 5 7 - 924349 + 1021895 7 10 - 952937 + 1106413 10 13 - 997862 + 1146751 13 - 19 - 908013 + 21 + 1181326 - 19 - 38 - 909375 + 21 + 40 + 1021895 - 38 + 40 319 - 325360 + 345754 @@ -11808,47 +11829,52 @@ 1 2 - 3887986 + 4348818 2 3 - 1615910 + 1757583 3 4 - 1327306 + 1436800 4 5 - 786854 + 922010 5 6 - 637107 + 743371 6 7 - 498250 + 564731 7 - 9 - 1033256 + 8 + 879752 - 9 - 13 - 882148 + 8 + 11 + 1160197 - 13 + 11 + 31 + 964269 + + + 33 78 - 601712 + 32654 @@ -11864,52 +11890,52 @@ 1 2 - 3190980 + 3630418 2 3 - 1287827 + 1456008 3 4 - 1029172 + 1164038 4 5 - 744652 + 735687 5 7 - 924349 + 1021895 7 10 - 952937 + 1106413 10 13 - 997862 + 1146751 13 - 19 - 908013 + 21 + 1181326 - 19 - 38 - 909375 + 21 + 40 + 1021895 - 38 + 40 319 - 325360 + 345754 @@ -11925,12 +11951,17 @@ 1 2 - 54190854 + 61836200 2 - 349 - 4352203 + 50 + 5032643 + + + 52 + 286 + 222819 @@ -11946,7 +11977,7 @@ 1 2 - 58543057 + 67091663 @@ -11962,12 +11993,12 @@ 1 2 - 58259899 + 66767038 2 - 347 - 283158 + 284 + 324624 @@ -11983,12 +12014,12 @@ 1 2 - 56848189 + 65030584 2 - 259 - 1694868 + 209 + 2061078 @@ -12004,12 +12035,17 @@ 1 2 - 54190854 + 61836200 2 - 349 - 4352203 + 50 + 5032643 + + + 52 + 286 + 222819 @@ -12019,15 +12055,15 @@ methodsKotlinType - 93308954 + 109719302 id - 93308954 + 109719302 kttypeid - 1361 + 1920 @@ -12041,7 +12077,7 @@ 1 2 - 93308954 + 109719302 @@ -12055,9 +12091,9 @@ 12 - 68542 - 68543 - 1361 + 57120 + 57121 + 1920 @@ -12067,27 +12103,27 @@ params - 101015498 + 120852585 id - 101015498 + 120852585 typeid - 11171154 + 12833239 pos - 29949 + 42258 parentid - 56251922 + 65007534 sourceid - 61310661 + 73639861 @@ -12101,7 +12137,7 @@ 1 2 - 101015498 + 120852585 @@ -12117,7 +12153,7 @@ 1 2 - 101015498 + 120852585 @@ -12133,7 +12169,7 @@ 1 2 - 101015498 + 120852585 @@ -12149,7 +12185,7 @@ 1 2 - 101015498 + 120852585 @@ -12165,32 +12201,37 @@ 1 2 - 5943609 + 6763334 2 3 - 1712565 + 1945827 3 4 - 869896 + 948902 4 6 - 970635 + 1148671 6 12 - 867173 + 1018053 12 - 7469 - 807274 + 326 + 966190 + + + 343 + 6738 + 42258 @@ -12206,17 +12247,22 @@ 1 2 - 9232606 + 10436011 2 3 - 1157138 + 1342678 3 + 8 + 964269 + + + 8 17 - 781409 + 90280 @@ -12232,32 +12278,32 @@ 1 2 - 6134197 + 7005362 2 3 - 1677170 + 1895885 3 4 - 888954 + 975794 4 6 - 917543 + 1087204 6 13 - 867173 + 1008449 13 - 5265 - 686115 + 4326 + 860543 @@ -12273,32 +12319,32 @@ 1 2 - 6337036 + 7216656 2 3 - 1569624 + 1788317 3 4 - 901206 + 996924 4 6 - 947492 + 1117938 6 13 - 856282 + 1002686 13 - 6292 - 559510 + 5757 + 710716 @@ -12314,57 +12360,57 @@ 1 2 - 2722 + 3841 - 53 - 56 - 2722 + 50 + 53 + 3841 - 110 - 112 - 2722 + 104 + 106 + 3841 - 165 - 172 - 2722 + 157 + 165 + 3841 - 224 - 242 - 2722 + 214 + 231 + 3841 - 305 - 330 - 2722 + 291 + 315 + 3841 - 524 - 666 - 2722 + 485 + 606 + 3841 - 880 - 1142 - 2722 + 804 + 1067 + 3841 - 1517 - 2090 - 2722 + 1445 + 2015 + 3841 - 3299 - 5949 - 2722 + 3084 + 5199 + 3841 - 15053 - 41322 - 2722 + 12689 + 33844 + 3841 @@ -12380,57 +12426,57 @@ 1 2 - 2722 + 3841 2 5 - 2722 + 3841 6 7 - 2722 + 3841 - 10 - 12 - 2722 + 11 + 14 + 3841 - 13 - 19 - 2722 + 15 + 20 + 3841 - 26 - 38 - 2722 + 27 + 37 + 3841 57 - 76 - 2722 + 75 + 3841 - 99 - 159 - 2722 + 97 + 153 + 3841 - 212 - 306 - 2722 + 201 + 289 + 3841 - 452 - 889 - 2722 + 403 + 777 + 3841 - 2370 - 6264 - 2722 + 1979 + 5089 + 3841 @@ -12446,57 +12492,57 @@ 1 2 - 2722 + 3841 - 53 - 56 - 2722 + 50 + 53 + 3841 - 110 - 112 - 2722 + 104 + 106 + 3841 - 165 - 172 - 2722 + 157 + 165 + 3841 - 224 - 242 - 2722 + 214 + 231 + 3841 - 305 - 330 - 2722 + 291 + 315 + 3841 - 524 - 666 - 2722 + 485 + 606 + 3841 - 880 - 1142 - 2722 + 804 + 1067 + 3841 - 1517 - 2090 - 2722 + 1445 + 2015 + 3841 - 3299 - 5949 - 2722 + 3084 + 5199 + 3841 - 15053 - 41322 - 2722 + 12689 + 33844 + 3841 @@ -12512,57 +12558,57 @@ 1 2 - 2722 + 3841 2 5 - 2722 + 3841 6 8 - 2722 + 3841 - 10 - 13 - 2722 + 11 + 15 + 3841 - 14 - 28 - 2722 + 16 + 29 + 3841 - 38 + 39 63 - 2722 + 3841 - 98 - 138 - 2722 + 101 + 144 + 3841 - 193 - 344 - 2722 + 210 + 386 + 3841 - 556 - 966 - 2722 + 628 + 1069 + 3841 - 1954 - 4172 - 2722 + 1955 + 3748 + 3841 - 10056 - 26381 - 2722 + 8555 + 21355 + 3841 @@ -12578,27 +12624,27 @@ 1 2 - 35759674 + 40633790 2 3 - 12394999 + 14389133 3 4 - 3606189 + 4060689 4 - 15 - 4258270 + 10 + 4992305 - 15 + 10 23 - 232789 + 931615 @@ -12614,17 +12660,17 @@ 1 2 - 39751122 + 45326442 2 3 - 12655015 + 14805959 3 23 - 3845785 + 4875132 @@ -12640,27 +12686,27 @@ 1 2 - 35759674 + 40633790 2 3 - 12394999 + 14389133 3 4 - 3606189 + 4060689 4 - 15 - 4258270 + 10 + 4992305 - 15 + 10 23 - 232789 + 931615 @@ -12676,27 +12722,27 @@ 1 2 - 35759674 + 40633790 2 3 - 12394999 + 14389133 3 4 - 3606189 + 4060689 4 - 15 - 4258270 + 10 + 4992305 - 15 + 10 23 - 232789 + 931615 @@ -12712,12 +12758,12 @@ 1 2 - 56891752 + 68584168 2 - 349 - 4418909 + 286 + 5055693 @@ -12733,12 +12779,12 @@ 1 2 - 59772347 + 71932220 2 - 349 - 1538314 + 286 + 1707641 @@ -12754,7 +12800,7 @@ 1 2 - 61310661 + 73639861 @@ -12770,12 +12816,12 @@ 1 2 - 56891752 + 68584168 2 - 349 - 4418909 + 286 + 5055693 @@ -12785,15 +12831,15 @@ paramsKotlinType - 101015498 + 120852585 id - 101015498 + 120852585 kttypeid - 1361 + 1920 @@ -12807,7 +12853,7 @@ 1 2 - 101015498 + 120852585 @@ -12821,9 +12867,9 @@ 12 - 74203 - 74204 - 1361 + 62916 + 62917 + 1920 @@ -12833,15 +12879,15 @@ paramName - 10331207 + 15639610 id - 10331207 + 15639610 nodeName - 1662195 + 2335761 @@ -12855,7 +12901,7 @@ 1 2 - 10331207 + 15639610 @@ -12871,37 +12917,37 @@ 1 2 - 717426 + 998845 2 3 - 328082 + 451401 3 4 - 174251 + 249711 4 5 - 129327 + 170956 5 - 9 - 137495 + 8 + 182481 - 9 - 25 - 130688 + 8 + 18 + 176718 - 25 - 517 - 44924 + 18 + 769 + 105647 @@ -12911,30 +12957,30 @@ isVarargsParam - 1003307 + 945061 param - 1003307 + 945061 exceptions - 1228169 + 1232644 id - 1228169 + 1232644 typeid - 36993 + 36990 parentid - 982877 + 987367 @@ -12948,7 +12994,7 @@ 1 2 - 1228169 + 1232644 @@ -12964,7 +13010,7 @@ 1 2 - 1228169 + 1232644 @@ -12980,7 +13026,7 @@ 1 2 - 11951 + 11950 2 @@ -13010,17 +13056,17 @@ 20 35 - 3414 - - - 49 - 107 2845 - 187 + 41 + 93 + 2845 + + + 106 813 - 1707 + 2276 @@ -13036,7 +13082,7 @@ 1 2 - 11951 + 11950 2 @@ -13066,17 +13112,17 @@ 20 35 - 3414 - - - 49 - 107 2845 - 187 + 41 + 93 + 2845 + + + 106 813 - 1707 + 2276 @@ -13092,12 +13138,12 @@ 1 2 - 750674 + 755179 2 3 - 224234 + 224220 3 @@ -13118,12 +13164,12 @@ 1 2 - 750674 + 755179 2 3 - 224234 + 224220 3 @@ -13138,41 +13184,41 @@ isAnnotType - 30058 + 29260 interfaceid - 30058 + 29260 isAnnotElem - 61062 + 61065 methodid - 61062 + 61065 annotValue - 1574849 + 1574925 parentid - 568147 + 568174 id2 - 52102 + 52104 value - 1574849 + 1574925 @@ -13186,32 +13232,32 @@ 1 2 - 153486 + 153493 2 3 - 252712 + 252724 3 4 - 48949 + 48951 4 6 - 26548 + 26550 6 7 - 35177 + 35179 7 9 - 46626 + 46628 9 @@ -13232,37 +13278,37 @@ 1 2 - 138054 + 138061 2 3 - 268144 + 268157 3 4 - 47290 + 47292 4 6 - 26051 + 26052 6 8 - 39657 + 39659 8 10 - 44137 + 44139 10 13 - 4811 + 4812 @@ -13278,22 +13324,22 @@ 1 2 - 17090 + 17091 2 3 - 5143 + 5144 3 4 - 2654 + 2655 4 6 - 4811 + 4812 6 @@ -13339,7 +13385,7 @@ 1 2 - 15265 + 15266 2 @@ -13364,7 +13410,7 @@ 8 10 - 4811 + 4812 10 @@ -13400,7 +13446,7 @@ 1 2 - 1574849 + 1574925 @@ -13416,7 +13462,7 @@ 1 2 - 1574849 + 1574925 @@ -13426,49 +13472,49 @@ isEnumType - 349864 + 397617 classid - 349864 + 397617 isEnumConst - 321905 + 3081053 fieldid - 321905 + 3081053 typeVars - 5105024 + 6288883 id - 5105024 + 6288883 nodeName - 70789 + 86438 pos - 5445 + 7683 kind - 1361 + 1920 parentid - 3715096 + 4444861 @@ -13482,7 +13528,7 @@ 1 2 - 5105024 + 6288883 @@ -13498,7 +13544,7 @@ 1 2 - 5105024 + 6288883 @@ -13514,7 +13560,7 @@ 1 2 - 5105024 + 6288883 @@ -13530,7 +13576,7 @@ 1 2 - 5105024 + 6288883 @@ -13546,47 +13592,47 @@ 1 2 - 20420 + 21129 2 3 - 10890 + 11525 3 4 - 6806 + 9604 4 7 - 5445 + 7683 7 10 - 5445 + 7683 10 28 - 5445 + 7683 - 37 - 71 - 5445 + 36 + 69 + 7683 71 - 253 - 5445 + 412 + 7683 - 459 - 951 - 5445 + 603 + 710 + 5762 @@ -13602,22 +13648,22 @@ 1 2 - 44924 + 51863 2 3 - 16336 + 23050 3 4 - 8168 + 9604 4 5 - 1361 + 1920 @@ -13633,7 +13679,7 @@ 1 2 - 70789 + 86438 @@ -13649,47 +13695,47 @@ 1 2 - 20420 + 21129 2 3 - 10890 + 11525 3 4 - 6806 + 9604 4 7 - 5445 + 7683 7 10 - 5445 + 7683 10 28 - 5445 + 7683 - 37 - 71 - 5445 + 36 + 69 + 7683 71 - 253 - 5445 + 412 + 7683 - 459 - 951 - 5445 + 603 + 710 + 5762 @@ -13705,22 +13751,22 @@ 6 7 - 1361 + 1920 - 94 - 95 - 1361 + 89 + 90 + 1920 - 921 - 922 - 1361 + 865 + 866 + 1920 - 2729 - 2730 - 1361 + 2314 + 2315 + 1920 @@ -13736,22 +13782,22 @@ 2 3 - 1361 + 1920 13 14 - 1361 + 1920 - 23 - 24 - 1361 + 21 + 22 + 1920 - 41 - 42 - 1361 + 34 + 35 + 1920 @@ -13767,7 +13813,7 @@ 1 2 - 5445 + 7683 @@ -13783,22 +13829,22 @@ 6 7 - 1361 + 1920 - 94 - 95 - 1361 + 89 + 90 + 1920 - 921 - 922 - 1361 + 865 + 866 + 1920 - 2729 - 2730 - 1361 + 2314 + 2315 + 1920 @@ -13812,9 +13858,9 @@ 12 - 3750 - 3751 - 1361 + 3274 + 3275 + 1920 @@ -13828,9 +13874,9 @@ 12 - 52 - 53 - 1361 + 45 + 46 + 1920 @@ -13846,7 +13892,7 @@ 4 5 - 1361 + 1920 @@ -13860,9 +13906,9 @@ 12 - 2729 - 2730 - 1361 + 2314 + 2315 + 1920 @@ -13878,17 +13924,17 @@ 1 2 - 2461302 + 2783320 2 3 - 1125828 + 1490584 3 5 - 127965 + 170956 @@ -13904,17 +13950,17 @@ 1 2 - 2461302 + 2783320 2 3 - 1125828 + 1490584 3 5 - 127965 + 170956 @@ -13930,17 +13976,17 @@ 1 2 - 2461302 + 2783320 2 3 - 1125828 + 1490584 3 5 - 127965 + 170956 @@ -13956,7 +14002,7 @@ 1 2 - 3715096 + 4444861 @@ -13966,19 +14012,19 @@ wildcards - 3629331 + 3338447 id - 3629331 + 3338447 nodeName - 980164 + 533998 kind - 2722 + 3841 @@ -13992,7 +14038,7 @@ 1 2 - 3629331 + 3338447 @@ -14008,7 +14054,7 @@ 1 2 - 3629331 + 3338447 @@ -14024,17 +14070,22 @@ 1 2 - 789577 + 397617 2 3 - 119797 + 55704 3 - 214 - 70789 + 7 + 44179 + + + 7 + 170 + 36496 @@ -14050,7 +14101,7 @@ 1 2 - 980164 + 533998 @@ -14064,14 +14115,14 @@ 12 - 1027 - 1028 - 1361 + 791 + 792 + 1920 - 1639 - 1640 - 1361 + 947 + 948 + 1920 @@ -14085,14 +14136,14 @@ 12 - 222 - 223 - 1361 + 91 + 92 + 1920 - 498 - 499 - 1361 + 187 + 188 + 1920 @@ -14102,23 +14153,23 @@ typeBounds - 4383514 + 4229725 id - 4383514 + 4229725 typeid - 3184173 + 2852471 pos - 1361 + 1920 parentid - 4383514 + 4229725 @@ -14132,7 +14183,7 @@ 1 2 - 4383514 + 4229725 @@ -14148,7 +14199,7 @@ 1 2 - 4383514 + 4229725 @@ -14164,7 +14215,7 @@ 1 2 - 4383514 + 4229725 @@ -14180,17 +14231,17 @@ 1 2 - 2506226 + 2091812 2 3 - 601712 + 695349 3 52 - 76235 + 65309 @@ -14206,7 +14257,7 @@ 1 2 - 3184173 + 2852471 @@ -14222,17 +14273,17 @@ 1 2 - 2506226 + 2091812 2 3 - 601712 + 695349 3 52 - 76235 + 65309 @@ -14246,9 +14297,9 @@ 12 - 3220 - 3221 - 1361 + 2202 + 2203 + 1920 @@ -14262,9 +14313,9 @@ 12 - 2339 - 2340 - 1361 + 1485 + 1486 + 1920 @@ -14278,9 +14329,9 @@ 12 - 3220 - 3221 - 1361 + 2202 + 2203 + 1920 @@ -14296,7 +14347,7 @@ 1 2 - 4383514 + 4229725 @@ -14312,7 +14363,7 @@ 1 2 - 4383514 + 4229725 @@ -14328,7 +14379,7 @@ 1 2 - 4383514 + 4229725 @@ -14338,11 +14389,11 @@ typeArgs - 53913162 + 53854237 argumentid - 1429292 + 1430551 pos @@ -14350,7 +14401,7 @@ parentid - 22268245 + 22244248 @@ -14364,17 +14415,17 @@ 1 2 - 866127 + 865848 2 3 - 469332 + 470615 3 11 - 93832 + 94087 @@ -14390,57 +14441,57 @@ 1 2 - 62430 + 64071 2 3 - 435425 + 433698 3 5 - 100353 + 100595 5 6 - 35320 + 35547 6 7 - 201035 + 201249 7 11 - 37958 + 39437 11 12 - 218221 + 218178 12 15 - 111144 + 111421 15 29 - 108553 + 108138 29 - 324 - 107243 + 341 + 107450 - 324 - 762773 - 11606 + 341 + 757580 + 10762 @@ -14459,48 +14510,48 @@ 5 - 598 - 599 + 597 + 598 5 - 889 - 890 + 887 + 888 5 - 1132 - 1133 + 1129 + 1130 5 - 1762 - 1763 + 1758 + 1759 5 - 3890 - 3891 + 3885 + 3886 5 - 4033 - 4034 + 4027 + 4028 5 - 51900 - 51901 + 51686 + 51687 5 - 143023 - 143024 + 142042 + 142043 5 - 161995 - 161996 + 161580 + 161581 5 @@ -14525,43 +14576,43 @@ 5 - 3896 - 3897 + 3895 + 3896 5 - 8697 - 8698 + 8694 + 8695 5 - 19018 - 19019 + 19012 + 19013 5 - 85642 - 85643 + 85622 + 85623 5 - 103285 - 103286 + 103257 + 103258 5 - 1458897 - 1458898 + 1447294 + 1447295 5 - 3827033 - 3827034 + 3800219 + 3800220 5 - 3875440 - 3875441 + 3848466 + 3848467 5 @@ -14578,22 +14629,22 @@ 1 2 - 295332 + 296162 2 3 - 13673753 + 13666793 3 4 - 7771091 + 7750153 4 11 - 528068 + 531138 @@ -14609,22 +14660,22 @@ 1 2 - 278146 + 278869 2 3 - 13607289 + 13599976 3 4 - 7789335 + 7768574 4 11 - 593474 + 596828 @@ -14634,37 +14685,37 @@ isParameterized - 25111274 + 27045654 memberid - 25111274 + 27045654 isRaw - 641191 + 731846 memberid - 641191 + 731846 erasure - 25752465 + 27777500 memberid - 25752465 + 27777500 erasureid - 865812 + 954665 @@ -14678,7 +14729,7 @@ 1 2 - 25752465 + 27777500 @@ -14694,57 +14745,62 @@ 1 2 - 144302 + 142143 2 3 - 133411 + 138301 3 4 - 88487 + 86438 4 5 - 54453 + 57625 5 6 - 50369 + 65309 6 8 - 63982 + 69150 8 - 11 - 78957 + 10 + 61467 - 11 - 19 - 70789 + 10 + 15 + 72992 - 19 - 33 - 70789 + 15 + 22 + 76834 - 33 - 93 - 65344 + 22 + 44 + 74913 - 97 - 1848 - 44924 + 46 + 124 + 72992 + + + 169 + 1564 + 36496 @@ -14754,15 +14810,15 @@ isAnonymClass - 192667 + 174213 classid - 192667 + 174213 parent - 192667 + 174213 @@ -14776,7 +14832,7 @@ 1 2 - 192667 + 174213 @@ -14792,7 +14848,7 @@ 1 2 - 192667 + 174213 @@ -14802,15 +14858,15 @@ isLocalClassOrInterface - 4057 + 3841 typeid - 4057 + 3841 parent - 4057 + 3841 @@ -14824,7 +14880,7 @@ 1 2 - 4057 + 3841 @@ -14840,7 +14896,7 @@ 1 2 - 4057 + 3841 @@ -14850,26 +14906,26 @@ isDefConstr - 139100 + 139383 constructorid - 139100 + 139383 lambdaKind - 183936 + 167982 exprId - 183936 + 167982 bodyKind - 15 + 13 @@ -14883,7 +14939,7 @@ 1 2 - 183936 + 167982 @@ -14897,9 +14953,9 @@ 12 - 11651 - 11652 - 15 + 12267 + 12268 + 13 @@ -14909,27 +14965,27 @@ arrays - 1116298 + 1375332 id - 1116298 + 1375332 nodeName - 687476 + 831730 elementtypeid - 1109491 + 1365728 dimension - 2722 + 3841 componenttypeid - 1116298 + 1375332 @@ -14943,7 +14999,7 @@ 1 2 - 1116298 + 1375332 @@ -14959,7 +15015,7 @@ 1 2 - 1116298 + 1375332 @@ -14975,7 +15031,7 @@ 1 2 - 1116298 + 1375332 @@ -14991,7 +15047,7 @@ 1 2 - 1116298 + 1375332 @@ -15007,17 +15063,17 @@ 1 2 - 562233 + 664616 2 3 - 99377 + 140222 3 - 95 - 25865 + 87 + 26891 @@ -15033,17 +15089,17 @@ 1 2 - 562233 + 664616 2 3 - 99377 + 140222 3 - 95 - 25865 + 87 + 26891 @@ -15059,7 +15115,7 @@ 1 2 - 687476 + 831730 @@ -15075,17 +15131,17 @@ 1 2 - 562233 + 664616 2 3 - 99377 + 140222 3 - 95 - 25865 + 87 + 26891 @@ -15101,12 +15157,12 @@ 1 2 - 1102685 + 1356124 2 3 - 6806 + 9604 @@ -15122,12 +15178,12 @@ 1 2 - 1102685 + 1356124 2 3 - 6806 + 9604 @@ -15143,12 +15199,12 @@ 1 2 - 1102685 + 1356124 2 3 - 6806 + 9604 @@ -15164,12 +15220,12 @@ 1 2 - 1102685 + 1356124 2 3 - 6806 + 9604 @@ -15185,12 +15241,12 @@ 5 6 - 1361 + 1920 - 815 - 816 - 1361 + 711 + 712 + 1920 @@ -15206,12 +15262,12 @@ 5 6 - 1361 + 1920 - 500 - 501 - 1361 + 428 + 429 + 1920 @@ -15227,12 +15283,12 @@ 5 6 - 1361 + 1920 - 815 - 816 - 1361 + 711 + 712 + 1920 @@ -15248,12 +15304,12 @@ 5 6 - 1361 + 1920 - 815 - 816 - 1361 + 711 + 712 + 1920 @@ -15269,7 +15325,7 @@ 1 2 - 1116298 + 1375332 @@ -15285,7 +15341,7 @@ 1 2 - 1116298 + 1375332 @@ -15301,7 +15357,7 @@ 1 2 - 1116298 + 1375332 @@ -15317,7 +15373,7 @@ 1 2 - 1116298 + 1375332 @@ -15327,15 +15383,15 @@ enclInReftype - 3410156 + 4051085 child - 3410156 + 4051085 parent - 929795 + 1125621 @@ -15349,7 +15405,7 @@ 1 2 - 3410156 + 4051085 @@ -15365,32 +15421,32 @@ 1 2 - 551342 + 666537 2 3 - 148386 + 176718 3 4 - 70789 + 86438 4 - 7 - 81680 + 6 + 82596 - 7 - 51 - 70789 + 6 + 17 + 84517 - 54 - 279 - 6806 + 17 + 265 + 28812 @@ -15400,15 +15456,15 @@ extendsReftype - 47868792 + 34389087 id1 - 34023966 + 33104034 id2 - 14017716 + 13251986 @@ -15422,22 +15478,12 @@ 1 2 - 26145892 + 32118635 2 - 3 - 3141972 - - - 3 - 4 - 3633415 - - - 4 10 - 1102685 + 985399 @@ -15453,17 +15499,17 @@ 1 2 - 11930782 + 12030322 2 - 3 - 1425322 + 5 + 1023816 - 3 - 12285 - 661611 + 5 + 8715 + 197848 @@ -15473,15 +15519,15 @@ implInterface - 3048147 + 14423708 id1 - 756277 + 8255839 id2 - 2271707 + 4487119 @@ -15495,37 +15541,27 @@ 1 2 - 209615 + 4223962 2 3 - 50204 + 2829421 3 4 - 67386 + 276603 4 5 - 61962 + 920090 5 - 6 - 19294 - - - 6 7 - 305429 - - - 7 - 10 - 42384 + 5762 @@ -15541,12 +15577,17 @@ 1 2 - 2196440 + 4003064 2 - 63781 - 75267 + 4 + 366883 + + + 4 + 2275 + 117172 @@ -15556,15 +15597,15 @@ permits - 177 + 129 id1 - 43 + 31 id2 - 177 + 129 @@ -15578,17 +15619,17 @@ 1 2 - 1 + 2 2 3 - 13 + 8 3 4 - 9 + 6 4 @@ -15598,25 +15639,20 @@ 5 6 - 5 + 2 6 - 7 - 4 - - - 7 8 2 8 9 - 1 + 2 - 10 + 9 11 2 @@ -15634,7 +15670,7 @@ 1 2 - 177 + 129 @@ -15644,15 +15680,15 @@ hasModifier - 249133355 + 270713939 id1 - 166385675 + 180180147 id2 - 13613 + 26891 @@ -15666,17 +15702,17 @@ 1 2 - 84119910 + 90230296 2 3 - 81783851 + 89365911 3 4 - 481914 + 583940 @@ -15690,54 +15726,74 @@ 12 - 31 - 32 - 1361 + 4 + 5 + 1920 + + + 14 + 15 + 1920 + + + 20 + 21 + 1920 + + + 21 + 22 + 1920 34 35 - 1361 + 1920 + + + 50 + 51 + 1920 109 110 - 1361 + 1920 - 267 - 268 - 1361 + 219 + 220 + 1920 - 500 - 501 - 1361 + 3864 + 3865 + 1920 - 8143 - 8144 - 1361 + 7193 + 7194 + 1920 - 18034 - 18035 - 1361 + 9195 + 9196 + 1920 - 18282 - 18283 - 1361 + 14706 + 14707 + 1920 - 20694 - 20695 - 1361 + 18810 + 18811 + 1920 - 116912 - 116913 - 1361 + 86695 + 86696 + 1920 @@ -15747,15 +15803,15 @@ imports - 368532 + 368550 id - 368532 + 368550 holder - 58075 + 58078 name @@ -15777,7 +15833,7 @@ 1 2 - 368532 + 368550 @@ -15793,7 +15849,7 @@ 1 2 - 368532 + 368550 @@ -15809,7 +15865,7 @@ 1 2 - 368532 + 368550 @@ -15825,7 +15881,7 @@ 1 2 - 28374 + 28375 2 @@ -15871,7 +15927,7 @@ 1 2 - 54591 + 54593 2 @@ -15892,7 +15948,7 @@ 1 2 - 55089 + 55091 2 @@ -15954,7 +16010,7 @@ 1 2 - 7300 + 7301 2 @@ -16078,27 +16134,27 @@ stmts - 2530730 + 2441387 id - 2530730 + 2441387 kind - 13613 + 9674 parent - 1783355 + 1353860 idx - 216453 + 15934 bodydecl - 703812 + 367630 @@ -16112,7 +16168,7 @@ 1 2 - 2530730 + 2441387 @@ -16128,7 +16184,7 @@ 1 2 - 2530730 + 2441387 @@ -16144,7 +16200,7 @@ 1 2 - 2530730 + 2441387 @@ -16160,7 +16216,7 @@ 1 2 - 2530730 + 2441387 @@ -16169,57 +16225,6 @@ kind id - - - 12 - - - 2 - 3 - 4084 - - - 4 - 5 - 1361 - - - 72 - 73 - 1361 - - - 96 - 97 - 1361 - - - 162 - 163 - 1361 - - - 373 - 374 - 1361 - - - 525 - 526 - 1361 - - - 621 - 622 - 1361 - - - - - - - kind - parent 12 @@ -16227,88 +16232,224 @@ 1 2 - 1361 - - - 2 - 3 - 2722 - - - 4 - 5 - 1361 - - - 53 - 54 - 1361 - - - 72 - 73 - 1361 - - - 98 - 99 - 1361 - - - 233 - 234 - 1361 - - - 373 - 374 - 1361 - - - 621 - 622 - 1361 - - - - - - - kind - idx - - - 12 - - - 1 - 2 - 5445 - - - 2 - 3 - 1361 - - - 5 - 6 - 1361 + 1707 6 7 - 1361 + 569 7 8 - 2722 + 569 - 158 - 159 - 1361 + 17 + 18 + 569 + + + 20 + 21 + 569 + + + 23 + 24 + 569 + + + 33 + 34 + 569 + + + 83 + 84 + 569 + + + 91 + 92 + 569 + + + 97 + 98 + 569 + + + 265 + 266 + 569 + + + 312 + 313 + 569 + + + 560 + 561 + 569 + + + 1243 + 1244 + 569 + + + 1530 + 1531 + 569 + + + + + + + kind + parent + + + 12 + + + 1 + 2 + 2276 + + + 7 + 8 + 569 + + + 12 + 13 + 569 + + + 17 + 18 + 569 + + + 21 + 22 + 569 + + + 33 + 34 + 569 + + + 71 + 72 + 569 + + + 81 + 82 + 569 + + + 91 + 92 + 569 + + + 246 + 247 + 569 + + + 265 + 266 + 569 + + + 271 + 272 + 569 + + + 716 + 717 + 569 + + + 1192 + 1193 + 569 + + + + + + + kind + idx + + + 12 + + + 1 + 2 + 2276 + + + 3 + 4 + 569 + + + 4 + 5 + 569 + + + 6 + 7 + 1138 + + + 7 + 8 + 569 + + + 8 + 9 + 1138 + + + 10 + 11 + 1138 + + + 13 + 14 + 569 + + + 17 + 18 + 569 + + + 21 + 22 + 569 + + + 26 + 27 + 569 @@ -16324,42 +16465,67 @@ 1 2 - 1361 + 2276 - 2 - 3 - 4084 + 7 + 8 + 1138 - 25 - 26 - 1361 + 17 + 18 + 569 - 71 - 72 - 1361 + 20 + 21 + 569 - 72 - 73 - 1361 + 21 + 22 + 569 + + + 53 + 54 + 569 + + + 54 + 55 + 569 + + + 91 + 92 + 569 119 120 - 1361 + 569 - 371 - 372 - 1361 + 179 + 180 + 569 - 517 - 518 - 1361 + 211 + 212 + 569 + + + 431 + 432 + 569 + + + 646 + 647 + 569 @@ -16375,17 +16541,22 @@ 1 2 - 1501557 + 989074 2 3 - 198755 + 191782 3 - 159 - 83041 + 6 + 107557 + + + 6 + 27 + 65445 @@ -16401,17 +16572,17 @@ 1 2 - 1587322 + 1081267 2 3 - 189226 + 194058 3 - 4 - 6806 + 6 + 78534 @@ -16427,17 +16598,22 @@ 1 2 - 1501557 + 989074 2 3 - 198755 + 191782 3 - 159 - 83041 + 6 + 107557 + + + 6 + 27 + 65445 @@ -16453,7 +16629,7 @@ 1 2 - 1783355 + 1353860 @@ -16466,25 +16642,70 @@ 12 - - 1 - 2 - 160638 - - - 2 - 3 - 34033 - 3 - 24 - 16336 + 5 + 1138 - 34 - 1213 - 5445 + 5 + 6 + 1138 + + + 6 + 7 + 2276 + + + 10 + 15 + 1138 + + + 15 + 16 + 1138 + + + 17 + 20 + 1138 + + + 24 + 32 + 1138 + + + 40 + 43 + 1138 + + + 55 + 72 + 1138 + + + 83 + 94 + 1138 + + + 115 + 160 + 1138 + + + 204 + 386 + 1138 + + + 939 + 1919 + 1138 @@ -16500,157 +16721,332 @@ 1 2 - 204200 - - - 2 - 9 - 12252 - - - - - - - idx - parent - - - 12 - - - 1 - 2 - 160638 + 2276 2 3 - 34033 + 3983 3 - 24 - 16336 + 5 + 1138 - 34 - 1213 - 5445 - - - - - - - idx - bodydecl - - - 12 - - - 1 - 2 - 160638 + 5 + 6 + 2276 - 2 - 3 - 34033 - - - 3 - 19 - 16336 - - - 29 - 518 - 5445 - - - - - - - bodydecl - id - - - 12 - - - 2 - 3 - 544535 - - - 3 - 4 - 58537 - - - 4 + 6 7 - 53092 + 2276 7 - 162 - 47646 - - - - - - - bodydecl - kind - - - 12 - - - 2 - 3 - 575846 - - - 3 - 4 - 81680 - - - 4 - 7 - 46285 - - - - - - - bodydecl - parent - - - 12 - - - 2 - 3 - 630300 - - - 3 - 8 - 57176 + 9 + 1138 9 - 47 - 16336 + 10 + 1138 + + + 12 + 14 + 1138 + + + 16 + 17 + 569 + + + + + + + idx + parent + + + 12 + + + 3 + 5 + 1138 + + + 5 + 6 + 1138 + + + 6 + 7 + 2276 + + + 10 + 15 + 1138 + + + 15 + 16 + 1138 + + + 17 + 20 + 1138 + + + 24 + 32 + 1138 + + + 40 + 43 + 1138 + + + 55 + 72 + 1138 + + + 83 + 94 + 1138 + + + 115 + 160 + 1138 + + + 204 + 386 + 1138 + + + 939 + 1919 + 1138 + + + + + + + idx + bodydecl + + + 12 + + + 3 + 5 + 1138 + + + 5 + 6 + 1138 + + + 6 + 7 + 2276 + + + 10 + 15 + 1138 + + + 15 + 16 + 1138 + + + 17 + 20 + 1138 + + + 24 + 32 + 1138 + + + 40 + 43 + 1138 + + + 54 + 56 + 1138 + + + 68 + 87 + 1138 + + + 104 + 138 + 1138 + + + 167 + 226 + 1138 + + + 337 + 647 + 1138 + + + + + + + bodydecl + id + + + 12 + + + 1 + 2 + 17641 + + + 2 + 3 + 157637 + + + 3 + 4 + 38128 + + + 4 + 5 + 33576 + + + 5 + 7 + 24470 + + + 7 + 10 + 29023 + + + 10 + 16 + 30730 + + + 16 + 34 + 27885 + + + 34 + 131 + 8536 + + + + + + + bodydecl + kind + + + 12 + + + 1 + 2 + 17641 + + + 2 + 3 + 193489 + + + 3 + 4 + 77965 + + + 4 + 5 + 29592 + + + 5 + 7 + 33007 + + + 7 + 11 + 15934 + + + + + + + bodydecl + parent + + + 12 + + + 1 + 2 + 17641 + + + 2 + 3 + 261780 + + + 4 + 5 + 33576 + + + 5 + 10 + 31299 + + + 10 + 88 + 23332 @@ -16666,22 +17062,37 @@ 1 2 - 544535 + 175279 2 3 - 92571 + 63737 3 + 4 + 27885 + + + 4 + 5 + 18210 + + + 5 7 - 54453 + 30161 7 - 159 - 12252 + 11 + 29592 + + + 11 + 28 + 22763 @@ -16691,11 +17102,11 @@ exprs - 7411134 + 7410663 id - 7411134 + 7410663 kind @@ -16703,11 +17114,11 @@ typeid - 115983 + 115976 parent - 5075960 + 5075638 idx @@ -16725,7 +17136,7 @@ 1 2 - 7411134 + 7410663 @@ -16741,7 +17152,7 @@ 1 2 - 7411134 + 7410663 @@ -16757,7 +17168,7 @@ 1 2 - 7411134 + 7410663 @@ -16773,7 +17184,7 @@ 1 2 - 7411134 + 7410663 @@ -17028,7 +17439,7 @@ 1 2 - 48884 + 48881 2 @@ -17038,7 +17449,7 @@ 3 6 - 10698 + 10697 6 @@ -17048,12 +17459,12 @@ 10 17 - 9272 + 9271 17 32 - 8723 + 8722 32 @@ -17079,32 +17490,32 @@ 1 2 - 57854 + 57851 2 3 - 18544 + 18543 3 4 - 8915 + 8914 4 5 - 14456 + 14455 5 6 - 9738 + 9737 6 32 - 6474 + 6473 @@ -17120,12 +17531,12 @@ 1 2 - 49048 + 49045 2 3 - 13414 + 13413 3 @@ -17135,7 +17546,7 @@ 5 8 - 8531 + 8530 8 @@ -17155,7 +17566,7 @@ 57 851 - 8723 + 8722 890 @@ -17176,22 +17587,22 @@ 1 2 - 58677 + 58673 2 3 - 18379 + 18378 3 4 - 14731 + 14730 4 5 - 19120 + 19119 5 @@ -17212,17 +17623,17 @@ 1 2 - 3222415 + 3222211 2 3 - 1522711 + 1522614 3 48 - 330833 + 330812 @@ -17238,17 +17649,17 @@ 1 2 - 3525515 + 3525291 2 3 - 1385549 + 1385461 3 8 - 164895 + 164884 @@ -17264,17 +17675,17 @@ 1 2 - 4085819 + 4085559 2 3 - 832130 + 832078 3 10 - 158009 + 157999 @@ -17290,17 +17701,17 @@ 1 2 - 3222415 + 3222211 2 3 - 1522711 + 1522614 3 48 - 330833 + 330812 @@ -17554,15 +17965,15 @@ exprsKotlinType - 7411134 + 7410663 id - 7411134 + 7410663 kttypeid - 12363 + 10930 @@ -17576,7 +17987,7 @@ 1 2 - 7411134 + 7410663 @@ -17592,17 +18003,17 @@ 1 2 - 9272 + 8197 2 3 - 2060 + 1821 - 7180 - 7181 - 1030 + 8123 + 8124 + 910 @@ -17612,15 +18023,15 @@ callableEnclosingExpr - 7299509 + 7299071 id - 7299509 + 7299071 callable_id - 19878 + 19877 @@ -17634,7 +18045,7 @@ 1 2 - 7299509 + 7299071 @@ -17705,7 +18116,7 @@ 73 177 - 1496 + 1495 179 @@ -17725,15 +18136,15 @@ statementEnclosingExpr - 7259150 + 7258714 id - 7259150 + 7258714 statement_id - 525810 + 525778 @@ -17747,7 +18158,7 @@ 1 2 - 7259150 + 7258714 @@ -17763,57 +18174,57 @@ 1 3 - 29127 + 29126 3 5 - 47080 + 47077 5 7 - 48477 + 48474 7 8 - 36044 + 36042 8 9 - 38147 + 38145 9 10 - 50450 + 50447 10 11 - 29214 + 29212 11 12 - 127057 + 127049 12 35 - 35324 + 35322 35 40 - 44626 + 44623 40 81 - 40224 + 40222 82 @@ -17828,11 +18239,11 @@ isParenthesized - 94658 + 94652 id - 94658 + 94652 parentheses @@ -17850,7 +18261,7 @@ 1 2 - 94658 + 94652 @@ -17881,37 +18292,37 @@ when_if - 83383 + 74334 id - 83383 + 74334 when_branch_else - 79912 + 76075 id - 79912 + 76075 callableBinding - 1832402 + 1832520 callerid - 1832402 + 1832520 callee - 263395 + 264311 @@ -17925,7 +18336,7 @@ 1 2 - 1832402 + 1832520 @@ -17941,32 +18352,32 @@ 1 2 - 162234 + 162993 2 3 - 33023 + 33137 3 4 - 16214 + 16271 4 7 - 22259 + 22271 7 20 - 19911 + 19902 20 46115 - 9751 + 9734 @@ -17976,15 +18387,15 @@ memberRefBinding - 23206 + 23208 id - 23206 + 23208 callable - 11196 + 11197 @@ -17998,7 +18409,7 @@ 1 2 - 23206 + 23208 @@ -18014,12 +18425,12 @@ 1 2 - 7861 + 7862 2 3 - 2074 + 2075 3 @@ -18039,15 +18450,15 @@ propertyRefGetBinding - 9639 + 8876 id - 9639 + 8876 getter - 5826 + 5366 @@ -18061,7 +18472,7 @@ 1 2 - 9639 + 8876 @@ -18077,17 +18488,17 @@ 1 2 - 2117 + 1951 2 3 - 3615 + 3328 3 6 - 94 + 86 @@ -18145,15 +18556,15 @@ propertyRefSetBinding - 2671 + 2600 id - 2671 + 2600 setter - 1335 + 1300 @@ -18167,7 +18578,7 @@ 1 2 - 2671 + 2600 @@ -18183,7 +18594,7 @@ 2 3 - 1335 + 1300 @@ -18193,15 +18604,15 @@ variableBinding - 2434432 + 2434277 expr - 2434432 + 2434277 variable - 572564 + 572528 @@ -18215,7 +18626,7 @@ 1 2 - 2434432 + 2434277 @@ -18231,37 +18642,37 @@ 1 2 - 205804 + 205791 2 3 - 120953 + 120945 3 4 - 85027 + 85022 4 5 - 45970 + 45967 5 7 - 40061 + 40059 7 14 - 43075 + 43072 14 464 - 31671 + 31669 @@ -18271,23 +18682,23 @@ localvars - 385297 + 385272 id - 385297 + 385272 nodeName - 140004 + 139995 typeid - 49513 + 49510 parentid - 385297 + 385272 @@ -18301,7 +18712,7 @@ 1 2 - 385297 + 385272 @@ -18317,7 +18728,7 @@ 1 2 - 385297 + 385272 @@ -18333,7 +18744,7 @@ 1 2 - 385297 + 385272 @@ -18349,22 +18760,22 @@ 1 2 - 83661 + 83655 2 3 - 26179 + 26178 3 5 - 10244 + 10243 5 9 - 11951 + 11950 9 @@ -18385,7 +18796,7 @@ 1 2 - 124638 + 124630 2 @@ -18411,22 +18822,22 @@ 1 2 - 83661 + 83655 2 3 - 26179 + 26178 3 5 - 10244 + 10243 5 9 - 11951 + 11950 9 @@ -18447,7 +18858,7 @@ 1 2 - 16504 + 16503 2 @@ -18462,7 +18873,7 @@ 5 6 - 5122 + 5121 6 @@ -18503,7 +18914,7 @@ 1 2 - 23334 + 23332 2 @@ -18513,7 +18924,7 @@ 3 4 - 6260 + 6259 4 @@ -18544,7 +18955,7 @@ 1 2 - 16504 + 16503 2 @@ -18559,7 +18970,7 @@ 5 6 - 5122 + 5121 6 @@ -18600,7 +19011,7 @@ 1 2 - 385297 + 385272 @@ -18616,7 +19027,7 @@ 1 2 - 385297 + 385272 @@ -18632,7 +19043,7 @@ 1 2 - 385297 + 385272 @@ -18642,15 +19053,15 @@ localvarsKotlinType - 227691 + 240107 id - 227691 + 240107 kttypeid - 154 + 1920 @@ -18664,7 +19075,7 @@ 1 2 - 227691 + 240107 @@ -18678,9 +19089,9 @@ 12 - 1470 - 1471 - 154 + 125 + 126 + 1920 @@ -18690,19 +19101,19 @@ namestrings - 4022677 + 4022436 name - 23386 + 23384 value - 22167 + 22166 parent - 4022677 + 4022436 @@ -18716,7 +19127,7 @@ 1 2 - 23386 + 23384 @@ -18732,7 +19143,7 @@ 1 2 - 9605 + 9604 2 @@ -18783,7 +19194,7 @@ 1 2 - 21560 + 21559 2 @@ -18804,7 +19215,7 @@ 1 2 - 9119 + 9118 2 @@ -18855,7 +19266,7 @@ 1 2 - 4022677 + 4022436 @@ -18871,7 +19282,7 @@ 1 2 - 4022677 + 4022436 @@ -18881,15 +19292,15 @@ modules - 7964 + 7965 id - 7964 + 7965 name - 7964 + 7965 @@ -18903,7 +19314,7 @@ 1 2 - 7964 + 7965 @@ -18919,7 +19330,7 @@ 1 2 - 7964 + 7965 @@ -18940,11 +19351,11 @@ cumodule - 247568 + 247580 fileId - 247568 + 247580 moduleId @@ -18962,7 +19373,7 @@ 1 2 - 247568 + 247580 @@ -19013,7 +19424,7 @@ directives - 50277 + 50279 id @@ -19021,7 +19432,7 @@ directive - 50277 + 50279 @@ -19081,7 +19492,7 @@ 1 2 - 50277 + 50279 @@ -19171,15 +19582,15 @@ exports - 35011 + 35013 id - 35011 + 35013 target - 35011 + 35013 @@ -19193,7 +19604,7 @@ 1 2 - 35011 + 35013 @@ -19209,7 +19620,7 @@ 1 2 - 35011 + 35013 @@ -19219,15 +19630,15 @@ exportsTo - 28706 + 28707 id - 12278 + 12279 target - 7466 + 7467 @@ -19241,7 +19652,7 @@ 1 2 - 7300 + 7301 2 @@ -19423,15 +19834,15 @@ uses - 10785 + 10786 id - 10785 + 10786 serviceInterface - 10785 + 10786 @@ -19445,7 +19856,7 @@ 1 2 - 10785 + 10786 @@ -19461,7 +19872,7 @@ 1 2 - 10785 + 10786 @@ -19519,7 +19930,7 @@ providesWith - 5309 + 5310 id @@ -19527,7 +19938,7 @@ serviceImpl - 5309 + 5310 @@ -19572,7 +19983,7 @@ 1 2 - 5309 + 5310 @@ -19582,48 +19993,48 @@ javadoc - 985153 + 985091 id - 985153 + 985091 isNormalComment - 649939 + 649898 commentid - 649939 + 649898 isEolComment - 610101 + 610062 commentid - 610101 + 610062 hasJavadoc - 435379 + 435352 documentableid - 368223 + 368199 javadocid - 435379 + 435352 @@ -19637,12 +20048,12 @@ 1 2 - 320416 + 320396 2 3 - 44960 + 44957 3 @@ -19663,7 +20074,7 @@ 1 2 - 435379 + 435352 @@ -19673,19 +20084,19 @@ javadocTag - 335830 + 335808 id - 335830 + 335808 name - 578 + 577 parentid - 114117 + 114110 idx @@ -19703,7 +20114,7 @@ 1 2 - 335830 + 335808 @@ -19719,7 +20130,7 @@ 1 2 - 335830 + 335808 @@ -19735,7 +20146,7 @@ 1 2 - 335830 + 335808 @@ -19884,37 +20295,37 @@ 1 2 - 33194 + 33192 2 3 - 24937 + 24935 3 4 - 18661 + 18660 4 5 - 13129 + 13128 5 6 - 9991 + 9990 6 7 - 7679 + 7678 7 11 - 6523 + 6522 @@ -19930,22 +20341,22 @@ 1 2 - 39966 + 39963 2 3 - 38644 + 38642 3 4 - 21139 + 21137 4 5 - 12221 + 12220 5 @@ -19966,37 +20377,37 @@ 1 2 - 33194 + 33192 2 3 - 24937 + 24935 3 4 - 18661 + 18660 4 5 - 13129 + 13128 5 6 - 9991 + 9990 6 7 - 7679 + 7678 7 11 - 6523 + 6522 @@ -20184,23 +20595,23 @@ javadocText - 2503007 + 2502848 id - 2503007 + 2502848 text - 1370450 + 1370363 parentid - 1169550 + 1169475 idx - 42115 + 42112 @@ -20214,7 +20625,7 @@ 1 2 - 2503007 + 2502848 @@ -20230,7 +20641,7 @@ 1 2 - 2503007 + 2502848 @@ -20246,7 +20657,7 @@ 1 2 - 2503007 + 2502848 @@ -20262,17 +20673,17 @@ 1 2 - 1139386 + 1139314 2 3 - 149679 + 149670 3 147 - 81384 + 81379 @@ -20288,17 +20699,17 @@ 1 2 - 1140524 + 1140452 2 3 - 149110 + 149101 3 88 - 80815 + 80810 @@ -20314,12 +20725,12 @@ 1 2 - 1346547 + 1346462 2 32 - 23903 + 23901 @@ -20335,22 +20746,22 @@ 1 2 - 870190 + 870135 2 3 - 159354 + 159344 3 12 - 83092 + 83086 12 75 - 56912 + 56908 @@ -20366,22 +20777,22 @@ 1 2 - 870190 + 870135 2 3 - 159354 + 159344 3 12 - 84230 + 84225 12 67 - 55774 + 55770 @@ -20397,22 +20808,22 @@ 1 2 - 870190 + 870135 2 3 - 159354 + 159344 3 12 - 83092 + 83086 12 75 - 56912 + 56908 @@ -20428,7 +20839,7 @@ 2 3 - 21057 + 21056 3 @@ -20484,7 +20895,7 @@ 2 3 - 20488 + 20487 3 @@ -20535,7 +20946,7 @@ 2 3 - 21057 + 21056 3 @@ -20580,15 +20991,15 @@ xmlEncoding - 800467 + 1129463 id - 800467 + 1129463 encoding - 1361 + 1920 @@ -20602,7 +21013,7 @@ 1 2 - 800467 + 1129463 @@ -20618,7 +21029,7 @@ 588 589 - 1361 + 1920 @@ -20976,27 +21387,27 @@ xmlElements - 106507143 + 150282022 id - 106507143 + 150282022 name - 337612 + 476372 parentid - 2747183 + 3876287 idx - 1207508 + 1703799 fileid - 800467 + 1129463 @@ -21010,7 +21421,7 @@ 1 2 - 106507143 + 150282022 @@ -21026,7 +21437,7 @@ 1 2 - 106507143 + 150282022 @@ -21042,7 +21453,7 @@ 1 2 - 106507143 + 150282022 @@ -21058,7 +21469,7 @@ 1 2 - 106507143 + 150282022 @@ -21074,57 +21485,57 @@ 1 2 - 106184 + 149826 2 3 - 40840 + 57625 3 4 - 16336 + 23050 4 6 - 29949 + 42258 6 8 - 24504 + 34575 8 9 - 12252 + 17287 9 10 - 23142 + 32654 10 18 - 28588 + 40337 18 48 - 25865 + 36496 52 250 - 25865 + 36496 342 73380 - 4084 + 5762 @@ -21140,52 +21551,52 @@ 1 2 - 123881 + 174797 2 3 - 46285 + 65309 3 4 - 17697 + 24971 4 5 - 14974 + 21129 5 6 - 19058 + 26891 6 8 - 28588 + 40337 8 10 - 28588 + 40337 10 21 - 27226 + 38417 22 128 - 25865 + 36496 130 229 - 5445 + 7683 @@ -21201,37 +21612,37 @@ 1 2 - 186503 + 263157 2 3 - 49008 + 69150 3 4 - 24504 + 34575 4 6 - 20420 + 28812 6 9 - 20420 + 28812 9 38 - 25865 + 36496 45 888 - 10890 + 15366 @@ -21247,42 +21658,42 @@ 1 2 - 183780 + 259315 2 3 - 36756 + 51863 3 4 - 17697 + 24971 4 5 - 13613 + 19208 5 7 - 29949 + 42258 7 16 - 25865 + 36496 17 114 - 27226 + 38417 118 131 - 2722 + 3841 @@ -21298,32 +21709,32 @@ 1 2 - 1671725 + 2358811 2 3 - 428822 + 605069 3 4 - 178335 + 251632 4 8 - 213730 + 301574 8 777 - 224621 + 316941 777 888 - 29949 + 42258 @@ -21339,17 +21750,17 @@ 1 2 - 2267992 + 3200146 2 3 - 291326 + 411063 3 17 - 187864 + 265078 @@ -21365,32 +21776,32 @@ 1 2 - 1671725 + 2358811 2 3 - 428822 + 605069 3 4 - 178335 + 251632 4 8 - 213730 + 301574 8 777 - 224621 + 316941 777 888 - 29949 + 42258 @@ -21406,7 +21817,7 @@ 1 2 - 2747183 + 3876287 @@ -21422,67 +21833,67 @@ 2 8 - 102100 + 144064 9 76 - 96655 + 136380 76 82 - 91209 + 128697 82 89 - 87125 + 122934 89 92 - 78957 + 111409 92 95 - 95293 + 134459 95 97 - 106184 + 149826 97 98 - 149747 + 211294 98 99 - 92571 + 130618 99 104 - 110268 + 155589 104 106 - 92571 + 130618 106 159 - 91209 + 128697 162 2019 - 13613 + 19208 @@ -21498,22 +21909,22 @@ 1 2 - 978803 + 1381095 2 5 - 89848 + 126776 5 9 - 103461 + 145985 9 150 - 35394 + 49942 @@ -21529,67 +21940,67 @@ 2 8 - 102100 + 144064 9 76 - 96655 + 136380 76 82 - 91209 + 128697 82 89 - 87125 + 122934 89 92 - 78957 + 111409 92 95 - 95293 + 134459 95 97 - 106184 + 149826 97 98 - 149747 + 211294 98 99 - 92571 + 130618 99 104 - 110268 + 155589 104 106 - 92571 + 130618 106 159 - 91209 + 128697 162 2019 - 13613 + 19208 @@ -21605,67 +22016,67 @@ 2 8 - 102100 + 144064 9 76 - 96655 + 136380 76 82 - 91209 + 128697 82 89 - 87125 + 122934 89 92 - 78957 + 111409 92 95 - 95293 + 134459 95 97 - 106184 + 149826 97 98 - 149747 + 211294 98 99 - 92571 + 130618 99 104 - 110268 + 155589 104 106 - 92571 + 130618 106 139 - 91209 + 128697 141 589 - 13613 + 19208 @@ -21681,57 +22092,57 @@ 1 2 - 58537 + 82596 2 3 - 134772 + 190164 3 4 - 176974 + 249711 4 5 - 74873 + 105647 5 7 - 59898 + 84517 7 10 - 66705 + 94121 10 31 - 62621 + 88359 35 694 - 61260 + 86438 738 776 - 20420 + 28812 777 779 - 65344 + 92201 788 889 - 19058 + 26891 @@ -21747,37 +22158,37 @@ 1 2 - 58537 + 82596 2 3 - 398872 + 562810 3 4 - 138856 + 195927 4 5 - 65344 + 92201 5 6 - 61260 + 86438 6 9 - 65344 + 92201 9 69 - 12252 + 17287 @@ -21793,27 +22204,27 @@ 1 2 - 58537 + 82596 2 3 - 567678 + 800997 3 4 - 57176 + 80675 4 6 - 58537 + 82596 6 165 - 58537 + 82596 @@ -21829,42 +22240,42 @@ 1 2 - 202839 + 286207 2 3 - 219175 + 309257 3 4 - 88487 + 124855 4 7 - 66705 + 94121 7 17 - 66705 + 94121 18 763 - 61260 + 86438 764 777 - 65344 + 92201 777 888 - 29949 + 42258 @@ -21874,31 +22285,31 @@ xmlAttrs - 129551904 + 182798274 id - 129551904 + 182798274 elementid - 105600491 + 149002731 name - 511863 + 722241 value - 8184375 + 11548187 idx - 31310 + 44179 fileid - 799106 + 1127542 @@ -21912,7 +22323,7 @@ 1 2 - 129551904 + 182798274 @@ -21928,7 +22339,7 @@ 1 2 - 129551904 + 182798274 @@ -21944,7 +22355,7 @@ 1 2 - 129551904 + 182798274 @@ -21960,7 +22371,7 @@ 1 2 - 129551904 + 182798274 @@ -21976,7 +22387,7 @@ 1 2 - 129551904 + 182798274 @@ -21992,17 +22403,17 @@ 1 2 - 96634707 + 136351973 2 6 - 7940695 + 11204353 6 24 - 1025088 + 1446404 @@ -22018,17 +22429,17 @@ 1 2 - 96634707 + 136351973 2 6 - 7954308 + 11223562 6 23 - 1011475 + 1427196 @@ -22044,17 +22455,17 @@ 1 2 - 96687799 + 136426886 2 6 - 7993787 + 11279267 6 21 - 918904 + 1296577 @@ -22070,17 +22481,17 @@ 1 2 - 96634707 + 136351973 2 6 - 7940695 + 11204353 6 24 - 1025088 + 1446404 @@ -22096,7 +22507,7 @@ 1 2 - 105600491 + 149002731 @@ -22112,62 +22523,62 @@ 1 2 - 106184 + 149826 2 3 - 55814 + 78755 3 4 - 31310 + 44179 4 5 - 17697 + 24971 5 6 - 29949 + 42258 6 8 - 36756 + 51863 8 11 - 42201 + 59546 11 22 - 39478 + 55704 23 38 - 40840 + 57625 38 79 - 40840 + 57625 81 168 - 39478 + 55704 168 74700 - 31310 + 44179 @@ -22183,62 +22594,62 @@ 1 2 - 106184 + 149826 2 3 - 55814 + 78755 3 4 - 31310 + 44179 4 5 - 17697 + 24971 5 6 - 29949 + 42258 6 8 - 36756 + 51863 8 11 - 46285 + 65309 11 25 - 43562 + 61467 25 39 - 42201 + 59546 43 91 - 40840 + 57625 91 227 - 39478 + 55704 227 74700 - 21781 + 30733 @@ -22254,42 +22665,42 @@ 1 2 - 215091 + 303495 2 3 - 80319 + 113330 3 4 - 34033 + 48021 4 5 - 36756 + 51863 5 9 - 42201 + 59546 9 21 - 39478 + 55704 22 64 - 42201 + 59546 68 2100 - 21781 + 30733 @@ -22305,37 +22716,37 @@ 1 2 - 201478 + 284286 2 3 - 95293 + 134459 3 4 - 49008 + 69150 4 5 - 54453 + 76834 5 7 - 32672 + 46100 7 10 - 40840 + 57625 10 21 - 38117 + 53783 @@ -22351,52 +22762,52 @@ 1 2 - 178335 + 251632 2 3 - 53092 + 74913 3 4 - 27226 + 38417 4 5 - 24504 + 34575 5 6 - 38117 + 53783 6 9 - 42201 + 59546 9 17 - 44924 + 63388 17 34 - 39478 + 55704 36 91 - 39478 + 55704 91 223 - 24504 + 34575 @@ -22412,37 +22823,37 @@ 1 2 - 4470639 + 6308091 2 3 - 1210231 + 1707641 3 5 - 627577 + 885514 5 31 - 616686 + 870147 31 91 - 643913 + 908564 91 1111 - 613964 + 866306 3397 3398 - 1361 + 1920 @@ -22458,32 +22869,32 @@ 1 2 - 4560488 + 6434868 2 3 - 1153054 + 1626965 3 5 - 635745 + 897039 5 33 - 645275 + 910485 33 93 - 631661 + 891277 93 3398 - 558149 + 787551 @@ -22499,17 +22910,17 @@ 1 2 - 7427470 + 10480191 2 4 - 658888 + 929694 4 53 - 98016 + 138301 @@ -22525,17 +22936,17 @@ 1 2 - 6778110 + 9563942 2 3 - 989694 + 1396462 3 20 - 416569 + 587781 @@ -22551,32 +22962,32 @@ 1 2 - 5268385 + 7433713 2 3 - 887593 + 1252398 3 10 - 635745 + 897039 10 83 - 622132 + 877831 83 99 - 624854 + 881672 99 182 - 145663 + 205531 @@ -22592,62 +23003,62 @@ 1 6 - 2722 + 3841 12 14 - 2722 + 3841 17 26 - 2722 + 3841 39 56 - 2722 + 3841 83 110 - 2722 + 3841 153 232 - 2722 + 3841 316 400 - 2722 + 3841 468 545 - 2722 + 3841 626 754 - 2722 + 3841 951 1491 - 2722 + 3841 4718 6587 - 2722 + 3841 77571 77572 - 1361 + 1920 @@ -22663,62 +23074,62 @@ 1 6 - 2722 + 3841 12 14 - 2722 + 3841 17 26 - 2722 + 3841 39 56 - 2722 + 3841 83 110 - 2722 + 3841 153 232 - 2722 + 3841 316 400 - 2722 + 3841 468 545 - 2722 + 3841 626 754 - 2722 + 3841 951 1491 - 2722 + 3841 4718 6587 - 2722 + 3841 77571 77572 - 1361 + 1920 @@ -22734,62 +23145,62 @@ 1 4 - 2722 + 3841 7 10 - 2722 + 3841 11 17 - 2722 + 3841 18 23 - 2722 + 3841 26 38 - 2722 + 3841 39 49 - 2722 + 3841 57 67 - 2722 + 3841 72 79 - 2722 + 3841 95 101 - 2722 + 3841 105 106 - 2722 + 3841 106 132 - 2722 + 3841 140 141 - 1361 + 1920 @@ -22805,62 +23216,62 @@ 1 5 - 2722 + 3841 7 10 - 2722 + 3841 11 18 - 2722 + 3841 22 32 - 2722 + 3841 46 63 - 2722 + 3841 85 119 - 2722 + 3841 142 185 - 2722 + 3841 212 228 - 2722 + 3841 253 275 - 2722 + 3841 307 423 - 2722 + 3841 580 1324 - 2722 + 3841 3579 3580 - 1361 + 1920 @@ -22876,62 +23287,62 @@ 1 6 - 2722 + 3841 7 8 - 2722 + 3841 10 19 - 2722 + 3841 23 36 - 2722 + 3841 45 59 - 2722 + 3841 73 97 - 2722 + 3841 115 131 - 2722 + 3841 140 148 - 2722 + 3841 168 181 - 2722 + 3841 248 363 - 2722 + 3841 473 530 - 2722 + 3841 587 588 - 1361 + 1920 @@ -22947,72 +23358,72 @@ 1 3 - 59898 + 84517 3 5 - 61260 + 86438 5 6 - 36756 + 51863 6 7 - 61260 + 86438 7 8 - 51730 + 72992 8 10 - 58537 + 82596 10 15 - 65344 + 92201 15 27 - 61260 + 86438 27 41 - 61260 + 86438 41 65 - 61260 + 86438 65 157 - 65344 + 92201 162 817 - 61260 + 86438 818 832 - 66705 + 94121 832 1187 - 27226 + 38417 @@ -23028,52 +23439,52 @@ 1 2 - 91209 + 128697 2 3 - 187864 + 265078 3 4 - 112991 + 159431 4 5 - 77596 + 109488 5 8 - 72151 + 101805 8 14 - 61260 + 86438 14 295 - 61260 + 86438 330 775 - 50369 + 71071 776 778 - 65344 + 92201 787 888 - 19058 + 26891 @@ -23089,62 +23500,62 @@ 1 2 - 50369 + 71071 2 3 - 65344 + 92201 3 4 - 50369 + 71071 4 5 - 66705 + 94121 5 6 - 121159 + 170956 6 7 - 114352 + 161351 7 8 - 50369 + 71071 8 12 - 61260 + 86438 12 18 - 69428 + 97963 18 24 - 68066 + 96042 24 37 - 62621 + 88359 37 55 - 19058 + 26891 @@ -23160,67 +23571,67 @@ 1 3 - 69428 + 97963 3 4 - 39478 + 55704 4 5 - 73512 + 103726 5 6 - 88487 + 124855 6 8 - 62621 + 88359 8 12 - 70789 + 99884 12 19 - 61260 + 86438 19 27 - 69428 + 97963 27 41 - 61260 + 86438 42 170 - 61260 + 86438 205 780 - 57176 + 80675 781 783 - 65344 + 92201 791 893 - 19058 + 26891 @@ -23236,47 +23647,47 @@ 1 2 - 78957 + 111409 2 3 - 76235 + 107567 3 4 - 151108 + 213215 4 5 - 155192 + 218977 5 6 - 92571 + 130618 6 10 - 68066 + 96042 10 12 - 46285 + 65309 12 15 - 69428 + 97963 15 24 - 61260 + 86438 @@ -23286,23 +23697,23 @@ xmlNs - 1283743 + 1811367 id - 8168 + 11525 prefixName - 9529 + 13445 URI - 8168 + 11525 fileid - 747375 + 1054550 @@ -23316,12 +23727,12 @@ 1 2 - 6806 + 9604 2 3 - 1361 + 1920 @@ -23337,7 +23748,7 @@ 1 2 - 8168 + 11525 @@ -23353,32 +23764,32 @@ 2 3 - 1361 + 1920 20 21 - 1361 + 1920 88 89 - 1361 + 1920 167 168 - 1361 + 1920 213 214 - 1361 + 1920 453 454 - 1361 + 1920 @@ -23394,7 +23805,7 @@ 1 2 - 9529 + 13445 @@ -23410,7 +23821,7 @@ 1 2 - 9529 + 13445 @@ -23426,37 +23837,37 @@ 1 2 - 1361 + 1920 2 3 - 1361 + 1920 20 21 - 1361 + 1920 88 89 - 1361 + 1920 166 167 - 1361 + 1920 213 214 - 1361 + 1920 453 454 - 1361 + 1920 @@ -23472,7 +23883,7 @@ 1 2 - 8168 + 11525 @@ -23488,12 +23899,12 @@ 1 2 - 6806 + 9604 2 3 - 1361 + 1920 @@ -23509,32 +23920,32 @@ 2 3 - 1361 + 1920 20 21 - 1361 + 1920 88 89 - 1361 + 1920 167 168 - 1361 + 1920 213 214 - 1361 + 1920 453 454 - 1361 + 1920 @@ -23550,17 +23961,17 @@ 1 2 - 333528 + 470609 2 3 - 291326 + 411063 3 4 - 122520 + 172877 @@ -23576,17 +23987,17 @@ 1 2 - 333528 + 470609 2 3 - 291326 + 411063 3 4 - 122520 + 172877 @@ -23602,17 +24013,17 @@ 1 2 - 333528 + 470609 2 3 - 291326 + 411063 3 4 - 122520 + 172877 @@ -23622,19 +24033,19 @@ xmlHasNs - 25896767 + 36540446 elementId - 25896767 + 36540446 nsId - 8168 + 11525 fileid - 743291 + 1048787 @@ -23648,7 +24059,7 @@ 1 2 - 25896767 + 36540446 @@ -23664,7 +24075,7 @@ 1 2 - 25896767 + 36540446 @@ -23680,32 +24091,32 @@ 13 14 - 1361 + 1920 84 85 - 1361 + 1920 2426 2427 - 1361 + 1920 2733 2734 - 1361 + 1920 3704 3705 - 1361 + 1920 10063 10064 - 1361 + 1920 @@ -23721,32 +24132,32 @@ 2 3 - 1361 + 1920 20 21 - 1361 + 1920 86 87 - 1361 + 1920 164 165 - 1361 + 1920 209 210 - 1361 + 1920 453 454 - 1361 + 1920 @@ -23762,77 +24173,77 @@ 1 3 - 44924 + 63388 3 5 - 62621 + 88359 5 6 - 34033 + 48021 6 7 - 65344 + 92201 7 8 - 49008 + 69150 8 10 - 61260 + 86438 10 15 - 65344 + 92201 15 25 - 55814 + 78755 25 36 - 57176 + 80675 36 49 - 58537 + 82596 49 54 - 14974 + 21129 54 55 - 58537 + 82596 55 81 - 57176 + 80675 81 298 - 55814 + 78755 298 833 - 2722 + 3841 @@ -23848,17 +24259,17 @@ 1 2 - 334889 + 472530 2 3 - 288604 + 407221 3 4 - 119797 + 169035 @@ -23868,23 +24279,23 @@ xmlComments - 107198704 + 151257816 id - 107198704 + 151257816 text - 1692145 + 2387624 parentid - 839946 + 1185168 fileid - 785493 + 1108333 @@ -23898,7 +24309,7 @@ 1 2 - 107198704 + 151257816 @@ -23914,7 +24325,7 @@ 1 2 - 107198704 + 151257816 @@ -23930,7 +24341,7 @@ 1 2 - 107198704 + 151257816 @@ -23946,67 +24357,67 @@ 1 2 - 232789 + 328466 2 7 - 138856 + 195927 7 32 - 140218 + 197848 32 61 - 127965 + 180560 61 76 - 127965 + 180560 76 84 - 136133 + 192085 84 90 - 125243 + 176718 90 94 - 111629 + 157510 94 95 - 59898 + 84517 95 96 - 100739 + 142143 96 98 - 140218 + 197848 98 100 - 126604 + 178639 100 460 - 123881 + 174797 @@ -24022,67 +24433,67 @@ 1 2 - 235511 + 332308 2 6 - 134772 + 190164 6 32 - 142940 + 201689 32 61 - 129327 + 182481 61 75 - 132049 + 186323 75 84 - 148386 + 209373 84 90 - 122520 + 172877 90 94 - 119797 + 169035 94 95 - 66705 + 94121 95 96 - 103461 + 145985 96 98 - 142940 + 201689 98 100 - 134772 + 190164 100 460 - 78957 + 111409 @@ -24098,67 +24509,67 @@ 1 2 - 246402 + 347674 2 7 - 133411 + 188243 7 32 - 133411 + 188243 32 61 - 129327 + 182481 61 75 - 132049 + 186323 75 84 - 148386 + 209373 84 90 - 122520 + 172877 90 94 - 119797 + 169035 94 95 - 66705 + 94121 95 96 - 103461 + 145985 96 98 - 142940 + 201689 98 100 - 134772 + 190164 100 460 - 78957 + 111409 @@ -24174,22 +24585,22 @@ 1 2 - 668417 + 943140 2 724 - 63982 + 90280 726 830 - 77596 + 109488 831 941 - 29949 + 42258 @@ -24205,27 +24616,27 @@ 1 2 - 668417 + 943140 2 697 - 63982 + 90280 697 795 - 34033 + 48021 795 827 - 63982 + 90280 838 899 - 9529 + 13445 @@ -24241,7 +24652,7 @@ 1 2 - 839946 + 1185168 @@ -24257,27 +24668,27 @@ 1 2 - 600350 + 847097 2 549 - 59898 + 84517 579 829 - 40840 + 57625 829 832 - 65344 + 92201 834 941 - 19058 + 26891 @@ -24293,27 +24704,27 @@ 1 2 - 600350 + 847097 2 536 - 59898 + 84517 560 795 - 51730 + 72992 795 812 - 59898 + 84517 819 899 - 13613 + 19208 @@ -24329,12 +24740,12 @@ 1 2 - 746014 + 1052629 2 6 - 39478 + 55704 @@ -24344,31 +24755,31 @@ xmlChars - 101302741 + 142938589 id - 101302741 + 142938589 text - 77797848 + 109773086 parentid - 101302741 + 142938589 idx - 1361 + 1920 isCDATA - 2722 + 3841 fileid - 176974 + 249711 @@ -24382,7 +24793,7 @@ 1 2 - 101302741 + 142938589 @@ -24398,7 +24809,7 @@ 1 2 - 101302741 + 142938589 @@ -24414,7 +24825,7 @@ 1 2 - 101302741 + 142938589 @@ -24430,7 +24841,7 @@ 1 2 - 101302741 + 142938589 @@ -24446,7 +24857,7 @@ 1 2 - 101302741 + 142938589 @@ -24462,17 +24873,17 @@ 1 2 - 66568156 + 93927944 2 3 - 6991841 + 9865517 3 128 - 4237850 + 5979625 @@ -24488,17 +24899,17 @@ 1 2 - 66568156 + 93927944 2 3 - 6991841 + 9865517 3 128 - 4237850 + 5979625 @@ -24514,7 +24925,7 @@ 1 2 - 77797848 + 109773086 @@ -24530,7 +24941,7 @@ 1 2 - 77797848 + 109773086 @@ -24546,12 +24957,12 @@ 1 2 - 74436700 + 105030493 2 76 - 3361148 + 4742593 @@ -24567,7 +24978,7 @@ 1 2 - 101302741 + 142938589 @@ -24583,7 +24994,7 @@ 1 2 - 101302741 + 142938589 @@ -24599,7 +25010,7 @@ 1 2 - 101302741 + 142938589 @@ -24615,7 +25026,7 @@ 1 2 - 101302741 + 142938589 @@ -24631,7 +25042,7 @@ 1 2 - 101302741 + 142938589 @@ -24647,7 +25058,7 @@ 74414 74415 - 1361 + 1920 @@ -24663,7 +25074,7 @@ 57148 57149 - 1361 + 1920 @@ -24679,7 +25090,7 @@ 74414 74415 - 1361 + 1920 @@ -24695,7 +25106,7 @@ 2 3 - 1361 + 1920 @@ -24711,7 +25122,7 @@ 130 131 - 1361 + 1920 @@ -24727,12 +25138,12 @@ 518 519 - 1361 + 1920 73896 73897 - 1361 + 1920 @@ -24748,12 +25159,12 @@ 492 493 - 1361 + 1920 56656 56657 - 1361 + 1920 @@ -24769,12 +25180,12 @@ 518 519 - 1361 + 1920 73896 73897 - 1361 + 1920 @@ -24790,7 +25201,7 @@ 1 2 - 2722 + 3841 @@ -24806,12 +25217,12 @@ 98 99 - 1361 + 1920 130 131 - 1361 + 1920 @@ -24827,57 +25238,57 @@ 1 2 - 14974 + 21129 2 23 - 13613 + 19208 24 243 - 13613 + 19208 294 566 - 13613 + 19208 610 686 - 13613 + 19208 691 764 - 13613 + 19208 765 775 - 13613 + 19208 775 776 - 4084 + 5762 776 777 - 49008 + 69150 777 803 - 13613 + 19208 807 888 - 13613 + 19208 @@ -24893,67 +25304,67 @@ 1 2 - 14974 + 21129 2 21 - 13613 + 19208 22 188 - 13613 + 19208 208 492 - 13613 + 19208 525 589 - 13613 + 19208 590 638 - 13613 + 19208 639 651 - 13613 + 19208 652 656 - 12252 + 17287 656 659 - 16336 + 23050 659 663 - 14974 + 21129 663 667 - 13613 + 19208 667 701 - 13613 + 19208 702 744 - 9529 + 13445 @@ -24969,57 +25380,57 @@ 1 2 - 14974 + 21129 2 23 - 13613 + 19208 24 243 - 13613 + 19208 294 566 - 13613 + 19208 610 686 - 13613 + 19208 691 764 - 13613 + 19208 765 775 - 13613 + 19208 775 776 - 4084 + 5762 776 777 - 49008 + 69150 777 803 - 13613 + 19208 807 888 - 13613 + 19208 @@ -25035,7 +25446,7 @@ 1 2 - 176974 + 249711 @@ -25051,12 +25462,12 @@ 1 2 - 43562 + 61467 2 3 - 133411 + 188243 @@ -25066,15 +25477,15 @@ xmllocations - 446644705 + 630217533 xmlElement - 445369130 + 628417691 location - 418533038 + 590551854 @@ -25088,12 +25499,12 @@ 1 2 - 445360961 + 628406166 2 454 - 8168 + 11525 @@ -25109,12 +25520,12 @@ 1 2 - 409045860 + 577165407 2 25 - 9487177 + 13386446 @@ -25355,19 +25766,19 @@ ktComments - 133411 + 188243 id - 133411 + 188243 kind - 4084 + 5762 text - 96655 + 136380 @@ -25381,7 +25792,7 @@ 1 2 - 133411 + 188243 @@ -25397,7 +25808,7 @@ 1 2 - 133411 + 188243 @@ -25413,17 +25824,17 @@ 16 17 - 1361 + 1920 22 23 - 1361 + 1920 60 61 - 1361 + 1920 @@ -25439,17 +25850,17 @@ 1 2 - 1361 + 1920 16 17 - 1361 + 1920 54 55 - 1361 + 1920 @@ -25465,12 +25876,12 @@ 1 2 - 92571 + 130618 4 23 - 4084 + 5762 @@ -25486,7 +25897,7 @@ 1 2 - 96655 + 136380 @@ -25496,19 +25907,19 @@ ktCommentSections - 59896 + 52611 id - 59896 + 52611 comment - 54765 + 48161 content - 50913 + 44765 @@ -25522,7 +25933,7 @@ 1 2 - 59896 + 52611 @@ -25538,7 +25949,7 @@ 1 2 - 59896 + 52611 @@ -25554,12 +25965,12 @@ 1 2 - 52697 + 46367 2 18 - 2068 + 1793 @@ -25575,12 +25986,12 @@ 1 2 - 52697 + 46367 2 18 - 2068 + 1793 @@ -25596,17 +26007,17 @@ 1 2 - 45040 + 39616 2 3 - 4909 + 4313 3 63 - 963 + 835 @@ -25622,17 +26033,17 @@ 1 2 - 45151 + 39712 2 3 - 4815 + 4231 3 56 - 947 + 821 @@ -25642,15 +26053,15 @@ ktCommentSectionNames - 5130 + 4450 id - 5130 + 4450 name - 15 + 13 @@ -25664,7 +26075,7 @@ 1 2 - 5130 + 4450 @@ -25680,7 +26091,7 @@ 325 326 - 15 + 13 @@ -25690,15 +26101,15 @@ ktCommentSectionSubjectNames - 5130 + 4450 id - 5130 + 4450 subjectname - 3362 + 2916 @@ -25712,7 +26123,7 @@ 1 2 - 5130 + 4450 @@ -25728,22 +26139,22 @@ 1 2 - 2557 + 2218 2 3 - 505 + 438 3 9 - 252 + 219 10 16 - 47 + 41 @@ -25753,15 +26164,15 @@ ktCommentOwners - 85377 + 75329 id - 54008 + 47914 owner - 83356 + 73508 @@ -25775,22 +26186,22 @@ 1 2 - 34510 + 30838 2 3 - 12361 + 10872 3 4 - 4641 + 4025 4 6 - 2494 + 2177 @@ -25806,12 +26217,12 @@ 1 2 - 81335 + 71701 2 - 3 - 2020 + 4 + 1807 @@ -25821,19 +26232,19 @@ ktExtensionFunctions - 702451 + 1206297 id - 702451 + 1206297 typeid - 84403 + 97963 kttypeid - 1361 + 1920 @@ -25847,7 +26258,7 @@ 1 2 - 702451 + 1206297 @@ -25863,7 +26274,7 @@ 1 2 - 702451 + 1206297 @@ -25879,37 +26290,37 @@ 1 2 - 53092 + 55704 2 3 - 5445 + 5762 3 4 - 2722 - - - 4 - 5 - 6806 + 3841 5 - 12 - 6806 + 6 + 13445 - 12 - 69 - 6806 + 7 + 16 + 7683 - 84 - 174 - 2722 + 20 + 87 + 7683 + + + 109 + 227 + 3841 @@ -25925,7 +26336,7 @@ 1 2 - 84403 + 97963 @@ -25939,9 +26350,9 @@ 12 - 516 - 517 - 1361 + 628 + 629 + 1920 @@ -25955,9 +26366,9 @@ 12 - 62 - 63 - 1361 + 51 + 52 + 1920 @@ -25967,15 +26378,15 @@ ktProperties - 30236718 + 21895839 id - 30236718 + 21895839 nodeName - 10667458 + 13542035 @@ -25989,7 +26400,7 @@ 1 2 - 30236718 + 21895839 @@ -26005,22 +26416,17 @@ 1 2 - 7868544 + 11824790 2 - 3 - 1212953 + 4 + 1142909 - 3 - 8 - 807274 - - - 8 - 554 - 778686 + 4 + 352 + 574335 @@ -26030,15 +26436,15 @@ ktPropertyGetters - 4557765 + 5985387 id - 4557765 + 5985387 getter - 4557765 + 5985387 @@ -26052,7 +26458,7 @@ 1 2 - 4557765 + 5985387 @@ -26068,7 +26474,7 @@ 1 2 - 4557765 + 5985387 @@ -26078,15 +26484,15 @@ ktPropertySetters - 264099 + 366883 id - 264099 + 366883 setter - 264099 + 366883 @@ -26100,7 +26506,7 @@ 1 2 - 264099 + 366883 @@ -26116,7 +26522,7 @@ 1 2 - 264099 + 366883 @@ -26126,15 +26532,15 @@ ktPropertyBackingFields - 23552540 + 14423708 id - 23552540 + 14423708 backingField - 23552540 + 14423708 @@ -26148,7 +26554,7 @@ 1 2 - 23552540 + 14423708 @@ -26164,7 +26570,7 @@ 1 2 - 23552540 + 14423708 @@ -26174,15 +26580,15 @@ ktSyntheticBody - 10303 + 9108 id - 10303 + 9108 kind - 2060 + 1821 @@ -26196,7 +26602,7 @@ 1 2 - 10303 + 9108 @@ -26212,7 +26618,7 @@ 5 6 - 2060 + 1821 @@ -26222,37 +26628,37 @@ ktLocalFunction - 2722 + 3841 id - 2722 + 3841 ktInitializerAssignment - 392065 + 199475 id - 392065 + 199475 ktPropertyDelegates - 5650 + 5201 id - 5650 + 5201 variableId - 5650 + 5201 @@ -26266,7 +26672,7 @@ 1 2 - 5650 + 5201 @@ -26282,7 +26688,7 @@ 1 2 - 5650 + 5201 @@ -26292,15 +26698,15 @@ compiler_generated - 533645 + 1467534 id - 533645 + 1467534 kind - 5445 + 13445 @@ -26314,7 +26720,7 @@ 1 2 - 533645 + 1467534 @@ -26328,24 +26734,39 @@ 12 - 2 - 3 - 1361 + 1 + 2 + 1920 8 9 - 1361 + 1920 + + + 38 + 39 + 1920 81 82 - 1361 + 1920 - 301 - 302 - 1361 + 85 + 86 + 1920 + + + 236 + 237 + 1920 + + + 315 + 316 + 1920 @@ -26355,15 +26776,15 @@ ktFunctionOriginalNames - 1215676 + 1586627 id - 1215676 + 1586627 name - 138856 + 186323 @@ -26377,7 +26798,7 @@ 1 2 - 1215676 + 1586627 @@ -26393,22 +26814,22 @@ 1 2 - 111629 + 147905 2 - 7 - 10890 + 4 + 13445 - 7 - 31 - 10890 + 6 + 16 + 15366 - 92 - 380 - 5445 + 22 + 339 + 9604 @@ -26418,11 +26839,11 @@ ktDataClasses - 80319 + 113330 id - 80319 + 113330 From fac839f481708075d8b27dd6f55debd3eff6ebd6 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Thu, 10 Nov 2022 17:31:31 +0000 Subject: [PATCH 0170/1420] Java/Kotlin: Add a changenote for Compilation.getInfo --- java/ql/lib/change-notes/2022-11-10-getInfo.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 java/ql/lib/change-notes/2022-11-10-getInfo.md diff --git a/java/ql/lib/change-notes/2022-11-10-getInfo.md b/java/ql/lib/change-notes/2022-11-10-getInfo.md new file mode 100644 index 00000000000..7a113ca3459 --- /dev/null +++ b/java/ql/lib/change-notes/2022-11-10-getInfo.md @@ -0,0 +1,4 @@ +--- +category: feature +--- +* The new `string Compilation.getInfo(string)` provides access to some information about compilations. From e00f87045e27e57660431de4dcd7c7786e73d74b Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Thu, 10 Nov 2022 20:31:13 +0000 Subject: [PATCH 0171/1420] Java: Add up/downgrade scripts --- .../old.dbscheme | 1246 +++++++++++++++++ .../semmlecode.dbscheme | 1240 ++++++++++++++++ .../upgrade.properties | 3 + .../old.dbscheme | 1240 ++++++++++++++++ .../semmlecode.dbscheme | 1246 +++++++++++++++++ .../upgrade.properties | 2 + 6 files changed, 4977 insertions(+) create mode 100644 java/downgrades/44d61b266bebf261cb027872646262e645efa059/old.dbscheme create mode 100644 java/downgrades/44d61b266bebf261cb027872646262e645efa059/semmlecode.dbscheme create mode 100644 java/downgrades/44d61b266bebf261cb027872646262e645efa059/upgrade.properties create mode 100644 java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/old.dbscheme create mode 100644 java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/semmlecode.dbscheme create mode 100644 java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/upgrade.properties diff --git a/java/downgrades/44d61b266bebf261cb027872646262e645efa059/old.dbscheme b/java/downgrades/44d61b266bebf261cb027872646262e645efa059/old.dbscheme new file mode 100644 index 00000000000..44d61b266be --- /dev/null +++ b/java/downgrades/44d61b266bebf261cb027872646262e645efa059/old.dbscheme @@ -0,0 +1,1246 @@ +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * javac A.java B.java C.java + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * javac A.java B.java C.java + */ + unique int id : @compilation, + int kind: int ref, + string cwd : string ref, + string name : string ref +); + +case @compilation.kind of + 1 = @javacompilation +| 2 = @kotlincompilation +; + +compilation_started( + int id : @compilation ref +) + +compilation_info( + int id : @compilation ref, + string info_key: string ref, + string info_value: string ref +) + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--javac-args` + * 2 | A.java + * 3 | B.java + * 4 | C.java + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | A.java + * 1 | B.java + * 2 | C.java + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * For each file recorded in `compilation_compiling_files`, + * there will be a corresponding row in + * `compilation_compiling_files_completed` once extraction + * of that file is complete. The `result` will indicate the + * extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +#keyset[id, num] +compilation_compiling_files_completed( + int id : @compilation ref, + int num : int ref, + int result : int ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * The `cpu_seconds` and `elapsed_seconds` are the CPU time and elapsed + * time (respectively) that the original compilation (not the extraction) + * took for compiler invocation `id`. + */ +compilation_compiler_times( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + * The `result` will indicate the extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref, + int result : int ref +); + +diagnostics( + unique int id: @diagnostic, + string generated_by: string ref, // TODO: Sync this with the other languages? + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + * External artifacts + */ + +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +snapshotDate( + unique date snapshotDate : date ref +); + +sourceLocationPrefix( + string prefix : string ref +); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/* + * SMAP + */ + +smap_header( + int outputFileId: @file ref, + string outputFilename: string ref, + string defaultStratum: string ref +); + +smap_files( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + string inputFileName: string ref, + int inputFileId: @file ref +); + +smap_lines( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + int inputStartLine: int ref, + int inputLineCount: int ref, + int outputStartLine: int ref, + int outputLineIncrement: int ref +); + +/* + * Locations and files + */ + +@location = @location_default ; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +hasLocation( + int locatableid: @locatable ref, + int id: @location ref +); + +@sourceline = @locatable ; + +#keyset[element_id] +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +/* + * Java + */ + +cupackage( + unique int id: @file ref, + int packageid: @package ref +); + +#keyset[fileid,keyName] +jarManifestMain( + int fileid: @file ref, + string keyName: string ref, + string value: string ref +); + +#keyset[fileid,entryName,keyName] +jarManifestEntries( + int fileid: @file ref, + string entryName: string ref, + string keyName: string ref, + string value: string ref +); + +packages( + unique int id: @package, + string nodeName: string ref +); + +primitives( + unique int id: @primitive, + string nodeName: string ref +); + +modifiers( + unique int id: @modifier, + string nodeName: string ref +); + +/** + * An errortype is used when the extractor is unable to extract a type + * correctly for some reason. + */ +error_type( + unique int id: @errortype +); + +classes( + unique int id: @class, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @class ref +); + +file_class( + int id: @class ref +); + +class_object( + unique int id: @class ref, + unique int instance: @field ref +); + +type_companion_object( + unique int id: @classorinterface ref, + unique int instance: @field ref, + unique int companion_object: @class ref +); + +kt_nullable_types( + unique int id: @kt_nullable_type, + int classid: @reftype ref +) + +kt_notnull_types( + unique int id: @kt_notnull_type, + int classid: @reftype ref +) + +kt_type_alias( + unique int id: @kt_type_alias, + string name: string ref, + int kttypeid: @kt_type ref +) + +@kt_type = @kt_nullable_type | @kt_notnull_type + +isRecord( + unique int id: @class ref +); + +interfaces( + unique int id: @interface, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @interface ref +); + +fielddecls( + unique int id: @fielddecl, + int parentid: @reftype ref +); + +#keyset[fieldId] #keyset[fieldDeclId,pos] +fieldDeclaredIn( + int fieldId: @field ref, + int fieldDeclId: @fielddecl ref, + int pos: int ref +); + +fields( + unique int id: @field, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @field ref +); + +fieldsKotlinType( + unique int id: @field ref, + int kttypeid: @kt_type ref +); + +constrs( + unique int id: @constructor, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @constructor ref +); + +constrsKotlinType( + unique int id: @constructor ref, + int kttypeid: @kt_type ref +); + +methods( + unique int id: @method, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @method ref +); + +methodsKotlinType( + unique int id: @method ref, + int kttypeid: @kt_type ref +); + +#keyset[parentid,pos] +params( + unique int id: @param, + int typeid: @type ref, + int pos: int ref, + int parentid: @callable ref, + int sourceid: @param ref +); + +paramsKotlinType( + unique int id: @param ref, + int kttypeid: @kt_type ref +); + +paramName( + unique int id: @param ref, + string nodeName: string ref +); + +isVarargsParam( + int param: @param ref +); + +exceptions( + unique int id: @exception, + int typeid: @type ref, + int parentid: @callable ref +); + +isAnnotType( + int interfaceid: @interface ref +); + +isAnnotElem( + int methodid: @method ref +); + +annotValue( + int parentid: @annotation ref, + int id2: @method ref, + unique int value: @expr ref +); + +isEnumType( + int classid: @class ref +); + +isEnumConst( + int fieldid: @field ref +); + +#keyset[parentid,pos] +typeVars( + unique int id: @typevariable, + string nodeName: string ref, + int pos: int ref, + int kind: int ref, // deprecated + int parentid: @classorinterfaceorcallable ref +); + +wildcards( + unique int id: @wildcard, + string nodeName: string ref, + int kind: int ref +); + +#keyset[parentid,pos] +typeBounds( + unique int id: @typebound, + int typeid: @reftype ref, + int pos: int ref, + int parentid: @boundedtype ref +); + +#keyset[parentid,pos] +typeArgs( + int argumentid: @reftype ref, + int pos: int ref, + int parentid: @classorinterfaceorcallable ref +); + +isParameterized( + int memberid: @member ref +); + +isRaw( + int memberid: @member ref +); + +erasure( + unique int memberid: @member ref, + int erasureid: @member ref +); + +#keyset[classid] #keyset[parent] +isAnonymClass( + int classid: @class ref, + int parent: @classinstancexpr ref +); + +#keyset[typeid] #keyset[parent] +isLocalClassOrInterface( + int typeid: @classorinterface ref, + int parent: @localtypedeclstmt ref +); + +isDefConstr( + int constructorid: @constructor ref +); + +#keyset[exprId] +lambdaKind( + int exprId: @lambdaexpr ref, + int bodyKind: int ref +); + +arrays( + unique int id: @array, + string nodeName: string ref, + int elementtypeid: @type ref, + int dimension: int ref, + int componenttypeid: @type ref +); + +enclInReftype( + unique int child: @reftype ref, + int parent: @reftype ref +); + +extendsReftype( + int id1: @reftype ref, + int id2: @classorinterface ref +); + +implInterface( + int id1: @classorarray ref, + int id2: @interface ref +); + +permits( + int id1: @classorinterface ref, + int id2: @classorinterface ref +); + +hasModifier( + int id1: @modifiable ref, + int id2: @modifier ref +); + +imports( + unique int id: @import, + int holder: @classorinterfaceorpackage ref, + string name: string ref, + int kind: int ref +); + +#keyset[parent,idx] +stmts( + unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + int bodydecl: @callable ref +); + +@stmtparent = @callable | @stmt | @switchexpr | @whenexpr| @stmtexpr; + +case @stmt.kind of + 0 = @block +| 1 = @ifstmt +| 2 = @forstmt +| 3 = @enhancedforstmt +| 4 = @whilestmt +| 5 = @dostmt +| 6 = @trystmt +| 7 = @switchstmt +| 8 = @synchronizedstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @emptystmt +| 14 = @exprstmt +| 15 = @labeledstmt +| 16 = @assertstmt +| 17 = @localvariabledeclstmt +| 18 = @localtypedeclstmt +| 19 = @constructorinvocationstmt +| 20 = @superconstructorinvocationstmt +| 21 = @case +| 22 = @catchclause +| 23 = @yieldstmt +| 24 = @errorstmt +| 25 = @whenbranch +; + +#keyset[parent,idx] +exprs( + unique int id: @expr, + int kind: int ref, + int typeid: @type ref, + int parent: @exprparent ref, + int idx: int ref +); + +exprsKotlinType( + unique int id: @expr ref, + int kttypeid: @kt_type ref +); + +callableEnclosingExpr( + unique int id: @expr ref, + int callable_id: @callable ref +); + +statementEnclosingExpr( + unique int id: @expr ref, + int statement_id: @stmt ref +); + +isParenthesized( + unique int id: @expr ref, + int parentheses: int ref +); + +case @expr.kind of + 1 = @arrayaccess +| 2 = @arraycreationexpr +| 3 = @arrayinit +| 4 = @assignexpr +| 5 = @assignaddexpr +| 6 = @assignsubexpr +| 7 = @assignmulexpr +| 8 = @assigndivexpr +| 9 = @assignremexpr +| 10 = @assignandexpr +| 11 = @assignorexpr +| 12 = @assignxorexpr +| 13 = @assignlshiftexpr +| 14 = @assignrshiftexpr +| 15 = @assignurshiftexpr +| 16 = @booleanliteral +| 17 = @integerliteral +| 18 = @longliteral +| 19 = @floatingpointliteral +| 20 = @doubleliteral +| 21 = @characterliteral +| 22 = @stringliteral +| 23 = @nullliteral +| 24 = @mulexpr +| 25 = @divexpr +| 26 = @remexpr +| 27 = @addexpr +| 28 = @subexpr +| 29 = @lshiftexpr +| 30 = @rshiftexpr +| 31 = @urshiftexpr +| 32 = @andbitexpr +| 33 = @orbitexpr +| 34 = @xorbitexpr +| 35 = @andlogicalexpr +| 36 = @orlogicalexpr +| 37 = @ltexpr +| 38 = @gtexpr +| 39 = @leexpr +| 40 = @geexpr +| 41 = @eqexpr +| 42 = @neexpr +| 43 = @postincexpr +| 44 = @postdecexpr +| 45 = @preincexpr +| 46 = @predecexpr +| 47 = @minusexpr +| 48 = @plusexpr +| 49 = @bitnotexpr +| 50 = @lognotexpr +| 51 = @castexpr +| 52 = @newexpr +| 53 = @conditionalexpr +| 54 = @parexpr // deprecated +| 55 = @instanceofexpr +| 56 = @localvariabledeclexpr +| 57 = @typeliteral +| 58 = @thisaccess +| 59 = @superaccess +| 60 = @varaccess +| 61 = @methodaccess +| 62 = @unannotatedtypeaccess +| 63 = @arraytypeaccess +| 64 = @packageaccess +| 65 = @wildcardtypeaccess +| 66 = @declannotation +| 67 = @uniontypeaccess +| 68 = @lambdaexpr +| 69 = @memberref +| 70 = @annotatedtypeaccess +| 71 = @typeannotation +| 72 = @intersectiontypeaccess +| 73 = @switchexpr +| 74 = @errorexpr +| 75 = @whenexpr +| 76 = @getclassexpr +| 77 = @safecastexpr +| 78 = @implicitcastexpr +| 79 = @implicitnotnullexpr +| 80 = @implicitcoerciontounitexpr +| 81 = @notinstanceofexpr +| 82 = @stmtexpr +| 83 = @stringtemplateexpr +| 84 = @notnullexpr +| 85 = @unsafecoerceexpr +| 86 = @valueeqexpr +| 87 = @valueneexpr +| 88 = @propertyref +; + +/** Holds if this `when` expression was written as an `if` expression. */ +when_if(unique int id: @whenexpr ref); + +/** Holds if this `when` branch was written as an `else` branch. */ +when_branch_else(unique int id: @whenbranch ref); + +@classinstancexpr = @newexpr | @lambdaexpr | @memberref | @propertyref + +@annotation = @declannotation | @typeannotation +@typeaccess = @unannotatedtypeaccess | @annotatedtypeaccess + +@assignment = @assignexpr + | @assignop; + +@unaryassignment = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr; + +@assignop = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + | @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignurshiftexpr; + +@literal = @booleanliteral + | @integerliteral + | @longliteral + | @floatingpointliteral + | @doubleliteral + | @characterliteral + | @stringliteral + | @nullliteral; + +@binaryexpr = @mulexpr + | @divexpr + | @remexpr + | @addexpr + | @subexpr + | @lshiftexpr + | @rshiftexpr + | @urshiftexpr + | @andbitexpr + | @orbitexpr + | @xorbitexpr + | @andlogicalexpr + | @orlogicalexpr + | @ltexpr + | @gtexpr + | @leexpr + | @geexpr + | @eqexpr + | @neexpr + | @valueeqexpr + | @valueneexpr; + +@unaryexpr = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr + | @minusexpr + | @plusexpr + | @bitnotexpr + | @lognotexpr + | @notnullexpr; + +@caller = @classinstancexpr + | @methodaccess + | @constructorinvocationstmt + | @superconstructorinvocationstmt; + +callableBinding( + unique int callerid: @caller ref, + int callee: @callable ref +); + +memberRefBinding( + unique int id: @expr ref, + int callable: @callable ref +); + +propertyRefGetBinding( + unique int id: @expr ref, + int getter: @callable ref +); + +propertyRefFieldBinding( + unique int id: @expr ref, + int field: @field ref +); + +propertyRefSetBinding( + unique int id: @expr ref, + int setter: @callable ref +); + +@exprparent = @stmt | @expr | @whenbranch | @callable | @field | @fielddecl | @class | @interface | @param | @localvar | @typevariable; + +variableBinding( + unique int expr: @varaccess ref, + int variable: @variable ref +); + +@variable = @localscopevariable | @field; + +@localscopevariable = @localvar | @param; + +localvars( + unique int id: @localvar, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @localvariabledeclexpr ref +); + +localvarsKotlinType( + unique int id: @localvar ref, + int kttypeid: @kt_type ref +); + +@namedexprorstmt = @breakstmt + | @continuestmt + | @labeledstmt + | @literal; + +namestrings( + string name: string ref, + string value: string ref, + unique int parent: @namedexprorstmt ref +); + +/* + * Modules + */ + +#keyset[name] +modules( + unique int id: @module, + string name: string ref +); + +isOpen( + int id: @module ref +); + +#keyset[fileId] +cumodule( + int fileId: @file ref, + int moduleId: @module ref +); + +@directive = @requires + | @exports + | @opens + | @uses + | @provides + +#keyset[directive] +directives( + int id: @module ref, + int directive: @directive ref +); + +requires( + unique int id: @requires, + int target: @module ref +); + +isTransitive( + int id: @requires ref +); + +isStatic( + int id: @requires ref +); + +exports( + unique int id: @exports, + int target: @package ref +); + +exportsTo( + int id: @exports ref, + int target: @module ref +); + +opens( + unique int id: @opens, + int target: @package ref +); + +opensTo( + int id: @opens ref, + int target: @module ref +); + +uses( + unique int id: @uses, + string serviceInterface: string ref +); + +provides( + unique int id: @provides, + string serviceInterface: string ref +); + +providesWith( + int id: @provides ref, + string serviceImpl: string ref +); + +/* + * Javadoc + */ + +javadoc( + unique int id: @javadoc +); + +isNormalComment( + int commentid : @javadoc ref +); + +isEolComment( + int commentid : @javadoc ref +); + +hasJavadoc( + int documentableid: @member ref, + int javadocid: @javadoc ref +); + +#keyset[parentid,idx] +javadocTag( + unique int id: @javadocTag, + string name: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +#keyset[parentid,idx] +javadocText( + unique int id: @javadocText, + string text: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +@javadocParent = @javadoc | @javadocTag; +@javadocElement = @javadocTag | @javadocText; + +@classorinterface = @interface | @class; +@classorinterfaceorpackage = @classorinterface | @package; +@classorinterfaceorcallable = @classorinterface | @callable; +@boundedtype = @typevariable | @wildcard; +@reftype = @classorinterface | @array | @boundedtype | @errortype; +@classorarray = @class | @array; +@type = @primitive | @reftype; +@callable = @method | @constructor; + +/** A program element that has a name. */ +@element = @package | @modifier | @annotation | @errortype | + @locatableElement; + +@locatableElement = @file | @primitive | @class | @interface | @method | @constructor | @param | @exception | @field | + @boundedtype | @array | @localvar | @expr | @stmt | @import | @fielddecl | @kt_type | @kt_type_alias | + @kt_property; + +@modifiable = @member_modifiable| @param | @localvar | @typevariable; + +@member_modifiable = @class | @interface | @method | @constructor | @field | @kt_property; + +@member = @method | @constructor | @field | @reftype ; + +/** A program element that has a location. */ +@locatable = @typebound | @javadoc | @javadocTag | @javadocText | @xmllocatable | @ktcomment | + @locatableElement; + +@top = @element | @locatable | @folder; + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* + * configuration files with key value pairs + */ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; + +ktComments( + unique int id: @ktcomment, + int kind: int ref, + string text : string ref +) + +ktCommentSections( + unique int id: @ktcommentsection, + int comment: @ktcomment ref, + string content : string ref +) + +ktCommentSectionNames( + unique int id: @ktcommentsection ref, + string name : string ref +) + +ktCommentSectionSubjectNames( + unique int id: @ktcommentsection ref, + string subjectname : string ref +) + +#keyset[id, owner] +ktCommentOwners( + int id: @ktcomment ref, + int owner: @top ref +) + +ktExtensionFunctions( + unique int id: @method ref, + int typeid: @type ref, + int kttypeid: @kt_type ref +) + +ktProperties( + unique int id: @kt_property, + string nodeName: string ref +) + +ktPropertyGetters( + unique int id: @kt_property ref, + int getter: @method ref +) + +ktPropertySetters( + unique int id: @kt_property ref, + int setter: @method ref +) + +ktPropertyBackingFields( + unique int id: @kt_property ref, + int backingField: @field ref +) + +ktSyntheticBody( + unique int id: @callable ref, + int kind: int ref + // 1: ENUM_VALUES + // 2: ENUM_VALUEOF +) + +ktLocalFunction( + unique int id: @method ref +) + +ktInitializerAssignment( + unique int id: @assignexpr ref +) + +ktPropertyDelegates( + unique int id: @kt_property ref, + unique int variableId: @variable ref +) + +/** + * If `id` is a compiler generated element, then the kind indicates the + * reason that the compiler generated it. + * See `Element.compilerGeneratedReason()` for an explanation of what + * each `kind` means. + */ +compiler_generated( + unique int id: @element ref, + int kind: int ref +) + +ktFunctionOriginalNames( + unique int id: @method ref, + string name: string ref +) + +ktDataClasses( + unique int id: @class ref +) diff --git a/java/downgrades/44d61b266bebf261cb027872646262e645efa059/semmlecode.dbscheme b/java/downgrades/44d61b266bebf261cb027872646262e645efa059/semmlecode.dbscheme new file mode 100644 index 00000000000..709f1d1fd04 --- /dev/null +++ b/java/downgrades/44d61b266bebf261cb027872646262e645efa059/semmlecode.dbscheme @@ -0,0 +1,1240 @@ +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * javac A.java B.java C.java + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * javac A.java B.java C.java + */ + unique int id : @compilation, + int kind: int ref, + string cwd : string ref, + string name : string ref +); + +case @compilation.kind of + 1 = @javacompilation +| 2 = @kotlincompilation +; + +compilation_started( + int id : @compilation ref +) + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--javac-args` + * 2 | A.java + * 3 | B.java + * 4 | C.java + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | A.java + * 1 | B.java + * 2 | C.java + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * For each file recorded in `compilation_compiling_files`, + * there will be a corresponding row in + * `compilation_compiling_files_completed` once extraction + * of that file is complete. The `result` will indicate the + * extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +#keyset[id, num] +compilation_compiling_files_completed( + int id : @compilation ref, + int num : int ref, + int result : int ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * The `cpu_seconds` and `elapsed_seconds` are the CPU time and elapsed + * time (respectively) that the original compilation (not the extraction) + * took for compiler invocation `id`. + */ +compilation_compiler_times( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + * The `result` will indicate the extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref, + int result : int ref +); + +diagnostics( + unique int id: @diagnostic, + string generated_by: string ref, // TODO: Sync this with the other languages? + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + * External artifacts + */ + +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +snapshotDate( + unique date snapshotDate : date ref +); + +sourceLocationPrefix( + string prefix : string ref +); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/* + * SMAP + */ + +smap_header( + int outputFileId: @file ref, + string outputFilename: string ref, + string defaultStratum: string ref +); + +smap_files( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + string inputFileName: string ref, + int inputFileId: @file ref +); + +smap_lines( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + int inputStartLine: int ref, + int inputLineCount: int ref, + int outputStartLine: int ref, + int outputLineIncrement: int ref +); + +/* + * Locations and files + */ + +@location = @location_default ; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +hasLocation( + int locatableid: @locatable ref, + int id: @location ref +); + +@sourceline = @locatable ; + +#keyset[element_id] +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +/* + * Java + */ + +cupackage( + unique int id: @file ref, + int packageid: @package ref +); + +#keyset[fileid,keyName] +jarManifestMain( + int fileid: @file ref, + string keyName: string ref, + string value: string ref +); + +#keyset[fileid,entryName,keyName] +jarManifestEntries( + int fileid: @file ref, + string entryName: string ref, + string keyName: string ref, + string value: string ref +); + +packages( + unique int id: @package, + string nodeName: string ref +); + +primitives( + unique int id: @primitive, + string nodeName: string ref +); + +modifiers( + unique int id: @modifier, + string nodeName: string ref +); + +/** + * An errortype is used when the extractor is unable to extract a type + * correctly for some reason. + */ +error_type( + unique int id: @errortype +); + +classes( + unique int id: @class, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @class ref +); + +file_class( + int id: @class ref +); + +class_object( + unique int id: @class ref, + unique int instance: @field ref +); + +type_companion_object( + unique int id: @classorinterface ref, + unique int instance: @field ref, + unique int companion_object: @class ref +); + +kt_nullable_types( + unique int id: @kt_nullable_type, + int classid: @reftype ref +) + +kt_notnull_types( + unique int id: @kt_notnull_type, + int classid: @reftype ref +) + +kt_type_alias( + unique int id: @kt_type_alias, + string name: string ref, + int kttypeid: @kt_type ref +) + +@kt_type = @kt_nullable_type | @kt_notnull_type + +isRecord( + unique int id: @class ref +); + +interfaces( + unique int id: @interface, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @interface ref +); + +fielddecls( + unique int id: @fielddecl, + int parentid: @reftype ref +); + +#keyset[fieldId] #keyset[fieldDeclId,pos] +fieldDeclaredIn( + int fieldId: @field ref, + int fieldDeclId: @fielddecl ref, + int pos: int ref +); + +fields( + unique int id: @field, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @field ref +); + +fieldsKotlinType( + unique int id: @field ref, + int kttypeid: @kt_type ref +); + +constrs( + unique int id: @constructor, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @constructor ref +); + +constrsKotlinType( + unique int id: @constructor ref, + int kttypeid: @kt_type ref +); + +methods( + unique int id: @method, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @method ref +); + +methodsKotlinType( + unique int id: @method ref, + int kttypeid: @kt_type ref +); + +#keyset[parentid,pos] +params( + unique int id: @param, + int typeid: @type ref, + int pos: int ref, + int parentid: @callable ref, + int sourceid: @param ref +); + +paramsKotlinType( + unique int id: @param ref, + int kttypeid: @kt_type ref +); + +paramName( + unique int id: @param ref, + string nodeName: string ref +); + +isVarargsParam( + int param: @param ref +); + +exceptions( + unique int id: @exception, + int typeid: @type ref, + int parentid: @callable ref +); + +isAnnotType( + int interfaceid: @interface ref +); + +isAnnotElem( + int methodid: @method ref +); + +annotValue( + int parentid: @annotation ref, + int id2: @method ref, + unique int value: @expr ref +); + +isEnumType( + int classid: @class ref +); + +isEnumConst( + int fieldid: @field ref +); + +#keyset[parentid,pos] +typeVars( + unique int id: @typevariable, + string nodeName: string ref, + int pos: int ref, + int kind: int ref, // deprecated + int parentid: @classorinterfaceorcallable ref +); + +wildcards( + unique int id: @wildcard, + string nodeName: string ref, + int kind: int ref +); + +#keyset[parentid,pos] +typeBounds( + unique int id: @typebound, + int typeid: @reftype ref, + int pos: int ref, + int parentid: @boundedtype ref +); + +#keyset[parentid,pos] +typeArgs( + int argumentid: @reftype ref, + int pos: int ref, + int parentid: @classorinterfaceorcallable ref +); + +isParameterized( + int memberid: @member ref +); + +isRaw( + int memberid: @member ref +); + +erasure( + unique int memberid: @member ref, + int erasureid: @member ref +); + +#keyset[classid] #keyset[parent] +isAnonymClass( + int classid: @class ref, + int parent: @classinstancexpr ref +); + +#keyset[typeid] #keyset[parent] +isLocalClassOrInterface( + int typeid: @classorinterface ref, + int parent: @localtypedeclstmt ref +); + +isDefConstr( + int constructorid: @constructor ref +); + +#keyset[exprId] +lambdaKind( + int exprId: @lambdaexpr ref, + int bodyKind: int ref +); + +arrays( + unique int id: @array, + string nodeName: string ref, + int elementtypeid: @type ref, + int dimension: int ref, + int componenttypeid: @type ref +); + +enclInReftype( + unique int child: @reftype ref, + int parent: @reftype ref +); + +extendsReftype( + int id1: @reftype ref, + int id2: @classorinterface ref +); + +implInterface( + int id1: @classorarray ref, + int id2: @interface ref +); + +permits( + int id1: @classorinterface ref, + int id2: @classorinterface ref +); + +hasModifier( + int id1: @modifiable ref, + int id2: @modifier ref +); + +imports( + unique int id: @import, + int holder: @classorinterfaceorpackage ref, + string name: string ref, + int kind: int ref +); + +#keyset[parent,idx] +stmts( + unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + int bodydecl: @callable ref +); + +@stmtparent = @callable | @stmt | @switchexpr | @whenexpr| @stmtexpr; + +case @stmt.kind of + 0 = @block +| 1 = @ifstmt +| 2 = @forstmt +| 3 = @enhancedforstmt +| 4 = @whilestmt +| 5 = @dostmt +| 6 = @trystmt +| 7 = @switchstmt +| 8 = @synchronizedstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @emptystmt +| 14 = @exprstmt +| 15 = @labeledstmt +| 16 = @assertstmt +| 17 = @localvariabledeclstmt +| 18 = @localtypedeclstmt +| 19 = @constructorinvocationstmt +| 20 = @superconstructorinvocationstmt +| 21 = @case +| 22 = @catchclause +| 23 = @yieldstmt +| 24 = @errorstmt +| 25 = @whenbranch +; + +#keyset[parent,idx] +exprs( + unique int id: @expr, + int kind: int ref, + int typeid: @type ref, + int parent: @exprparent ref, + int idx: int ref +); + +exprsKotlinType( + unique int id: @expr ref, + int kttypeid: @kt_type ref +); + +callableEnclosingExpr( + unique int id: @expr ref, + int callable_id: @callable ref +); + +statementEnclosingExpr( + unique int id: @expr ref, + int statement_id: @stmt ref +); + +isParenthesized( + unique int id: @expr ref, + int parentheses: int ref +); + +case @expr.kind of + 1 = @arrayaccess +| 2 = @arraycreationexpr +| 3 = @arrayinit +| 4 = @assignexpr +| 5 = @assignaddexpr +| 6 = @assignsubexpr +| 7 = @assignmulexpr +| 8 = @assigndivexpr +| 9 = @assignremexpr +| 10 = @assignandexpr +| 11 = @assignorexpr +| 12 = @assignxorexpr +| 13 = @assignlshiftexpr +| 14 = @assignrshiftexpr +| 15 = @assignurshiftexpr +| 16 = @booleanliteral +| 17 = @integerliteral +| 18 = @longliteral +| 19 = @floatingpointliteral +| 20 = @doubleliteral +| 21 = @characterliteral +| 22 = @stringliteral +| 23 = @nullliteral +| 24 = @mulexpr +| 25 = @divexpr +| 26 = @remexpr +| 27 = @addexpr +| 28 = @subexpr +| 29 = @lshiftexpr +| 30 = @rshiftexpr +| 31 = @urshiftexpr +| 32 = @andbitexpr +| 33 = @orbitexpr +| 34 = @xorbitexpr +| 35 = @andlogicalexpr +| 36 = @orlogicalexpr +| 37 = @ltexpr +| 38 = @gtexpr +| 39 = @leexpr +| 40 = @geexpr +| 41 = @eqexpr +| 42 = @neexpr +| 43 = @postincexpr +| 44 = @postdecexpr +| 45 = @preincexpr +| 46 = @predecexpr +| 47 = @minusexpr +| 48 = @plusexpr +| 49 = @bitnotexpr +| 50 = @lognotexpr +| 51 = @castexpr +| 52 = @newexpr +| 53 = @conditionalexpr +| 54 = @parexpr // deprecated +| 55 = @instanceofexpr +| 56 = @localvariabledeclexpr +| 57 = @typeliteral +| 58 = @thisaccess +| 59 = @superaccess +| 60 = @varaccess +| 61 = @methodaccess +| 62 = @unannotatedtypeaccess +| 63 = @arraytypeaccess +| 64 = @packageaccess +| 65 = @wildcardtypeaccess +| 66 = @declannotation +| 67 = @uniontypeaccess +| 68 = @lambdaexpr +| 69 = @memberref +| 70 = @annotatedtypeaccess +| 71 = @typeannotation +| 72 = @intersectiontypeaccess +| 73 = @switchexpr +| 74 = @errorexpr +| 75 = @whenexpr +| 76 = @getclassexpr +| 77 = @safecastexpr +| 78 = @implicitcastexpr +| 79 = @implicitnotnullexpr +| 80 = @implicitcoerciontounitexpr +| 81 = @notinstanceofexpr +| 82 = @stmtexpr +| 83 = @stringtemplateexpr +| 84 = @notnullexpr +| 85 = @unsafecoerceexpr +| 86 = @valueeqexpr +| 87 = @valueneexpr +| 88 = @propertyref +; + +/** Holds if this `when` expression was written as an `if` expression. */ +when_if(unique int id: @whenexpr ref); + +/** Holds if this `when` branch was written as an `else` branch. */ +when_branch_else(unique int id: @whenbranch ref); + +@classinstancexpr = @newexpr | @lambdaexpr | @memberref | @propertyref + +@annotation = @declannotation | @typeannotation +@typeaccess = @unannotatedtypeaccess | @annotatedtypeaccess + +@assignment = @assignexpr + | @assignop; + +@unaryassignment = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr; + +@assignop = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + | @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignurshiftexpr; + +@literal = @booleanliteral + | @integerliteral + | @longliteral + | @floatingpointliteral + | @doubleliteral + | @characterliteral + | @stringliteral + | @nullliteral; + +@binaryexpr = @mulexpr + | @divexpr + | @remexpr + | @addexpr + | @subexpr + | @lshiftexpr + | @rshiftexpr + | @urshiftexpr + | @andbitexpr + | @orbitexpr + | @xorbitexpr + | @andlogicalexpr + | @orlogicalexpr + | @ltexpr + | @gtexpr + | @leexpr + | @geexpr + | @eqexpr + | @neexpr + | @valueeqexpr + | @valueneexpr; + +@unaryexpr = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr + | @minusexpr + | @plusexpr + | @bitnotexpr + | @lognotexpr + | @notnullexpr; + +@caller = @classinstancexpr + | @methodaccess + | @constructorinvocationstmt + | @superconstructorinvocationstmt; + +callableBinding( + unique int callerid: @caller ref, + int callee: @callable ref +); + +memberRefBinding( + unique int id: @expr ref, + int callable: @callable ref +); + +propertyRefGetBinding( + unique int id: @expr ref, + int getter: @callable ref +); + +propertyRefFieldBinding( + unique int id: @expr ref, + int field: @field ref +); + +propertyRefSetBinding( + unique int id: @expr ref, + int setter: @callable ref +); + +@exprparent = @stmt | @expr | @whenbranch | @callable | @field | @fielddecl | @class | @interface | @param | @localvar | @typevariable; + +variableBinding( + unique int expr: @varaccess ref, + int variable: @variable ref +); + +@variable = @localscopevariable | @field; + +@localscopevariable = @localvar | @param; + +localvars( + unique int id: @localvar, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @localvariabledeclexpr ref +); + +localvarsKotlinType( + unique int id: @localvar ref, + int kttypeid: @kt_type ref +); + +@namedexprorstmt = @breakstmt + | @continuestmt + | @labeledstmt + | @literal; + +namestrings( + string name: string ref, + string value: string ref, + unique int parent: @namedexprorstmt ref +); + +/* + * Modules + */ + +#keyset[name] +modules( + unique int id: @module, + string name: string ref +); + +isOpen( + int id: @module ref +); + +#keyset[fileId] +cumodule( + int fileId: @file ref, + int moduleId: @module ref +); + +@directive = @requires + | @exports + | @opens + | @uses + | @provides + +#keyset[directive] +directives( + int id: @module ref, + int directive: @directive ref +); + +requires( + unique int id: @requires, + int target: @module ref +); + +isTransitive( + int id: @requires ref +); + +isStatic( + int id: @requires ref +); + +exports( + unique int id: @exports, + int target: @package ref +); + +exportsTo( + int id: @exports ref, + int target: @module ref +); + +opens( + unique int id: @opens, + int target: @package ref +); + +opensTo( + int id: @opens ref, + int target: @module ref +); + +uses( + unique int id: @uses, + string serviceInterface: string ref +); + +provides( + unique int id: @provides, + string serviceInterface: string ref +); + +providesWith( + int id: @provides ref, + string serviceImpl: string ref +); + +/* + * Javadoc + */ + +javadoc( + unique int id: @javadoc +); + +isNormalComment( + int commentid : @javadoc ref +); + +isEolComment( + int commentid : @javadoc ref +); + +hasJavadoc( + int documentableid: @member ref, + int javadocid: @javadoc ref +); + +#keyset[parentid,idx] +javadocTag( + unique int id: @javadocTag, + string name: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +#keyset[parentid,idx] +javadocText( + unique int id: @javadocText, + string text: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +@javadocParent = @javadoc | @javadocTag; +@javadocElement = @javadocTag | @javadocText; + +@classorinterface = @interface | @class; +@classorinterfaceorpackage = @classorinterface | @package; +@classorinterfaceorcallable = @classorinterface | @callable; +@boundedtype = @typevariable | @wildcard; +@reftype = @classorinterface | @array | @boundedtype | @errortype; +@classorarray = @class | @array; +@type = @primitive | @reftype; +@callable = @method | @constructor; + +/** A program element that has a name. */ +@element = @package | @modifier | @annotation | @errortype | + @locatableElement; + +@locatableElement = @file | @primitive | @class | @interface | @method | @constructor | @param | @exception | @field | + @boundedtype | @array | @localvar | @expr | @stmt | @import | @fielddecl | @kt_type | @kt_type_alias | + @kt_property; + +@modifiable = @member_modifiable| @param | @localvar | @typevariable; + +@member_modifiable = @class | @interface | @method | @constructor | @field | @kt_property; + +@member = @method | @constructor | @field | @reftype ; + +/** A program element that has a location. */ +@locatable = @typebound | @javadoc | @javadocTag | @javadocText | @xmllocatable | @ktcomment | + @locatableElement; + +@top = @element | @locatable | @folder; + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* + * configuration files with key value pairs + */ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; + +ktComments( + unique int id: @ktcomment, + int kind: int ref, + string text : string ref +) + +ktCommentSections( + unique int id: @ktcommentsection, + int comment: @ktcomment ref, + string content : string ref +) + +ktCommentSectionNames( + unique int id: @ktcommentsection ref, + string name : string ref +) + +ktCommentSectionSubjectNames( + unique int id: @ktcommentsection ref, + string subjectname : string ref +) + +#keyset[id, owner] +ktCommentOwners( + int id: @ktcomment ref, + int owner: @top ref +) + +ktExtensionFunctions( + unique int id: @method ref, + int typeid: @type ref, + int kttypeid: @kt_type ref +) + +ktProperties( + unique int id: @kt_property, + string nodeName: string ref +) + +ktPropertyGetters( + unique int id: @kt_property ref, + int getter: @method ref +) + +ktPropertySetters( + unique int id: @kt_property ref, + int setter: @method ref +) + +ktPropertyBackingFields( + unique int id: @kt_property ref, + int backingField: @field ref +) + +ktSyntheticBody( + unique int id: @callable ref, + int kind: int ref + // 1: ENUM_VALUES + // 2: ENUM_VALUEOF +) + +ktLocalFunction( + unique int id: @method ref +) + +ktInitializerAssignment( + unique int id: @assignexpr ref +) + +ktPropertyDelegates( + unique int id: @kt_property ref, + unique int variableId: @variable ref +) + +/** + * If `id` is a compiler generated element, then the kind indicates the + * reason that the compiler generated it. + * See `Element.compilerGeneratedReason()` for an explanation of what + * each `kind` means. + */ +compiler_generated( + unique int id: @element ref, + int kind: int ref +) + +ktFunctionOriginalNames( + unique int id: @method ref, + string name: string ref +) + +ktDataClasses( + unique int id: @class ref +) diff --git a/java/downgrades/44d61b266bebf261cb027872646262e645efa059/upgrade.properties b/java/downgrades/44d61b266bebf261cb027872646262e645efa059/upgrade.properties new file mode 100644 index 00000000000..8d5b41b6e56 --- /dev/null +++ b/java/downgrades/44d61b266bebf261cb027872646262e645efa059/upgrade.properties @@ -0,0 +1,3 @@ +description: Remove compilation_info +compatibility: full +compilation_info.rel: delete diff --git a/java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/old.dbscheme b/java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/old.dbscheme new file mode 100644 index 00000000000..709f1d1fd04 --- /dev/null +++ b/java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/old.dbscheme @@ -0,0 +1,1240 @@ +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * javac A.java B.java C.java + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * javac A.java B.java C.java + */ + unique int id : @compilation, + int kind: int ref, + string cwd : string ref, + string name : string ref +); + +case @compilation.kind of + 1 = @javacompilation +| 2 = @kotlincompilation +; + +compilation_started( + int id : @compilation ref +) + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--javac-args` + * 2 | A.java + * 3 | B.java + * 4 | C.java + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | A.java + * 1 | B.java + * 2 | C.java + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * For each file recorded in `compilation_compiling_files`, + * there will be a corresponding row in + * `compilation_compiling_files_completed` once extraction + * of that file is complete. The `result` will indicate the + * extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +#keyset[id, num] +compilation_compiling_files_completed( + int id : @compilation ref, + int num : int ref, + int result : int ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * The `cpu_seconds` and `elapsed_seconds` are the CPU time and elapsed + * time (respectively) that the original compilation (not the extraction) + * took for compiler invocation `id`. + */ +compilation_compiler_times( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + * The `result` will indicate the extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref, + int result : int ref +); + +diagnostics( + unique int id: @diagnostic, + string generated_by: string ref, // TODO: Sync this with the other languages? + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + * External artifacts + */ + +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +snapshotDate( + unique date snapshotDate : date ref +); + +sourceLocationPrefix( + string prefix : string ref +); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/* + * SMAP + */ + +smap_header( + int outputFileId: @file ref, + string outputFilename: string ref, + string defaultStratum: string ref +); + +smap_files( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + string inputFileName: string ref, + int inputFileId: @file ref +); + +smap_lines( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + int inputStartLine: int ref, + int inputLineCount: int ref, + int outputStartLine: int ref, + int outputLineIncrement: int ref +); + +/* + * Locations and files + */ + +@location = @location_default ; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +hasLocation( + int locatableid: @locatable ref, + int id: @location ref +); + +@sourceline = @locatable ; + +#keyset[element_id] +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +/* + * Java + */ + +cupackage( + unique int id: @file ref, + int packageid: @package ref +); + +#keyset[fileid,keyName] +jarManifestMain( + int fileid: @file ref, + string keyName: string ref, + string value: string ref +); + +#keyset[fileid,entryName,keyName] +jarManifestEntries( + int fileid: @file ref, + string entryName: string ref, + string keyName: string ref, + string value: string ref +); + +packages( + unique int id: @package, + string nodeName: string ref +); + +primitives( + unique int id: @primitive, + string nodeName: string ref +); + +modifiers( + unique int id: @modifier, + string nodeName: string ref +); + +/** + * An errortype is used when the extractor is unable to extract a type + * correctly for some reason. + */ +error_type( + unique int id: @errortype +); + +classes( + unique int id: @class, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @class ref +); + +file_class( + int id: @class ref +); + +class_object( + unique int id: @class ref, + unique int instance: @field ref +); + +type_companion_object( + unique int id: @classorinterface ref, + unique int instance: @field ref, + unique int companion_object: @class ref +); + +kt_nullable_types( + unique int id: @kt_nullable_type, + int classid: @reftype ref +) + +kt_notnull_types( + unique int id: @kt_notnull_type, + int classid: @reftype ref +) + +kt_type_alias( + unique int id: @kt_type_alias, + string name: string ref, + int kttypeid: @kt_type ref +) + +@kt_type = @kt_nullable_type | @kt_notnull_type + +isRecord( + unique int id: @class ref +); + +interfaces( + unique int id: @interface, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @interface ref +); + +fielddecls( + unique int id: @fielddecl, + int parentid: @reftype ref +); + +#keyset[fieldId] #keyset[fieldDeclId,pos] +fieldDeclaredIn( + int fieldId: @field ref, + int fieldDeclId: @fielddecl ref, + int pos: int ref +); + +fields( + unique int id: @field, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @field ref +); + +fieldsKotlinType( + unique int id: @field ref, + int kttypeid: @kt_type ref +); + +constrs( + unique int id: @constructor, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @constructor ref +); + +constrsKotlinType( + unique int id: @constructor ref, + int kttypeid: @kt_type ref +); + +methods( + unique int id: @method, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @method ref +); + +methodsKotlinType( + unique int id: @method ref, + int kttypeid: @kt_type ref +); + +#keyset[parentid,pos] +params( + unique int id: @param, + int typeid: @type ref, + int pos: int ref, + int parentid: @callable ref, + int sourceid: @param ref +); + +paramsKotlinType( + unique int id: @param ref, + int kttypeid: @kt_type ref +); + +paramName( + unique int id: @param ref, + string nodeName: string ref +); + +isVarargsParam( + int param: @param ref +); + +exceptions( + unique int id: @exception, + int typeid: @type ref, + int parentid: @callable ref +); + +isAnnotType( + int interfaceid: @interface ref +); + +isAnnotElem( + int methodid: @method ref +); + +annotValue( + int parentid: @annotation ref, + int id2: @method ref, + unique int value: @expr ref +); + +isEnumType( + int classid: @class ref +); + +isEnumConst( + int fieldid: @field ref +); + +#keyset[parentid,pos] +typeVars( + unique int id: @typevariable, + string nodeName: string ref, + int pos: int ref, + int kind: int ref, // deprecated + int parentid: @classorinterfaceorcallable ref +); + +wildcards( + unique int id: @wildcard, + string nodeName: string ref, + int kind: int ref +); + +#keyset[parentid,pos] +typeBounds( + unique int id: @typebound, + int typeid: @reftype ref, + int pos: int ref, + int parentid: @boundedtype ref +); + +#keyset[parentid,pos] +typeArgs( + int argumentid: @reftype ref, + int pos: int ref, + int parentid: @classorinterfaceorcallable ref +); + +isParameterized( + int memberid: @member ref +); + +isRaw( + int memberid: @member ref +); + +erasure( + unique int memberid: @member ref, + int erasureid: @member ref +); + +#keyset[classid] #keyset[parent] +isAnonymClass( + int classid: @class ref, + int parent: @classinstancexpr ref +); + +#keyset[typeid] #keyset[parent] +isLocalClassOrInterface( + int typeid: @classorinterface ref, + int parent: @localtypedeclstmt ref +); + +isDefConstr( + int constructorid: @constructor ref +); + +#keyset[exprId] +lambdaKind( + int exprId: @lambdaexpr ref, + int bodyKind: int ref +); + +arrays( + unique int id: @array, + string nodeName: string ref, + int elementtypeid: @type ref, + int dimension: int ref, + int componenttypeid: @type ref +); + +enclInReftype( + unique int child: @reftype ref, + int parent: @reftype ref +); + +extendsReftype( + int id1: @reftype ref, + int id2: @classorinterface ref +); + +implInterface( + int id1: @classorarray ref, + int id2: @interface ref +); + +permits( + int id1: @classorinterface ref, + int id2: @classorinterface ref +); + +hasModifier( + int id1: @modifiable ref, + int id2: @modifier ref +); + +imports( + unique int id: @import, + int holder: @classorinterfaceorpackage ref, + string name: string ref, + int kind: int ref +); + +#keyset[parent,idx] +stmts( + unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + int bodydecl: @callable ref +); + +@stmtparent = @callable | @stmt | @switchexpr | @whenexpr| @stmtexpr; + +case @stmt.kind of + 0 = @block +| 1 = @ifstmt +| 2 = @forstmt +| 3 = @enhancedforstmt +| 4 = @whilestmt +| 5 = @dostmt +| 6 = @trystmt +| 7 = @switchstmt +| 8 = @synchronizedstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @emptystmt +| 14 = @exprstmt +| 15 = @labeledstmt +| 16 = @assertstmt +| 17 = @localvariabledeclstmt +| 18 = @localtypedeclstmt +| 19 = @constructorinvocationstmt +| 20 = @superconstructorinvocationstmt +| 21 = @case +| 22 = @catchclause +| 23 = @yieldstmt +| 24 = @errorstmt +| 25 = @whenbranch +; + +#keyset[parent,idx] +exprs( + unique int id: @expr, + int kind: int ref, + int typeid: @type ref, + int parent: @exprparent ref, + int idx: int ref +); + +exprsKotlinType( + unique int id: @expr ref, + int kttypeid: @kt_type ref +); + +callableEnclosingExpr( + unique int id: @expr ref, + int callable_id: @callable ref +); + +statementEnclosingExpr( + unique int id: @expr ref, + int statement_id: @stmt ref +); + +isParenthesized( + unique int id: @expr ref, + int parentheses: int ref +); + +case @expr.kind of + 1 = @arrayaccess +| 2 = @arraycreationexpr +| 3 = @arrayinit +| 4 = @assignexpr +| 5 = @assignaddexpr +| 6 = @assignsubexpr +| 7 = @assignmulexpr +| 8 = @assigndivexpr +| 9 = @assignremexpr +| 10 = @assignandexpr +| 11 = @assignorexpr +| 12 = @assignxorexpr +| 13 = @assignlshiftexpr +| 14 = @assignrshiftexpr +| 15 = @assignurshiftexpr +| 16 = @booleanliteral +| 17 = @integerliteral +| 18 = @longliteral +| 19 = @floatingpointliteral +| 20 = @doubleliteral +| 21 = @characterliteral +| 22 = @stringliteral +| 23 = @nullliteral +| 24 = @mulexpr +| 25 = @divexpr +| 26 = @remexpr +| 27 = @addexpr +| 28 = @subexpr +| 29 = @lshiftexpr +| 30 = @rshiftexpr +| 31 = @urshiftexpr +| 32 = @andbitexpr +| 33 = @orbitexpr +| 34 = @xorbitexpr +| 35 = @andlogicalexpr +| 36 = @orlogicalexpr +| 37 = @ltexpr +| 38 = @gtexpr +| 39 = @leexpr +| 40 = @geexpr +| 41 = @eqexpr +| 42 = @neexpr +| 43 = @postincexpr +| 44 = @postdecexpr +| 45 = @preincexpr +| 46 = @predecexpr +| 47 = @minusexpr +| 48 = @plusexpr +| 49 = @bitnotexpr +| 50 = @lognotexpr +| 51 = @castexpr +| 52 = @newexpr +| 53 = @conditionalexpr +| 54 = @parexpr // deprecated +| 55 = @instanceofexpr +| 56 = @localvariabledeclexpr +| 57 = @typeliteral +| 58 = @thisaccess +| 59 = @superaccess +| 60 = @varaccess +| 61 = @methodaccess +| 62 = @unannotatedtypeaccess +| 63 = @arraytypeaccess +| 64 = @packageaccess +| 65 = @wildcardtypeaccess +| 66 = @declannotation +| 67 = @uniontypeaccess +| 68 = @lambdaexpr +| 69 = @memberref +| 70 = @annotatedtypeaccess +| 71 = @typeannotation +| 72 = @intersectiontypeaccess +| 73 = @switchexpr +| 74 = @errorexpr +| 75 = @whenexpr +| 76 = @getclassexpr +| 77 = @safecastexpr +| 78 = @implicitcastexpr +| 79 = @implicitnotnullexpr +| 80 = @implicitcoerciontounitexpr +| 81 = @notinstanceofexpr +| 82 = @stmtexpr +| 83 = @stringtemplateexpr +| 84 = @notnullexpr +| 85 = @unsafecoerceexpr +| 86 = @valueeqexpr +| 87 = @valueneexpr +| 88 = @propertyref +; + +/** Holds if this `when` expression was written as an `if` expression. */ +when_if(unique int id: @whenexpr ref); + +/** Holds if this `when` branch was written as an `else` branch. */ +when_branch_else(unique int id: @whenbranch ref); + +@classinstancexpr = @newexpr | @lambdaexpr | @memberref | @propertyref + +@annotation = @declannotation | @typeannotation +@typeaccess = @unannotatedtypeaccess | @annotatedtypeaccess + +@assignment = @assignexpr + | @assignop; + +@unaryassignment = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr; + +@assignop = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + | @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignurshiftexpr; + +@literal = @booleanliteral + | @integerliteral + | @longliteral + | @floatingpointliteral + | @doubleliteral + | @characterliteral + | @stringliteral + | @nullliteral; + +@binaryexpr = @mulexpr + | @divexpr + | @remexpr + | @addexpr + | @subexpr + | @lshiftexpr + | @rshiftexpr + | @urshiftexpr + | @andbitexpr + | @orbitexpr + | @xorbitexpr + | @andlogicalexpr + | @orlogicalexpr + | @ltexpr + | @gtexpr + | @leexpr + | @geexpr + | @eqexpr + | @neexpr + | @valueeqexpr + | @valueneexpr; + +@unaryexpr = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr + | @minusexpr + | @plusexpr + | @bitnotexpr + | @lognotexpr + | @notnullexpr; + +@caller = @classinstancexpr + | @methodaccess + | @constructorinvocationstmt + | @superconstructorinvocationstmt; + +callableBinding( + unique int callerid: @caller ref, + int callee: @callable ref +); + +memberRefBinding( + unique int id: @expr ref, + int callable: @callable ref +); + +propertyRefGetBinding( + unique int id: @expr ref, + int getter: @callable ref +); + +propertyRefFieldBinding( + unique int id: @expr ref, + int field: @field ref +); + +propertyRefSetBinding( + unique int id: @expr ref, + int setter: @callable ref +); + +@exprparent = @stmt | @expr | @whenbranch | @callable | @field | @fielddecl | @class | @interface | @param | @localvar | @typevariable; + +variableBinding( + unique int expr: @varaccess ref, + int variable: @variable ref +); + +@variable = @localscopevariable | @field; + +@localscopevariable = @localvar | @param; + +localvars( + unique int id: @localvar, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @localvariabledeclexpr ref +); + +localvarsKotlinType( + unique int id: @localvar ref, + int kttypeid: @kt_type ref +); + +@namedexprorstmt = @breakstmt + | @continuestmt + | @labeledstmt + | @literal; + +namestrings( + string name: string ref, + string value: string ref, + unique int parent: @namedexprorstmt ref +); + +/* + * Modules + */ + +#keyset[name] +modules( + unique int id: @module, + string name: string ref +); + +isOpen( + int id: @module ref +); + +#keyset[fileId] +cumodule( + int fileId: @file ref, + int moduleId: @module ref +); + +@directive = @requires + | @exports + | @opens + | @uses + | @provides + +#keyset[directive] +directives( + int id: @module ref, + int directive: @directive ref +); + +requires( + unique int id: @requires, + int target: @module ref +); + +isTransitive( + int id: @requires ref +); + +isStatic( + int id: @requires ref +); + +exports( + unique int id: @exports, + int target: @package ref +); + +exportsTo( + int id: @exports ref, + int target: @module ref +); + +opens( + unique int id: @opens, + int target: @package ref +); + +opensTo( + int id: @opens ref, + int target: @module ref +); + +uses( + unique int id: @uses, + string serviceInterface: string ref +); + +provides( + unique int id: @provides, + string serviceInterface: string ref +); + +providesWith( + int id: @provides ref, + string serviceImpl: string ref +); + +/* + * Javadoc + */ + +javadoc( + unique int id: @javadoc +); + +isNormalComment( + int commentid : @javadoc ref +); + +isEolComment( + int commentid : @javadoc ref +); + +hasJavadoc( + int documentableid: @member ref, + int javadocid: @javadoc ref +); + +#keyset[parentid,idx] +javadocTag( + unique int id: @javadocTag, + string name: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +#keyset[parentid,idx] +javadocText( + unique int id: @javadocText, + string text: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +@javadocParent = @javadoc | @javadocTag; +@javadocElement = @javadocTag | @javadocText; + +@classorinterface = @interface | @class; +@classorinterfaceorpackage = @classorinterface | @package; +@classorinterfaceorcallable = @classorinterface | @callable; +@boundedtype = @typevariable | @wildcard; +@reftype = @classorinterface | @array | @boundedtype | @errortype; +@classorarray = @class | @array; +@type = @primitive | @reftype; +@callable = @method | @constructor; + +/** A program element that has a name. */ +@element = @package | @modifier | @annotation | @errortype | + @locatableElement; + +@locatableElement = @file | @primitive | @class | @interface | @method | @constructor | @param | @exception | @field | + @boundedtype | @array | @localvar | @expr | @stmt | @import | @fielddecl | @kt_type | @kt_type_alias | + @kt_property; + +@modifiable = @member_modifiable| @param | @localvar | @typevariable; + +@member_modifiable = @class | @interface | @method | @constructor | @field | @kt_property; + +@member = @method | @constructor | @field | @reftype ; + +/** A program element that has a location. */ +@locatable = @typebound | @javadoc | @javadocTag | @javadocText | @xmllocatable | @ktcomment | + @locatableElement; + +@top = @element | @locatable | @folder; + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* + * configuration files with key value pairs + */ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; + +ktComments( + unique int id: @ktcomment, + int kind: int ref, + string text : string ref +) + +ktCommentSections( + unique int id: @ktcommentsection, + int comment: @ktcomment ref, + string content : string ref +) + +ktCommentSectionNames( + unique int id: @ktcommentsection ref, + string name : string ref +) + +ktCommentSectionSubjectNames( + unique int id: @ktcommentsection ref, + string subjectname : string ref +) + +#keyset[id, owner] +ktCommentOwners( + int id: @ktcomment ref, + int owner: @top ref +) + +ktExtensionFunctions( + unique int id: @method ref, + int typeid: @type ref, + int kttypeid: @kt_type ref +) + +ktProperties( + unique int id: @kt_property, + string nodeName: string ref +) + +ktPropertyGetters( + unique int id: @kt_property ref, + int getter: @method ref +) + +ktPropertySetters( + unique int id: @kt_property ref, + int setter: @method ref +) + +ktPropertyBackingFields( + unique int id: @kt_property ref, + int backingField: @field ref +) + +ktSyntheticBody( + unique int id: @callable ref, + int kind: int ref + // 1: ENUM_VALUES + // 2: ENUM_VALUEOF +) + +ktLocalFunction( + unique int id: @method ref +) + +ktInitializerAssignment( + unique int id: @assignexpr ref +) + +ktPropertyDelegates( + unique int id: @kt_property ref, + unique int variableId: @variable ref +) + +/** + * If `id` is a compiler generated element, then the kind indicates the + * reason that the compiler generated it. + * See `Element.compilerGeneratedReason()` for an explanation of what + * each `kind` means. + */ +compiler_generated( + unique int id: @element ref, + int kind: int ref +) + +ktFunctionOriginalNames( + unique int id: @method ref, + string name: string ref +) + +ktDataClasses( + unique int id: @class ref +) diff --git a/java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/semmlecode.dbscheme b/java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/semmlecode.dbscheme new file mode 100644 index 00000000000..44d61b266be --- /dev/null +++ b/java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/semmlecode.dbscheme @@ -0,0 +1,1246 @@ +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * javac A.java B.java C.java + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * javac A.java B.java C.java + */ + unique int id : @compilation, + int kind: int ref, + string cwd : string ref, + string name : string ref +); + +case @compilation.kind of + 1 = @javacompilation +| 2 = @kotlincompilation +; + +compilation_started( + int id : @compilation ref +) + +compilation_info( + int id : @compilation ref, + string info_key: string ref, + string info_value: string ref +) + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--javac-args` + * 2 | A.java + * 3 | B.java + * 4 | C.java + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | A.java + * 1 | B.java + * 2 | C.java + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * For each file recorded in `compilation_compiling_files`, + * there will be a corresponding row in + * `compilation_compiling_files_completed` once extraction + * of that file is complete. The `result` will indicate the + * extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +#keyset[id, num] +compilation_compiling_files_completed( + int id : @compilation ref, + int num : int ref, + int result : int ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * The `cpu_seconds` and `elapsed_seconds` are the CPU time and elapsed + * time (respectively) that the original compilation (not the extraction) + * took for compiler invocation `id`. + */ +compilation_compiler_times( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + * The `result` will indicate the extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref, + int result : int ref +); + +diagnostics( + unique int id: @diagnostic, + string generated_by: string ref, // TODO: Sync this with the other languages? + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + * External artifacts + */ + +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +snapshotDate( + unique date snapshotDate : date ref +); + +sourceLocationPrefix( + string prefix : string ref +); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/* + * SMAP + */ + +smap_header( + int outputFileId: @file ref, + string outputFilename: string ref, + string defaultStratum: string ref +); + +smap_files( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + string inputFileName: string ref, + int inputFileId: @file ref +); + +smap_lines( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + int inputStartLine: int ref, + int inputLineCount: int ref, + int outputStartLine: int ref, + int outputLineIncrement: int ref +); + +/* + * Locations and files + */ + +@location = @location_default ; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +hasLocation( + int locatableid: @locatable ref, + int id: @location ref +); + +@sourceline = @locatable ; + +#keyset[element_id] +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +/* + * Java + */ + +cupackage( + unique int id: @file ref, + int packageid: @package ref +); + +#keyset[fileid,keyName] +jarManifestMain( + int fileid: @file ref, + string keyName: string ref, + string value: string ref +); + +#keyset[fileid,entryName,keyName] +jarManifestEntries( + int fileid: @file ref, + string entryName: string ref, + string keyName: string ref, + string value: string ref +); + +packages( + unique int id: @package, + string nodeName: string ref +); + +primitives( + unique int id: @primitive, + string nodeName: string ref +); + +modifiers( + unique int id: @modifier, + string nodeName: string ref +); + +/** + * An errortype is used when the extractor is unable to extract a type + * correctly for some reason. + */ +error_type( + unique int id: @errortype +); + +classes( + unique int id: @class, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @class ref +); + +file_class( + int id: @class ref +); + +class_object( + unique int id: @class ref, + unique int instance: @field ref +); + +type_companion_object( + unique int id: @classorinterface ref, + unique int instance: @field ref, + unique int companion_object: @class ref +); + +kt_nullable_types( + unique int id: @kt_nullable_type, + int classid: @reftype ref +) + +kt_notnull_types( + unique int id: @kt_notnull_type, + int classid: @reftype ref +) + +kt_type_alias( + unique int id: @kt_type_alias, + string name: string ref, + int kttypeid: @kt_type ref +) + +@kt_type = @kt_nullable_type | @kt_notnull_type + +isRecord( + unique int id: @class ref +); + +interfaces( + unique int id: @interface, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @interface ref +); + +fielddecls( + unique int id: @fielddecl, + int parentid: @reftype ref +); + +#keyset[fieldId] #keyset[fieldDeclId,pos] +fieldDeclaredIn( + int fieldId: @field ref, + int fieldDeclId: @fielddecl ref, + int pos: int ref +); + +fields( + unique int id: @field, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @field ref +); + +fieldsKotlinType( + unique int id: @field ref, + int kttypeid: @kt_type ref +); + +constrs( + unique int id: @constructor, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @constructor ref +); + +constrsKotlinType( + unique int id: @constructor ref, + int kttypeid: @kt_type ref +); + +methods( + unique int id: @method, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @method ref +); + +methodsKotlinType( + unique int id: @method ref, + int kttypeid: @kt_type ref +); + +#keyset[parentid,pos] +params( + unique int id: @param, + int typeid: @type ref, + int pos: int ref, + int parentid: @callable ref, + int sourceid: @param ref +); + +paramsKotlinType( + unique int id: @param ref, + int kttypeid: @kt_type ref +); + +paramName( + unique int id: @param ref, + string nodeName: string ref +); + +isVarargsParam( + int param: @param ref +); + +exceptions( + unique int id: @exception, + int typeid: @type ref, + int parentid: @callable ref +); + +isAnnotType( + int interfaceid: @interface ref +); + +isAnnotElem( + int methodid: @method ref +); + +annotValue( + int parentid: @annotation ref, + int id2: @method ref, + unique int value: @expr ref +); + +isEnumType( + int classid: @class ref +); + +isEnumConst( + int fieldid: @field ref +); + +#keyset[parentid,pos] +typeVars( + unique int id: @typevariable, + string nodeName: string ref, + int pos: int ref, + int kind: int ref, // deprecated + int parentid: @classorinterfaceorcallable ref +); + +wildcards( + unique int id: @wildcard, + string nodeName: string ref, + int kind: int ref +); + +#keyset[parentid,pos] +typeBounds( + unique int id: @typebound, + int typeid: @reftype ref, + int pos: int ref, + int parentid: @boundedtype ref +); + +#keyset[parentid,pos] +typeArgs( + int argumentid: @reftype ref, + int pos: int ref, + int parentid: @classorinterfaceorcallable ref +); + +isParameterized( + int memberid: @member ref +); + +isRaw( + int memberid: @member ref +); + +erasure( + unique int memberid: @member ref, + int erasureid: @member ref +); + +#keyset[classid] #keyset[parent] +isAnonymClass( + int classid: @class ref, + int parent: @classinstancexpr ref +); + +#keyset[typeid] #keyset[parent] +isLocalClassOrInterface( + int typeid: @classorinterface ref, + int parent: @localtypedeclstmt ref +); + +isDefConstr( + int constructorid: @constructor ref +); + +#keyset[exprId] +lambdaKind( + int exprId: @lambdaexpr ref, + int bodyKind: int ref +); + +arrays( + unique int id: @array, + string nodeName: string ref, + int elementtypeid: @type ref, + int dimension: int ref, + int componenttypeid: @type ref +); + +enclInReftype( + unique int child: @reftype ref, + int parent: @reftype ref +); + +extendsReftype( + int id1: @reftype ref, + int id2: @classorinterface ref +); + +implInterface( + int id1: @classorarray ref, + int id2: @interface ref +); + +permits( + int id1: @classorinterface ref, + int id2: @classorinterface ref +); + +hasModifier( + int id1: @modifiable ref, + int id2: @modifier ref +); + +imports( + unique int id: @import, + int holder: @classorinterfaceorpackage ref, + string name: string ref, + int kind: int ref +); + +#keyset[parent,idx] +stmts( + unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + int bodydecl: @callable ref +); + +@stmtparent = @callable | @stmt | @switchexpr | @whenexpr| @stmtexpr; + +case @stmt.kind of + 0 = @block +| 1 = @ifstmt +| 2 = @forstmt +| 3 = @enhancedforstmt +| 4 = @whilestmt +| 5 = @dostmt +| 6 = @trystmt +| 7 = @switchstmt +| 8 = @synchronizedstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @emptystmt +| 14 = @exprstmt +| 15 = @labeledstmt +| 16 = @assertstmt +| 17 = @localvariabledeclstmt +| 18 = @localtypedeclstmt +| 19 = @constructorinvocationstmt +| 20 = @superconstructorinvocationstmt +| 21 = @case +| 22 = @catchclause +| 23 = @yieldstmt +| 24 = @errorstmt +| 25 = @whenbranch +; + +#keyset[parent,idx] +exprs( + unique int id: @expr, + int kind: int ref, + int typeid: @type ref, + int parent: @exprparent ref, + int idx: int ref +); + +exprsKotlinType( + unique int id: @expr ref, + int kttypeid: @kt_type ref +); + +callableEnclosingExpr( + unique int id: @expr ref, + int callable_id: @callable ref +); + +statementEnclosingExpr( + unique int id: @expr ref, + int statement_id: @stmt ref +); + +isParenthesized( + unique int id: @expr ref, + int parentheses: int ref +); + +case @expr.kind of + 1 = @arrayaccess +| 2 = @arraycreationexpr +| 3 = @arrayinit +| 4 = @assignexpr +| 5 = @assignaddexpr +| 6 = @assignsubexpr +| 7 = @assignmulexpr +| 8 = @assigndivexpr +| 9 = @assignremexpr +| 10 = @assignandexpr +| 11 = @assignorexpr +| 12 = @assignxorexpr +| 13 = @assignlshiftexpr +| 14 = @assignrshiftexpr +| 15 = @assignurshiftexpr +| 16 = @booleanliteral +| 17 = @integerliteral +| 18 = @longliteral +| 19 = @floatingpointliteral +| 20 = @doubleliteral +| 21 = @characterliteral +| 22 = @stringliteral +| 23 = @nullliteral +| 24 = @mulexpr +| 25 = @divexpr +| 26 = @remexpr +| 27 = @addexpr +| 28 = @subexpr +| 29 = @lshiftexpr +| 30 = @rshiftexpr +| 31 = @urshiftexpr +| 32 = @andbitexpr +| 33 = @orbitexpr +| 34 = @xorbitexpr +| 35 = @andlogicalexpr +| 36 = @orlogicalexpr +| 37 = @ltexpr +| 38 = @gtexpr +| 39 = @leexpr +| 40 = @geexpr +| 41 = @eqexpr +| 42 = @neexpr +| 43 = @postincexpr +| 44 = @postdecexpr +| 45 = @preincexpr +| 46 = @predecexpr +| 47 = @minusexpr +| 48 = @plusexpr +| 49 = @bitnotexpr +| 50 = @lognotexpr +| 51 = @castexpr +| 52 = @newexpr +| 53 = @conditionalexpr +| 54 = @parexpr // deprecated +| 55 = @instanceofexpr +| 56 = @localvariabledeclexpr +| 57 = @typeliteral +| 58 = @thisaccess +| 59 = @superaccess +| 60 = @varaccess +| 61 = @methodaccess +| 62 = @unannotatedtypeaccess +| 63 = @arraytypeaccess +| 64 = @packageaccess +| 65 = @wildcardtypeaccess +| 66 = @declannotation +| 67 = @uniontypeaccess +| 68 = @lambdaexpr +| 69 = @memberref +| 70 = @annotatedtypeaccess +| 71 = @typeannotation +| 72 = @intersectiontypeaccess +| 73 = @switchexpr +| 74 = @errorexpr +| 75 = @whenexpr +| 76 = @getclassexpr +| 77 = @safecastexpr +| 78 = @implicitcastexpr +| 79 = @implicitnotnullexpr +| 80 = @implicitcoerciontounitexpr +| 81 = @notinstanceofexpr +| 82 = @stmtexpr +| 83 = @stringtemplateexpr +| 84 = @notnullexpr +| 85 = @unsafecoerceexpr +| 86 = @valueeqexpr +| 87 = @valueneexpr +| 88 = @propertyref +; + +/** Holds if this `when` expression was written as an `if` expression. */ +when_if(unique int id: @whenexpr ref); + +/** Holds if this `when` branch was written as an `else` branch. */ +when_branch_else(unique int id: @whenbranch ref); + +@classinstancexpr = @newexpr | @lambdaexpr | @memberref | @propertyref + +@annotation = @declannotation | @typeannotation +@typeaccess = @unannotatedtypeaccess | @annotatedtypeaccess + +@assignment = @assignexpr + | @assignop; + +@unaryassignment = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr; + +@assignop = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + | @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignurshiftexpr; + +@literal = @booleanliteral + | @integerliteral + | @longliteral + | @floatingpointliteral + | @doubleliteral + | @characterliteral + | @stringliteral + | @nullliteral; + +@binaryexpr = @mulexpr + | @divexpr + | @remexpr + | @addexpr + | @subexpr + | @lshiftexpr + | @rshiftexpr + | @urshiftexpr + | @andbitexpr + | @orbitexpr + | @xorbitexpr + | @andlogicalexpr + | @orlogicalexpr + | @ltexpr + | @gtexpr + | @leexpr + | @geexpr + | @eqexpr + | @neexpr + | @valueeqexpr + | @valueneexpr; + +@unaryexpr = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr + | @minusexpr + | @plusexpr + | @bitnotexpr + | @lognotexpr + | @notnullexpr; + +@caller = @classinstancexpr + | @methodaccess + | @constructorinvocationstmt + | @superconstructorinvocationstmt; + +callableBinding( + unique int callerid: @caller ref, + int callee: @callable ref +); + +memberRefBinding( + unique int id: @expr ref, + int callable: @callable ref +); + +propertyRefGetBinding( + unique int id: @expr ref, + int getter: @callable ref +); + +propertyRefFieldBinding( + unique int id: @expr ref, + int field: @field ref +); + +propertyRefSetBinding( + unique int id: @expr ref, + int setter: @callable ref +); + +@exprparent = @stmt | @expr | @whenbranch | @callable | @field | @fielddecl | @class | @interface | @param | @localvar | @typevariable; + +variableBinding( + unique int expr: @varaccess ref, + int variable: @variable ref +); + +@variable = @localscopevariable | @field; + +@localscopevariable = @localvar | @param; + +localvars( + unique int id: @localvar, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @localvariabledeclexpr ref +); + +localvarsKotlinType( + unique int id: @localvar ref, + int kttypeid: @kt_type ref +); + +@namedexprorstmt = @breakstmt + | @continuestmt + | @labeledstmt + | @literal; + +namestrings( + string name: string ref, + string value: string ref, + unique int parent: @namedexprorstmt ref +); + +/* + * Modules + */ + +#keyset[name] +modules( + unique int id: @module, + string name: string ref +); + +isOpen( + int id: @module ref +); + +#keyset[fileId] +cumodule( + int fileId: @file ref, + int moduleId: @module ref +); + +@directive = @requires + | @exports + | @opens + | @uses + | @provides + +#keyset[directive] +directives( + int id: @module ref, + int directive: @directive ref +); + +requires( + unique int id: @requires, + int target: @module ref +); + +isTransitive( + int id: @requires ref +); + +isStatic( + int id: @requires ref +); + +exports( + unique int id: @exports, + int target: @package ref +); + +exportsTo( + int id: @exports ref, + int target: @module ref +); + +opens( + unique int id: @opens, + int target: @package ref +); + +opensTo( + int id: @opens ref, + int target: @module ref +); + +uses( + unique int id: @uses, + string serviceInterface: string ref +); + +provides( + unique int id: @provides, + string serviceInterface: string ref +); + +providesWith( + int id: @provides ref, + string serviceImpl: string ref +); + +/* + * Javadoc + */ + +javadoc( + unique int id: @javadoc +); + +isNormalComment( + int commentid : @javadoc ref +); + +isEolComment( + int commentid : @javadoc ref +); + +hasJavadoc( + int documentableid: @member ref, + int javadocid: @javadoc ref +); + +#keyset[parentid,idx] +javadocTag( + unique int id: @javadocTag, + string name: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +#keyset[parentid,idx] +javadocText( + unique int id: @javadocText, + string text: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +@javadocParent = @javadoc | @javadocTag; +@javadocElement = @javadocTag | @javadocText; + +@classorinterface = @interface | @class; +@classorinterfaceorpackage = @classorinterface | @package; +@classorinterfaceorcallable = @classorinterface | @callable; +@boundedtype = @typevariable | @wildcard; +@reftype = @classorinterface | @array | @boundedtype | @errortype; +@classorarray = @class | @array; +@type = @primitive | @reftype; +@callable = @method | @constructor; + +/** A program element that has a name. */ +@element = @package | @modifier | @annotation | @errortype | + @locatableElement; + +@locatableElement = @file | @primitive | @class | @interface | @method | @constructor | @param | @exception | @field | + @boundedtype | @array | @localvar | @expr | @stmt | @import | @fielddecl | @kt_type | @kt_type_alias | + @kt_property; + +@modifiable = @member_modifiable| @param | @localvar | @typevariable; + +@member_modifiable = @class | @interface | @method | @constructor | @field | @kt_property; + +@member = @method | @constructor | @field | @reftype ; + +/** A program element that has a location. */ +@locatable = @typebound | @javadoc | @javadocTag | @javadocText | @xmllocatable | @ktcomment | + @locatableElement; + +@top = @element | @locatable | @folder; + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* + * configuration files with key value pairs + */ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; + +ktComments( + unique int id: @ktcomment, + int kind: int ref, + string text : string ref +) + +ktCommentSections( + unique int id: @ktcommentsection, + int comment: @ktcomment ref, + string content : string ref +) + +ktCommentSectionNames( + unique int id: @ktcommentsection ref, + string name : string ref +) + +ktCommentSectionSubjectNames( + unique int id: @ktcommentsection ref, + string subjectname : string ref +) + +#keyset[id, owner] +ktCommentOwners( + int id: @ktcomment ref, + int owner: @top ref +) + +ktExtensionFunctions( + unique int id: @method ref, + int typeid: @type ref, + int kttypeid: @kt_type ref +) + +ktProperties( + unique int id: @kt_property, + string nodeName: string ref +) + +ktPropertyGetters( + unique int id: @kt_property ref, + int getter: @method ref +) + +ktPropertySetters( + unique int id: @kt_property ref, + int setter: @method ref +) + +ktPropertyBackingFields( + unique int id: @kt_property ref, + int backingField: @field ref +) + +ktSyntheticBody( + unique int id: @callable ref, + int kind: int ref + // 1: ENUM_VALUES + // 2: ENUM_VALUEOF +) + +ktLocalFunction( + unique int id: @method ref +) + +ktInitializerAssignment( + unique int id: @assignexpr ref +) + +ktPropertyDelegates( + unique int id: @kt_property ref, + unique int variableId: @variable ref +) + +/** + * If `id` is a compiler generated element, then the kind indicates the + * reason that the compiler generated it. + * See `Element.compilerGeneratedReason()` for an explanation of what + * each `kind` means. + */ +compiler_generated( + unique int id: @element ref, + int kind: int ref +) + +ktFunctionOriginalNames( + unique int id: @method ref, + string name: string ref +) + +ktDataClasses( + unique int id: @class ref +) diff --git a/java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/upgrade.properties b/java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/upgrade.properties new file mode 100644 index 00000000000..1c05ac39dbe --- /dev/null +++ b/java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/upgrade.properties @@ -0,0 +1,2 @@ +description: Add compilation_info +compatibility: backwards From 88f703af1fad7bf516a54227f25d24ecf17c970b Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 10 Nov 2022 22:13:34 +0100 Subject: [PATCH 0172/1420] DataFlow: Accept changes to `.expected` --- .../dataflow-consistency.expected | 2 + .../dataflow-ir-consistency.expected | 2 + .../fields/dataflow-consistency.expected | 2 + .../fields/dataflow-ir-consistency.expected | 2 + .../syntax-zoo/dataflow-consistency.expected | 2 + .../dataflow-ir-consistency.expected | 2 + .../basic/dataflow-consistency.expected | 2 + .../calls/dataflow-consistency.expected | 2 + .../consistency/dataflow-consistency.expected | 2 + .../coverage/dataflow-consistency.expected | 2 + .../fieldflow/dataflow-consistency.expected | 2 + .../global-flow/dataflow-consistency.expected | 2 + .../match/dataflow-consistency.expected | 2 + .../pep_328/dataflow-consistency.expected | 2 + .../regression/dataflow-consistency.expected | 2 + .../dataflow-consistency.expected | 2 + .../basic/dataflow-consistency.expected | 2 + .../dataflow-consistency.expected | 2 + .../dataflow-consistency.expected | 2 + .../dataflow-consistency.expected | 2 + .../dataflow-consistency.expected | 2 + .../dataflow-consistency.expected | 2 + .../dataflow-consistency.expected | 2 + .../dataflow-consistency.expected | 2 + .../py3/dataflow-consistency.expected | 2 + .../django-orm/dataflow-consistency.expected | 50 +++++++++++++++++++ 26 files changed, 100 insertions(+) diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected index 6b24d8bdbc6..f38d5943d9d 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected index 929b5b69bc4..182f679710c 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected @@ -21,6 +21,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected index f66ef23ba74..8a9e15049fc 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected @@ -12,6 +12,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected index 4dee8fb627f..c1b3d0c66b7 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected @@ -15,6 +15,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected index 086fa0415b7..1980b113311 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected @@ -52,6 +52,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected index 70f8446ec5a..26abe41c33f 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected @@ -1463,6 +1463,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/experimental/dataflow/basic/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/basic/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/experimental/dataflow/basic/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/basic/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/experimental/dataflow/calls/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/calls/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/experimental/dataflow/calls/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/calls/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/coverage/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/experimental/dataflow/global-flow/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/global-flow/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/experimental/dataflow/global-flow/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/global-flow/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/experimental/dataflow/match/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/match/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/experimental/dataflow/match/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/match/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/experimental/dataflow/pep_328/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/pep_328/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/experimental/dataflow/pep_328/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/pep_328/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/experimental/dataflow/regression/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/regression/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/experimental/dataflow/regression/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/regression/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/experimental/dataflow/strange-essaflow/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/strange-essaflow/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/experimental/dataflow/strange-essaflow/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/strange-essaflow/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/experimental/dataflow/tainttracking/basic/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/tainttracking/basic/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/basic/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/basic/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/experimental/dataflow/tainttracking/unwanted-global-flow/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/tainttracking/unwanted-global-flow/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/unwanted-global-flow/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/unwanted-global-flow/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/experimental/dataflow/typetracking/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/typetracking/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/experimental/dataflow/typetracking/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/typetracking/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/experimental/dataflow/variable-capture/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/variable-capture/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/experimental/dataflow/variable-capture/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/variable-capture/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/library-tests/ApiGraphs/py3/dataflow-consistency.expected b/python/ql/test/library-tests/ApiGraphs/py3/dataflow-consistency.expected index 9fedaf9f663..8f4dbd04742 100644 --- a/python/ql/test/library-tests/ApiGraphs/py3/dataflow-consistency.expected +++ b/python/ql/test/library-tests/ApiGraphs/py3/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/python/ql/test/library-tests/frameworks/django-orm/dataflow-consistency.expected b/python/ql/test/library-tests/frameworks/django-orm/dataflow-consistency.expected index 9fedaf9f663..06a8a168262 100644 --- a/python/ql/test/library-tests/frameworks/django-orm/dataflow-consistency.expected +++ b/python/ql/test/library-tests/frameworks/django-orm/dataflow-consistency.expected @@ -6,6 +6,56 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal +| testapp/orm_form_test.py:6:1:6:28 | [orm-model] Class MyModel | testapp/tests.py:83:16:83:36 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_form_test.py:6:1:6:28 | [orm-model] Class MyModel | testapp/tests.py:84:16:84:43 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_form_test.py:6:1:6:28 | [orm-model] Class MyModel | testapp/tests.py:85:16:85:36 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_inheritance.py:45:15:45:20 | ControlFlowNode for SOURCE | testapp/orm_inheritance.py:29:1:29:25 | [orm-model] Class Book | Store step does not preserve enclosing callable. | +| testapp/orm_inheritance.py:76:15:76:20 | ControlFlowNode for SOURCE | testapp/orm_inheritance.py:29:1:29:25 | [orm-model] Class Book | Store step does not preserve enclosing callable. | +| testapp/orm_inheritance.py:76:15:76:20 | ControlFlowNode for SOURCE | testapp/orm_inheritance.py:33:1:33:25 | [orm-model] Class PhysicalBook | Store step does not preserve enclosing callable. | +| testapp/orm_inheritance.py:77:27:77:32 | ControlFlowNode for SOURCE | testapp/orm_inheritance.py:33:1:33:25 | [orm-model] Class PhysicalBook | Store step does not preserve enclosing callable. | +| testapp/orm_inheritance.py:78:35:78:40 | ControlFlowNode for SOURCE | testapp/orm_inheritance.py:33:1:33:25 | [orm-model] Class PhysicalBook | Store step does not preserve enclosing callable. | +| testapp/orm_inheritance.py:93:15:93:26 | ControlFlowNode for Str | testapp/orm_inheritance.py:29:1:29:25 | [orm-model] Class Book | Store step does not preserve enclosing callable. | +| testapp/orm_inheritance.py:93:15:93:26 | ControlFlowNode for Str | testapp/orm_inheritance.py:38:1:38:18 | [orm-model] Class EBook | Store step does not preserve enclosing callable. | +| testapp/orm_inheritance.py:94:23:94:28 | ControlFlowNode for Str | testapp/orm_inheritance.py:38:1:38:18 | [orm-model] Class EBook | Store step does not preserve enclosing callable. | +| testapp/orm_inheritance.py:95:35:95:40 | ControlFlowNode for Str | testapp/orm_inheritance.py:38:1:38:18 | [orm-model] Class EBook | Store step does not preserve enclosing callable. | +| testapp/orm_inheritance.py:133:15:133:20 | ControlFlowNode for SOURCE | testapp/orm_inheritance.py:117:1:117:33 | [orm-model] Class PolyBook | Store step does not preserve enclosing callable. | +| testapp/orm_inheritance.py:167:15:167:20 | ControlFlowNode for SOURCE | testapp/orm_inheritance.py:117:1:117:33 | [orm-model] Class PolyBook | Store step does not preserve enclosing callable. | +| testapp/orm_inheritance.py:167:15:167:20 | ControlFlowNode for SOURCE | testapp/orm_inheritance.py:121:1:121:33 | [orm-model] Class PolyPhysicalBook | Store step does not preserve enclosing callable. | +| testapp/orm_inheritance.py:168:27:168:32 | ControlFlowNode for SOURCE | testapp/orm_inheritance.py:121:1:121:33 | [orm-model] Class PolyPhysicalBook | Store step does not preserve enclosing callable. | +| testapp/orm_inheritance.py:169:35:169:40 | ControlFlowNode for SOURCE | testapp/orm_inheritance.py:121:1:121:33 | [orm-model] Class PolyPhysicalBook | Store step does not preserve enclosing callable. | +| testapp/orm_inheritance.py:183:15:183:26 | ControlFlowNode for Str | testapp/orm_inheritance.py:117:1:117:33 | [orm-model] Class PolyBook | Store step does not preserve enclosing callable. | +| testapp/orm_inheritance.py:183:15:183:26 | ControlFlowNode for Str | testapp/orm_inheritance.py:126:1:126:26 | [orm-model] Class PolyEBook | Store step does not preserve enclosing callable. | +| testapp/orm_inheritance.py:184:23:184:28 | ControlFlowNode for Str | testapp/orm_inheritance.py:126:1:126:26 | [orm-model] Class PolyEBook | Store step does not preserve enclosing callable. | +| testapp/orm_inheritance.py:185:35:185:40 | ControlFlowNode for Str | testapp/orm_inheritance.py:126:1:126:26 | [orm-model] Class PolyEBook | Store step does not preserve enclosing callable. | +| testapp/orm_security_tests.py:15:1:15:27 | [orm-model] Class Person | testapp/orm_security_tests.py:42:23:42:42 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:115:41:115:46 | ControlFlowNode for SOURCE | testapp/orm_tests.py:110:1:110:30 | [orm-model] Class TestSave5 | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:131:86:131:91 | ControlFlowNode for SOURCE | testapp/orm_tests.py:126:1:126:30 | [orm-model] Class TestSave6 | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:149:89:149:94 | ControlFlowNode for SOURCE | testapp/orm_tests.py:144:1:144:30 | [orm-model] Class TestSave7 | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:161:1:161:30 | [orm-model] Class TestSave8 | testapp/orm_tests.py:168:22:168:44 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:165:35:165:39 | ControlFlowNode for Str | testapp/orm_tests.py:161:1:161:30 | [orm-model] Class TestSave8 | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:168:58:168:63 | ControlFlowNode for SOURCE | testapp/orm_tests.py:161:1:161:30 | [orm-model] Class TestSave8 | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:184:41:184:45 | ControlFlowNode for Str | testapp/orm_tests.py:177:1:177:30 | [orm-model] Class TestSave9 | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:185:49:185:51 | ControlFlowNode for obj | testapp/orm_tests.py:180:1:180:44 | [orm-model] Class TestSave9WithForeignKey | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:212:55:212:59 | ControlFlowNode for Str | testapp/orm_tests.py:206:1:206:35 | [orm-model] Class save10_Comment | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:239:55:239:59 | ControlFlowNode for Str | testapp/orm_tests.py:233:1:233:35 | [orm-model] Class save11_Comment | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:273:1:273:31 | [orm-model] Class TestSave13 | testapp/orm_tests.py:281:12:281:35 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:294:1:294:29 | [orm-model] Class TestLoad | testapp/orm_tests.py:308:12:308:33 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:294:1:294:29 | [orm-model] Class TestLoad | testapp/orm_tests.py:314:12:314:33 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:294:1:294:29 | [orm-model] Class TestLoad | testapp/orm_tests.py:320:11:320:32 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:294:1:294:29 | [orm-model] Class TestLoad | testapp/orm_tests.py:320:11:320:59 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:294:1:294:29 | [orm-model] Class TestLoad | testapp/orm_tests.py:320:11:320:78 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:294:1:294:29 | [orm-model] Class TestLoad | testapp/orm_tests.py:324:12:324:33 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:294:1:294:29 | [orm-model] Class TestLoad | testapp/orm_tests.py:324:12:324:60 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:294:1:294:29 | [orm-model] Class TestLoad | testapp/orm_tests.py:324:12:324:79 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:294:1:294:29 | [orm-model] Class TestLoad | testapp/orm_tests.py:331:12:331:33 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:294:1:294:29 | [orm-model] Class TestLoad | testapp/orm_tests.py:337:12:337:33 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:294:1:294:29 | [orm-model] Class TestLoad | testapp/orm_tests.py:344:12:344:33 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:294:1:294:29 | [orm-model] Class TestLoad | testapp/orm_tests.py:350:12:350:33 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:294:1:294:29 | [orm-model] Class TestLoad | testapp/orm_tests.py:356:12:356:33 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/orm_tests.py:294:1:294:29 | [orm-model] Class TestLoad | testapp/orm_tests.py:363:9:363:37 | ControlFlowNode for Attribute() | Store step does not preserve enclosing callable. | +| testapp/tests.py:81:33:81:37 | ControlFlowNode for Str | testapp/orm_form_test.py:6:1:6:28 | [orm-model] Class MyModel | Store step does not preserve enclosing callable. | compatibleTypesReflexive unreachableNodeCCtx localCallNodes From e25e192ef33b74ae388b1bbc593c1c5963c15ecf Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Thu, 10 Nov 2022 18:42:04 +1300 Subject: [PATCH 0173/1420] Ruby: Change the CFG for while clauses The `when` node now acts as a join point for patterns in the when clause, with match/no-match completions. This is similar to how `or` expressions work. The result of this is that the `when` clause "controls" the body of the `when`, which allows us to model barrier guards for multi-pattern when clauses. For this code case x when 1, 2 y end The old CFG was x --> when --> 1 --no-match--> 2 ---no-match---> case \ \ ^ \ \ | \ --match----+ | \ | | \ | | ------match---------> y --+ The new CFG is x --> 1 --no-match--> 2 --no-match--> [no-match] when --no-match--> case \ \ ^ \ \ | \ --match--> [match] when --match--> y -----+ \ / \ / -------match----- i.e. all patterns flow to the `when` node, which is split based on whether the pattern matched or not. The body of the when clause then has a single predecessor `[match] when`, which acts as condition block that controls `y`. --- .../lib/codeql/ruby/controlflow/CfgNodes.qll | 2 +- .../ruby/controlflow/internal/Completion.qll | 8 +- .../internal/ControlFlowGraphImpl.qll | 21 +++-- .../ruby/controlflow/internal/Splitting.qll | 7 ++ .../codeql/ruby/dataflow/BarrierGuards.qll | 2 +- .../controlflow/graph/Cfg.expected | 76 ++++++++++++------- .../dataflow/barrier-guards/barrier-guards.rb | 4 +- 7 files changed, 79 insertions(+), 41 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll index 4417891a66a..31898e0a652 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll @@ -446,7 +446,7 @@ module ExprNodes { final ExprCfgNode getBody() { e.hasCfgChild(e.getBody(), this, result) } /** Gets the `i`th pattern this `when`-clause. */ - final ExprCfgNode getPattern(int i) { e.hasCfgChild(e.getPattern(i), this, result) } + final ExprCfgNode getPattern(int i) { result.getExpr() = e.getPattern(i) } } /** A control-flow node that wraps a `CasePattern`. */ diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/Completion.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/Completion.qll index fe4cca24d69..d727b99d6cf 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/internal/Completion.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/internal/Completion.qll @@ -233,8 +233,12 @@ private predicate inMatchingContext(AstNode n) { or exists(CaseExpr c, WhenClause w | exists(c.getValue()) and - c.getABranch() = w and - w.getPattern(_) = n + ( + c.getABranch() = w and + w.getPattern(_) = n + or + w = n + ) ) or n instanceof CasePattern diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll index bdd67d2a693..1ca24d43ab8 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -400,7 +400,7 @@ module Trees { c instanceof SimpleCompletion or exists(int i, WhenTree branch | branch = this.getBranch(i) | - last(branch.getLastPattern(), pred, c) and + pred = branch and first(this.getBranch(i + 1), succ) and c.(ConditionalCompletion).getValue() = false ) @@ -1397,7 +1397,7 @@ module Trees { final override ControlFlowTree getChildElement(int i) { result = this.getMethodName(i) } } - private class WhenTree extends PreOrderTree, WhenClause { + private class WhenTree extends ControlFlowTree, WhenClause { final override predicate propagatesAbnormal(AstNode child) { child = this.getAPattern() } final Expr getLastPattern() { @@ -1407,17 +1407,21 @@ module Trees { ) } + final override predicate first(AstNode first) { first(this.getPattern(0), first) } + final override predicate last(AstNode last, Completion c) { - last(this.getLastPattern(), last, c) and + last = this and c.(ConditionalCompletion).getValue() = false or - last(this.getBody(), last, c) + last(this.getBody(), last, c) and + c instanceof NormalCompletion } final override predicate succ(AstNode pred, AstNode succ, Completion c) { pred = this and - first(this.getPattern(0), succ) and - c instanceof SimpleCompletion + c.isValidFor(this) and + c.(ConditionalCompletion).getValue() = true and + first(this.getBody(), succ) or exists(int i, Expr p, boolean b | p = this.getPattern(i) and @@ -1425,10 +1429,13 @@ module Trees { b = c.(ConditionalCompletion).getValue() | b = true and - first(this.getBody(), succ) + succ = this or b = false and first(this.getPattern(i + 1), succ) + or + not exists(this.getPattern(i + 1)) and + succ = this ) } } diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/Splitting.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/Splitting.qll index 9581b45a95e..5e24616ac4a 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/internal/Splitting.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/internal/Splitting.qll @@ -85,7 +85,14 @@ private module ConditionalCompletionSplitting { or last(succ.(ConditionalExpr).getBranch(_), pred, c) and completion = c + or + last(succ.(WhenClause).getAPattern(), pred, c) and completion = c ) + or + succ(pred, succ, c) and + succ(succ, _, completion) and + succ instanceof WhenClause and + completion = c } override predicate hasEntryScope(CfgScope scope, AstNode succ) { none() } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll index b2827e53255..e80495ab2f3 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll @@ -186,8 +186,8 @@ private predicate stringConstCaseCompare( case.getValue() = testedNode and ( exists(CfgNodes::ExprNodes::WhenClauseCfgNode branchNode | + guard = branchNode and branchNode = case.getBranch(_) and - guard = branchNode.getPattern(_) and // For simplicity, consider patterns that contain only string literals or arrays of string literals forall(ExprCfgNode pattern | pattern = branchNode.getPattern(_) | // when "foo" diff --git a/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected b/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected index 9c26785029e..69e4cc294de 100644 --- a/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -526,17 +526,20 @@ case.rb: #-----| -> exit if_in_case (normal) # 2| call to x1 -#-----| -> when ... +#-----| -> 1 # 2| self #-----| -> call to x1 -# 3| when ... -#-----| -> 1 +# 3| [match] when ... +#-----| match -> self + +# 3| [no-match] when ... +#-----| no-match -> 2 # 3| 1 -#-----| match -> self -#-----| no-match -> when ... +#-----| match -> [match] when ... +#-----| no-match -> [no-match] when ... # 3| then ... #-----| -> case ... @@ -569,12 +572,15 @@ case.rb: # 3| x2 #-----| -> "x2" -# 4| when ... -#-----| -> 2 +# 4| [match] when ... +#-----| match -> self + +# 4| [no-match] when ... +#-----| no-match -> case ... # 4| 2 -#-----| no-match -> case ... -#-----| match -> self +#-----| match -> [match] when ... +#-----| no-match -> [no-match] when ... # 4| then ... #-----| -> case ... @@ -1826,17 +1832,20 @@ cfg.rb: #-----| -> call to puts # 41| case ... -#-----| -> when ... +#-----| -> b # 41| 10 -#-----| -> when ... - -# 42| when ... #-----| -> 1 -# 42| 1 +# 42| [match] when ... #-----| match -> self -#-----| no-match -> when ... + +# 42| [no-match] when ... +#-----| no-match -> 2 + +# 42| 1 +#-----| match -> [match] when ... +#-----| no-match -> [no-match] when ... # 42| then ... #-----| -> case ... @@ -1853,20 +1862,23 @@ cfg.rb: # 42| one #-----| -> "one" -# 43| when ... -#-----| -> 2 +# 43| [match] when ... +#-----| match -> self + +# 43| [no-match] when ... +#-----| no-match -> self # 43| 2 +#-----| match -> [match] when ... #-----| no-match -> 3 -#-----| match -> self # 43| 3 +#-----| match -> [match] when ... #-----| no-match -> 4 -#-----| match -> self # 43| 4 -#-----| match -> self -#-----| no-match -> self +#-----| match -> [match] when ... +#-----| no-match -> [no-match] when ... # 43| then ... #-----| -> case ... @@ -1901,15 +1913,19 @@ cfg.rb: # 47| case ... #-----| -> chained +# 48| [false] when ... +#-----| false -> b + # 48| when ... -#-----| -> b +#-----| match -> self +#-----| no-match -> b # 48| b #-----| -> 1 # 48| ... == ... -#-----| true -> self -#-----| false -> when ... +#-----| false -> [false] when ... +#-----| true -> when ... # 48| 1 #-----| -> ... == ... @@ -1929,15 +1945,19 @@ cfg.rb: # 48| one #-----| -> "one" +# 49| [false] when ... +#-----| false -> case ... + # 49| when ... -#-----| -> b +#-----| no-match -> case ... +#-----| match -> self # 49| b #-----| -> 0 # 49| ... == ... +#-----| true -> when ... #-----| false -> b -#-----| true -> self # 49| 0 #-----| -> ... == ... @@ -1946,8 +1966,8 @@ cfg.rb: #-----| -> 1 # 49| ... > ... -#-----| false -> case ... -#-----| true -> self +#-----| false -> [false] when ... +#-----| true -> when ... # 49| 1 #-----| -> ... > ... diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb index 26c5da44798..6323a5224b6 100644 --- a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb @@ -137,9 +137,9 @@ end case foo when "foo", "bar" - foo # $ MISSING: guarded + foo # $ guarded when "baz", "quux" - foo # $ MISSING: guarded + foo # $ guarded else foo end From dcebe930e778e2b420a50d7876e32e77aaa8a92b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Nov 2022 00:21:31 +0000 Subject: [PATCH 0174/1420] Add changed framework coverage reports --- csharp/documentation/library-coverage/coverage.csv | 2 +- csharp/documentation/library-coverage/coverage.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/csharp/documentation/library-coverage/coverage.csv b/csharp/documentation/library-coverage/coverage.csv index 6ab77f6aba6..5d0813f71e9 100644 --- a/csharp/documentation/library-coverage/coverage.csv +++ b/csharp/documentation/library-coverage/coverage.csv @@ -24,5 +24,5 @@ Microsoft.Win32,,,8,,,,,,,,,,,,8, MySql.Data.MySqlClient,48,,,,,,,,,,48,,,,, Newtonsoft.Json,,,91,,,,,,,,,,,,73,18 ServiceStack,194,,7,27,,,,,,75,92,,,,7, -System,65,4,12131,,8,8,9,,4,,33,3,1,3,10139,1992 +System,65,4,12142,,8,8,9,,4,,33,3,1,3,10151,1991 Windows.Security.Cryptography.Core,1,,,,,,,1,,,,,,,, diff --git a/csharp/documentation/library-coverage/coverage.rst b/csharp/documentation/library-coverage/coverage.rst index 0f3dce2941a..fa178752fe3 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``",4,12131,65,7 + System,"``System.*``, ``System``",4,12142,65,7 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``, ``Windows.Security.Cryptography.Core``",,556,138, - Totals,,4,12694,397,7 + Totals,,4,12705,397,7 From 62ea1f0a05fb6166a679a578518d71a8e781390a Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Fri, 11 Nov 2022 18:24:20 +1300 Subject: [PATCH 0175/1420] Ruby: Fix performance of string comparison guard The `or` case ran extremely slowly before this change. Also exclude string interpolations from consideration, for correctness, and add some more tests. --- .../codeql/ruby/dataflow/BarrierGuards.qll | 39 +++++++++++++++---- .../dataflow/barrier-guards/barrier-guards.rb | 17 ++++++++ 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll index e80495ab2f3..01b42a520bf 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll @@ -7,12 +7,16 @@ private import codeql.ruby.controlflow.CfgNodes private import codeql.ruby.dataflow.SSA private import codeql.ruby.ast.internal.Constant private import codeql.ruby.InclusionTests +private import codeql.ruby.ast.internal.Literal private predicate stringConstCompare(CfgNodes::AstCfgNode guard, CfgNode testedNode, boolean branch) { exists(CfgNodes::ExprNodes::ComparisonOperationCfgNode c | c = guard and exists(CfgNodes::ExprNodes::StringLiteralCfgNode strLitNode | - c.getExpr() instanceof EqExpr and branch = true + // Only consider strings without any interpolations + not exists(StringInterpolationComponent comp | comp = strLitNode.getExpr().getComponent(_)) and + c.getExpr() instanceof EqExpr and + branch = true or c.getExpr() instanceof CaseEqExpr and branch = true or @@ -26,24 +30,43 @@ private predicate stringConstCompare(CfgNodes::AstCfgNode guard, CfgNode testedN or stringConstCaseCompare(guard, testedNode, branch) or - stringConstCompareOr(guard, testedNode, branch) + exists(Ssa::Definition def, CfgNodes::ExprNodes::BinaryOperationCfgNode g | + g = guard and + stringConstCompareOr(guard, def, branch) and + stringConstCompare(g.getLeftOperand(), testedNode, _) + ) or stringConstCompareAnd(guard, testedNode, branch) } +/** + * Holds if `guard` is an `or` expression whose operands are string comparison guards that test the same SSA variable. + * `testedNode` is an arbitrary node that is tested by one of the guards. + * For example: + * + * ```rb + * x == "foo" or x == "bar" + * ``` + */ private predicate stringConstCompareOr( - CfgNodes::ExprNodes::BinaryOperationCfgNode guard, CfgNode testedNode, boolean branch + CfgNodes::ExprNodes::BinaryOperationCfgNode guard, Ssa::Definition def, boolean branch ) { guard.getExpr() instanceof LogicalOrExpr and branch = true and - exists(Ssa::Definition def | - forall(CfgNode innerGuard | innerGuard = guard.getAnOperand() | - stringConstCompare(innerGuard, def.getARead(), branch) - ) and - testedNode = any(CfgNode node | stringConstCompare(guard.getAnOperand(), node, _)) + forall(CfgNode innerGuard | innerGuard = guard.getAnOperand() | + stringConstCompare(innerGuard, def.getARead(), branch) ) } +/** + * Holds if guard is an `and` expression containing a string comparison guard in either operand. + * For example: + * + * ```rb + * x == "foo" and other_condition() + * other_condition() and x == "foo" + * ``` + */ private predicate stringConstCompareAnd( CfgNodes::ExprNodes::BinaryOperationCfgNode guard, CfgNode testedNode, boolean branch ) { diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb index 6323a5224b6..1946be36275 100644 --- a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb @@ -250,3 +250,20 @@ case bar in "foo" foo end + +if foo == "#{some_method()}" + foo +end + +F = "foo" +if foo == "#{F}" + foo # $ MISSING: guarded +end + +f = "foo" +if foo == "#{f}" + foo # $ MISSING: guarded +end + +foo == "foo" && foo # $ guarded +foo && foo == "foo" \ No newline at end of file From b16cecc8db1c678b4b5d4300ebcec1030fc902bc Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Fri, 11 Nov 2022 18:29:07 +1300 Subject: [PATCH 0176/1420] Ruby: Add missing doc --- ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll | 1 + ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll index 31898e0a652..b411c766f03 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll @@ -878,6 +878,7 @@ module ExprNodes { final override SplatExpr getExpr() { result = super.getExpr() } + /** Gets the operand of this splat expression. */ final ExprCfgNode getOperand() { e.hasCfgChild(e.getOperand(), this, result) } } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll index 01b42a520bf..16e965f6657 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll @@ -14,7 +14,7 @@ private predicate stringConstCompare(CfgNodes::AstCfgNode guard, CfgNode testedN c = guard and exists(CfgNodes::ExprNodes::StringLiteralCfgNode strLitNode | // Only consider strings without any interpolations - not exists(StringInterpolationComponent comp | comp = strLitNode.getExpr().getComponent(_)) and + not strLitNode.getExpr().getComponent(_) instanceof StringInterpolationComponent and c.getExpr() instanceof EqExpr and branch = true or @@ -40,8 +40,7 @@ private predicate stringConstCompare(CfgNodes::AstCfgNode guard, CfgNode testedN } /** - * Holds if `guard` is an `or` expression whose operands are string comparison guards that test the same SSA variable. - * `testedNode` is an arbitrary node that is tested by one of the guards. + * Holds if `guard` is an `or` expression whose operands are string comparison guards that test `def`. * For example: * * ```rb @@ -59,7 +58,7 @@ private predicate stringConstCompareOr( } /** - * Holds if guard is an `and` expression containing a string comparison guard in either operand. + * Holds if `guard` is an `and` expression containing a string comparison guard in either operand. * For example: * * ```rb From 2b4217b8a41536b5b550ba56e4f911743bc976fd Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Fri, 11 Nov 2022 18:41:51 +1300 Subject: [PATCH 0177/1420] Ruby: Update test fixture --- .../barrier-guards/barrier-guards.expected | 217 ++++++++++++++++-- 1 file changed, 195 insertions(+), 22 deletions(-) diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected index 05c515edebf..7804824ffb6 100644 --- a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected @@ -10,8 +10,15 @@ oldStyleBarrierGuards | barrier-guards.rb:43:4:43:15 | ... == ... | barrier-guards.rb:45:9:45:11 | foo | barrier-guards.rb:43:4:43:6 | foo | true | | barrier-guards.rb:70:4:70:21 | call to include? | barrier-guards.rb:71:5:71:7 | foo | barrier-guards.rb:70:18:70:20 | foo | true | | barrier-guards.rb:82:4:82:25 | ... != ... | barrier-guards.rb:83:5:83:7 | foo | barrier-guards.rb:82:15:82:17 | foo | true | -| barrier-guards.rb:196:6:196:17 | ... == ... | barrier-guards.rb:197:5:197:7 | foo | barrier-guards.rb:196:6:196:8 | foo | true | -| barrier-guards.rb:201:6:201:17 | ... == ... | barrier-guards.rb:201:24:201:26 | foo | barrier-guards.rb:201:6:201:8 | foo | true | +| barrier-guards.rb:207:4:207:15 | ... == ... | barrier-guards.rb:208:5:208:7 | foo | barrier-guards.rb:207:4:207:6 | foo | true | +| barrier-guards.rb:211:10:211:21 | ... == ... | barrier-guards.rb:212:5:212:7 | foo | barrier-guards.rb:211:10:211:12 | foo | true | +| barrier-guards.rb:215:16:215:27 | ... == ... | barrier-guards.rb:216:5:216:7 | foo | barrier-guards.rb:215:16:215:18 | foo | true | +| barrier-guards.rb:219:4:219:15 | ... == ... | barrier-guards.rb:219:21:219:23 | foo | barrier-guards.rb:219:4:219:6 | foo | true | +| barrier-guards.rb:219:4:219:15 | ... == ... | barrier-guards.rb:220:5:220:7 | foo | barrier-guards.rb:219:4:219:6 | foo | true | +| barrier-guards.rb:219:21:219:32 | ... == ... | barrier-guards.rb:220:5:220:7 | foo | barrier-guards.rb:219:21:219:23 | foo | true | +| barrier-guards.rb:232:6:232:17 | ... == ... | barrier-guards.rb:233:5:233:7 | foo | barrier-guards.rb:232:6:232:8 | foo | true | +| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:237:24:237:26 | foo | barrier-guards.rb:237:6:237:8 | foo | true | +| barrier-guards.rb:268:1:268:12 | ... == ... | barrier-guards.rb:268:17:268:19 | foo | barrier-guards.rb:268:1:268:3 | foo | true | newStyleBarrierGuards | barrier-guards.rb:4:5:4:7 | foo | | barrier-guards.rb:10:5:10:7 | foo | @@ -26,13 +33,23 @@ newStyleBarrierGuards | barrier-guards.rb:126:5:126:7 | foo | | barrier-guards.rb:133:5:133:7 | foo | | barrier-guards.rb:135:5:135:7 | foo | +| barrier-guards.rb:140:5:140:7 | foo | +| barrier-guards.rb:142:5:142:7 | foo | | barrier-guards.rb:149:5:149:7 | foo | | barrier-guards.rb:154:5:154:7 | foo | | barrier-guards.rb:159:5:159:7 | foo | | barrier-guards.rb:164:5:164:7 | foo | -| barrier-guards.rb:197:5:197:7 | foo | -| barrier-guards.rb:201:24:201:26 | foo | +| barrier-guards.rb:192:5:192:7 | foo | +| barrier-guards.rb:196:5:196:7 | foo | | barrier-guards.rb:208:5:208:7 | foo | +| barrier-guards.rb:212:5:212:7 | foo | +| barrier-guards.rb:216:5:216:7 | foo | +| barrier-guards.rb:219:21:219:23 | foo | +| barrier-guards.rb:220:5:220:7 | foo | +| barrier-guards.rb:233:5:233:7 | foo | +| barrier-guards.rb:237:24:237:26 | foo | +| barrier-guards.rb:244:5:244:7 | foo | +| barrier-guards.rb:268:17:268:19 | foo | controls | barrier-guards.rb:3:4:3:15 | ... == ... | barrier-guards.rb:4:5:4:7 | foo | true | | barrier-guards.rb:3:4:3:15 | ... == ... | barrier-guards.rb:6:5:6:7 | foo | false | @@ -80,48 +97,204 @@ controls | barrier-guards.rb:118:8:118:8 | call to x | barrier-guards.rb:118:4:118:8 | [true] not ... | false | | barrier-guards.rb:118:8:118:8 | call to x | barrier-guards.rb:119:5:119:7 | foo | false | | barrier-guards.rb:118:8:118:8 | call to x | barrier-guards.rb:121:5:121:8 | bars | true | +| barrier-guards.rb:125:1:126:19 | [match] when ... | barrier-guards.rb:126:5:126:7 | foo | match | +| barrier-guards.rb:125:1:126:19 | [no-match] when ... | barrier-guards.rb:128:5:128:7 | foo | no-match | +| barrier-guards.rb:125:6:125:10 | "foo" | barrier-guards.rb:125:1:126:19 | [match] when ... | match | +| barrier-guards.rb:125:6:125:10 | "foo" | barrier-guards.rb:125:1:126:19 | [no-match] when ... | no-match | | barrier-guards.rb:125:6:125:10 | "foo" | barrier-guards.rb:126:5:126:7 | foo | match | | barrier-guards.rb:125:6:125:10 | "foo" | barrier-guards.rb:128:5:128:7 | foo | no-match | +| barrier-guards.rb:132:1:133:19 | [match] when ... | barrier-guards.rb:133:5:133:7 | foo | match | +| barrier-guards.rb:132:1:133:19 | [no-match] when ... | barrier-guards.rb:134:1:135:19 | [match] when ... | no-match | +| barrier-guards.rb:132:1:133:19 | [no-match] when ... | barrier-guards.rb:134:1:135:19 | [no-match] when ... | no-match | +| barrier-guards.rb:132:1:133:19 | [no-match] when ... | barrier-guards.rb:134:7:134:9 | bar | no-match | +| barrier-guards.rb:132:1:133:19 | [no-match] when ... | barrier-guards.rb:135:5:135:7 | foo | no-match | +| barrier-guards.rb:132:6:132:10 | "foo" | barrier-guards.rb:132:1:133:19 | [match] when ... | match | +| barrier-guards.rb:132:6:132:10 | "foo" | barrier-guards.rb:132:1:133:19 | [no-match] when ... | no-match | | barrier-guards.rb:132:6:132:10 | "foo" | barrier-guards.rb:133:5:133:7 | foo | match | -| barrier-guards.rb:132:6:132:10 | "foo" | barrier-guards.rb:134:1:135:19 | when ... | no-match | +| barrier-guards.rb:132:6:132:10 | "foo" | barrier-guards.rb:134:1:135:19 | [match] when ... | no-match | +| barrier-guards.rb:132:6:132:10 | "foo" | barrier-guards.rb:134:1:135:19 | [no-match] when ... | no-match | +| barrier-guards.rb:132:6:132:10 | "foo" | barrier-guards.rb:134:7:134:9 | bar | no-match | | barrier-guards.rb:132:6:132:10 | "foo" | barrier-guards.rb:135:5:135:7 | foo | no-match | +| barrier-guards.rb:134:1:135:19 | [match] when ... | barrier-guards.rb:135:5:135:7 | foo | match | +| barrier-guards.rb:134:6:134:10 | "bar" | barrier-guards.rb:134:1:135:19 | [match] when ... | match | +| barrier-guards.rb:134:6:134:10 | "bar" | barrier-guards.rb:134:1:135:19 | [no-match] when ... | no-match | | barrier-guards.rb:134:6:134:10 | "bar" | barrier-guards.rb:135:5:135:7 | foo | match | +| barrier-guards.rb:139:1:140:19 | [match] when ... | barrier-guards.rb:140:5:140:7 | foo | match | +| barrier-guards.rb:139:1:140:19 | [no-match] when ... | barrier-guards.rb:141:1:142:19 | [match] when ... | no-match | +| barrier-guards.rb:139:1:140:19 | [no-match] when ... | barrier-guards.rb:141:1:142:19 | [no-match] when ... | no-match | +| barrier-guards.rb:139:1:140:19 | [no-match] when ... | barrier-guards.rb:141:7:141:9 | baz | no-match | +| barrier-guards.rb:139:1:140:19 | [no-match] when ... | barrier-guards.rb:141:14:141:17 | quux | no-match | +| barrier-guards.rb:139:1:140:19 | [no-match] when ... | barrier-guards.rb:142:5:142:7 | foo | no-match | +| barrier-guards.rb:139:1:140:19 | [no-match] when ... | barrier-guards.rb:144:5:144:7 | foo | no-match | +| barrier-guards.rb:139:6:139:10 | "foo" | barrier-guards.rb:139:1:140:19 | [no-match] when ... | no-match | | barrier-guards.rb:139:6:139:10 | "foo" | barrier-guards.rb:139:14:139:16 | bar | no-match | -| barrier-guards.rb:139:6:139:10 | "foo" | barrier-guards.rb:141:1:142:7 | when ... | no-match | +| barrier-guards.rb:139:6:139:10 | "foo" | barrier-guards.rb:141:1:142:19 | [match] when ... | no-match | +| barrier-guards.rb:139:6:139:10 | "foo" | barrier-guards.rb:141:1:142:19 | [no-match] when ... | no-match | +| barrier-guards.rb:139:6:139:10 | "foo" | barrier-guards.rb:141:7:141:9 | baz | no-match | | barrier-guards.rb:139:6:139:10 | "foo" | barrier-guards.rb:141:14:141:17 | quux | no-match | | barrier-guards.rb:139:6:139:10 | "foo" | barrier-guards.rb:142:5:142:7 | foo | no-match | | barrier-guards.rb:139:6:139:10 | "foo" | barrier-guards.rb:144:5:144:7 | foo | no-match | -| barrier-guards.rb:139:13:139:17 | "bar" | barrier-guards.rb:141:1:142:7 | when ... | no-match | +| barrier-guards.rb:139:13:139:17 | "bar" | barrier-guards.rb:139:1:140:19 | [no-match] when ... | no-match | +| barrier-guards.rb:139:13:139:17 | "bar" | barrier-guards.rb:141:1:142:19 | [match] when ... | no-match | +| barrier-guards.rb:139:13:139:17 | "bar" | barrier-guards.rb:141:1:142:19 | [no-match] when ... | no-match | +| barrier-guards.rb:139:13:139:17 | "bar" | barrier-guards.rb:141:7:141:9 | baz | no-match | | barrier-guards.rb:139:13:139:17 | "bar" | barrier-guards.rb:141:14:141:17 | quux | no-match | | barrier-guards.rb:139:13:139:17 | "bar" | barrier-guards.rb:142:5:142:7 | foo | no-match | | barrier-guards.rb:139:13:139:17 | "bar" | barrier-guards.rb:144:5:144:7 | foo | no-match | +| barrier-guards.rb:141:1:142:19 | [match] when ... | barrier-guards.rb:142:5:142:7 | foo | match | +| barrier-guards.rb:141:1:142:19 | [no-match] when ... | barrier-guards.rb:144:5:144:7 | foo | no-match | +| barrier-guards.rb:141:6:141:10 | "baz" | barrier-guards.rb:141:1:142:19 | [no-match] when ... | no-match | | barrier-guards.rb:141:6:141:10 | "baz" | barrier-guards.rb:141:14:141:17 | quux | no-match | | barrier-guards.rb:141:6:141:10 | "baz" | barrier-guards.rb:144:5:144:7 | foo | no-match | +| barrier-guards.rb:141:13:141:18 | "quux" | barrier-guards.rb:141:1:142:19 | [no-match] when ... | no-match | | barrier-guards.rb:141:13:141:18 | "quux" | barrier-guards.rb:144:5:144:7 | foo | no-match | +| barrier-guards.rb:148:1:149:19 | [match] when ... | barrier-guards.rb:149:5:149:7 | foo | match | +| barrier-guards.rb:148:6:148:20 | * ... | barrier-guards.rb:148:1:149:19 | [match] when ... | match | +| barrier-guards.rb:148:6:148:20 | * ... | barrier-guards.rb:148:1:149:19 | [no-match] when ... | no-match | | barrier-guards.rb:148:6:148:20 | * ... | barrier-guards.rb:149:5:149:7 | foo | match | +| barrier-guards.rb:153:1:154:19 | [match] when ... | barrier-guards.rb:154:5:154:7 | foo | match | +| barrier-guards.rb:153:6:153:17 | * ... | barrier-guards.rb:153:1:154:19 | [match] when ... | match | +| barrier-guards.rb:153:6:153:17 | * ... | barrier-guards.rb:153:1:154:19 | [no-match] when ... | no-match | | barrier-guards.rb:153:6:153:17 | * ... | barrier-guards.rb:154:5:154:7 | foo | match | +| barrier-guards.rb:158:1:159:19 | [match] when ... | barrier-guards.rb:159:5:159:7 | foo | match | +| barrier-guards.rb:158:6:158:9 | * ... | barrier-guards.rb:158:1:159:19 | [match] when ... | match | +| barrier-guards.rb:158:6:158:9 | * ... | barrier-guards.rb:158:1:159:19 | [no-match] when ... | no-match | | barrier-guards.rb:158:6:158:9 | * ... | barrier-guards.rb:159:5:159:7 | foo | match | +| barrier-guards.rb:163:1:164:19 | [match] when ... | barrier-guards.rb:164:5:164:7 | foo | match | +| barrier-guards.rb:163:6:163:10 | * ... | barrier-guards.rb:163:1:164:19 | [match] when ... | match | +| barrier-guards.rb:163:6:163:10 | * ... | barrier-guards.rb:163:1:164:19 | [no-match] when ... | no-match | | barrier-guards.rb:163:6:163:10 | * ... | barrier-guards.rb:164:5:164:7 | foo | match | +| barrier-guards.rb:168:1:169:7 | [match] when ... | barrier-guards.rb:169:5:169:7 | foo | match | +| barrier-guards.rb:168:6:168:16 | * ... | barrier-guards.rb:168:1:169:7 | [match] when ... | match | +| barrier-guards.rb:168:6:168:16 | * ... | barrier-guards.rb:168:1:169:7 | [no-match] when ... | no-match | | barrier-guards.rb:168:6:168:16 | * ... | barrier-guards.rb:169:5:169:7 | foo | match | +| barrier-guards.rb:173:1:174:7 | [match] when ... | barrier-guards.rb:174:5:174:7 | foo | match | +| barrier-guards.rb:173:6:173:10 | "foo" | barrier-guards.rb:173:1:174:7 | [no-match] when ... | no-match | | barrier-guards.rb:173:6:173:10 | "foo" | barrier-guards.rb:173:13:173:13 | self | no-match | +| barrier-guards.rb:173:13:173:13 | call to x | barrier-guards.rb:173:1:174:7 | [no-match] when ... | no-match | +| barrier-guards.rb:180:1:181:7 | [match] when ... | barrier-guards.rb:181:5:181:7 | foo | match | +| barrier-guards.rb:180:6:180:15 | * ... | barrier-guards.rb:180:1:181:7 | [match] when ... | match | +| barrier-guards.rb:180:6:180:15 | * ... | barrier-guards.rb:180:1:181:7 | [no-match] when ... | no-match | | barrier-guards.rb:180:6:180:15 | * ... | barrier-guards.rb:181:5:181:7 | foo | match | +| barrier-guards.rb:187:1:188:7 | [match] when ... | barrier-guards.rb:188:5:188:7 | foo | match | +| barrier-guards.rb:187:6:187:15 | * ... | barrier-guards.rb:187:1:188:7 | [match] when ... | match | +| barrier-guards.rb:187:6:187:15 | * ... | barrier-guards.rb:187:1:188:7 | [no-match] when ... | no-match | | barrier-guards.rb:187:6:187:15 | * ... | barrier-guards.rb:188:5:188:7 | foo | match | | barrier-guards.rb:191:4:191:15 | ... == ... | barrier-guards.rb:191:4:191:31 | [false] ... or ... | false | | barrier-guards.rb:191:4:191:15 | ... == ... | barrier-guards.rb:191:20:191:22 | foo | false | | barrier-guards.rb:191:4:191:31 | [true] ... or ... | barrier-guards.rb:192:5:192:7 | foo | true | | barrier-guards.rb:191:20:191:31 | ... == ... | barrier-guards.rb:191:4:191:31 | [false] ... or ... | false | -| barrier-guards.rb:196:6:196:17 | ... == ... | barrier-guards.rb:197:5:197:7 | foo | true | -| barrier-guards.rb:201:6:201:17 | ... == ... | barrier-guards.rb:201:24:201:26 | foo | true | -| barrier-guards.rb:201:6:201:17 | ... == ... | barrier-guards.rb:202:1:202:26 | when ... | false | -| barrier-guards.rb:201:6:201:17 | ... == ... | barrier-guards.rb:202:24:202:26 | foo | false | -| barrier-guards.rb:201:6:201:17 | ... == ... | barrier-guards.rb:203:1:203:22 | when ... | false | -| barrier-guards.rb:201:6:201:17 | ... == ... | barrier-guards.rb:203:20:203:22 | foo | false | -| barrier-guards.rb:202:6:202:17 | ... == ... | barrier-guards.rb:202:24:202:26 | foo | true | -| barrier-guards.rb:202:6:202:17 | ... == ... | barrier-guards.rb:203:1:203:22 | when ... | false | -| barrier-guards.rb:202:6:202:17 | ... == ... | barrier-guards.rb:203:20:203:22 | foo | false | -| barrier-guards.rb:203:6:203:13 | ... == ... | barrier-guards.rb:203:20:203:22 | foo | true | -| barrier-guards.rb:207:4:207:8 | "foo" | barrier-guards.rb:208:5:208:7 | foo | match | -| barrier-guards.rb:207:4:207:8 | "foo" | barrier-guards.rb:209:1:210:7 | in ... then ... | no-match | -| barrier-guards.rb:207:4:207:8 | "foo" | barrier-guards.rb:210:5:210:7 | foo | no-match | -| barrier-guards.rb:209:4:209:4 | x | barrier-guards.rb:210:5:210:7 | foo | match | -| barrier-guards.rb:214:4:214:8 | "foo" | barrier-guards.rb:215:5:215:7 | foo | match | +| barrier-guards.rb:195:4:195:15 | ... == ... | barrier-guards.rb:195:4:195:31 | [false] ... or ... | false | +| barrier-guards.rb:195:4:195:15 | ... == ... | barrier-guards.rb:195:4:195:47 | [false] ... or ... | false | +| barrier-guards.rb:195:4:195:15 | ... == ... | barrier-guards.rb:195:20:195:22 | foo | false | +| barrier-guards.rb:195:4:195:15 | ... == ... | barrier-guards.rb:195:36:195:38 | foo | false | +| barrier-guards.rb:195:4:195:31 | [false] ... or ... | barrier-guards.rb:195:4:195:47 | [false] ... or ... | false | +| barrier-guards.rb:195:4:195:31 | [false] ... or ... | barrier-guards.rb:195:36:195:38 | foo | false | +| barrier-guards.rb:195:4:195:47 | [true] ... or ... | barrier-guards.rb:196:5:196:7 | foo | true | +| barrier-guards.rb:195:20:195:31 | ... == ... | barrier-guards.rb:195:4:195:31 | [false] ... or ... | false | +| barrier-guards.rb:195:20:195:31 | ... == ... | barrier-guards.rb:195:4:195:47 | [false] ... or ... | false | +| barrier-guards.rb:195:20:195:31 | ... == ... | barrier-guards.rb:195:36:195:38 | foo | false | +| barrier-guards.rb:195:36:195:47 | ... == ... | barrier-guards.rb:195:4:195:47 | [false] ... or ... | false | +| barrier-guards.rb:199:4:199:15 | ... == ... | barrier-guards.rb:199:4:199:31 | [false] ... or ... | false | +| barrier-guards.rb:199:4:199:15 | ... == ... | barrier-guards.rb:199:4:199:43 | [false] ... or ... | false | +| barrier-guards.rb:199:4:199:15 | ... == ... | barrier-guards.rb:199:20:199:22 | foo | false | +| barrier-guards.rb:199:4:199:15 | ... == ... | barrier-guards.rb:199:36:199:38 | foo | false | +| barrier-guards.rb:199:4:199:31 | [false] ... or ... | barrier-guards.rb:199:4:199:43 | [false] ... or ... | false | +| barrier-guards.rb:199:4:199:31 | [false] ... or ... | barrier-guards.rb:199:36:199:38 | foo | false | +| barrier-guards.rb:199:4:199:43 | [true] ... or ... | barrier-guards.rb:200:5:200:7 | foo | true | +| barrier-guards.rb:199:20:199:31 | ... == ... | barrier-guards.rb:199:4:199:31 | [false] ... or ... | false | +| barrier-guards.rb:199:20:199:31 | ... == ... | barrier-guards.rb:199:4:199:43 | [false] ... or ... | false | +| barrier-guards.rb:199:20:199:31 | ... == ... | barrier-guards.rb:199:36:199:38 | foo | false | +| barrier-guards.rb:199:36:199:43 | ... == ... | barrier-guards.rb:199:4:199:43 | [false] ... or ... | false | +| barrier-guards.rb:203:4:203:15 | ... == ... | barrier-guards.rb:203:4:203:31 | [false] ... or ... | false | +| barrier-guards.rb:203:4:203:15 | ... == ... | barrier-guards.rb:203:4:203:47 | [false] ... or ... | false | +| barrier-guards.rb:203:4:203:15 | ... == ... | barrier-guards.rb:203:20:203:22 | self | false | +| barrier-guards.rb:203:4:203:15 | ... == ... | barrier-guards.rb:203:36:203:38 | foo | false | +| barrier-guards.rb:203:4:203:31 | [false] ... or ... | barrier-guards.rb:203:4:203:47 | [false] ... or ... | false | +| barrier-guards.rb:203:4:203:31 | [false] ... or ... | barrier-guards.rb:203:36:203:38 | foo | false | +| barrier-guards.rb:203:4:203:47 | [true] ... or ... | barrier-guards.rb:204:5:204:7 | foo | true | +| barrier-guards.rb:203:20:203:31 | ... == ... | barrier-guards.rb:203:4:203:31 | [false] ... or ... | false | +| barrier-guards.rb:203:20:203:31 | ... == ... | barrier-guards.rb:203:4:203:47 | [false] ... or ... | false | +| barrier-guards.rb:203:20:203:31 | ... == ... | barrier-guards.rb:203:36:203:38 | foo | false | +| barrier-guards.rb:203:36:203:47 | ... == ... | barrier-guards.rb:203:4:203:47 | [false] ... or ... | false | +| barrier-guards.rb:207:4:207:15 | ... == ... | barrier-guards.rb:207:4:207:21 | [true] ... and ... | true | +| barrier-guards.rb:207:4:207:15 | ... == ... | barrier-guards.rb:207:21:207:21 | self | true | +| barrier-guards.rb:207:4:207:15 | ... == ... | barrier-guards.rb:208:5:208:7 | foo | true | +| barrier-guards.rb:207:4:207:21 | [true] ... and ... | barrier-guards.rb:208:5:208:7 | foo | true | +| barrier-guards.rb:207:21:207:21 | call to x | barrier-guards.rb:207:4:207:21 | [true] ... and ... | true | +| barrier-guards.rb:207:21:207:21 | call to x | barrier-guards.rb:208:5:208:7 | foo | true | +| barrier-guards.rb:211:4:211:4 | call to x | barrier-guards.rb:211:4:211:21 | [true] ... and ... | true | +| barrier-guards.rb:211:4:211:4 | call to x | barrier-guards.rb:211:10:211:12 | foo | true | +| barrier-guards.rb:211:4:211:4 | call to x | barrier-guards.rb:212:5:212:7 | foo | true | +| barrier-guards.rb:211:4:211:21 | [true] ... and ... | barrier-guards.rb:212:5:212:7 | foo | true | +| barrier-guards.rb:211:10:211:21 | ... == ... | barrier-guards.rb:211:4:211:21 | [true] ... and ... | true | +| barrier-guards.rb:211:10:211:21 | ... == ... | barrier-guards.rb:212:5:212:7 | foo | true | +| barrier-guards.rb:215:4:215:4 | call to x | barrier-guards.rb:215:4:215:10 | [true] ... and ... | true | +| barrier-guards.rb:215:4:215:4 | call to x | barrier-guards.rb:215:4:215:27 | [true] ... and ... | true | +| barrier-guards.rb:215:4:215:4 | call to x | barrier-guards.rb:215:10:215:10 | self | true | +| barrier-guards.rb:215:4:215:4 | call to x | barrier-guards.rb:215:16:215:18 | foo | true | +| barrier-guards.rb:215:4:215:4 | call to x | barrier-guards.rb:216:5:216:7 | foo | true | +| barrier-guards.rb:215:4:215:10 | [true] ... and ... | barrier-guards.rb:215:4:215:27 | [true] ... and ... | true | +| barrier-guards.rb:215:4:215:10 | [true] ... and ... | barrier-guards.rb:215:16:215:18 | foo | true | +| barrier-guards.rb:215:4:215:10 | [true] ... and ... | barrier-guards.rb:216:5:216:7 | foo | true | +| barrier-guards.rb:215:4:215:27 | [true] ... and ... | barrier-guards.rb:216:5:216:7 | foo | true | +| barrier-guards.rb:215:10:215:10 | call to y | barrier-guards.rb:215:4:215:10 | [true] ... and ... | true | +| barrier-guards.rb:215:10:215:10 | call to y | barrier-guards.rb:215:4:215:27 | [true] ... and ... | true | +| barrier-guards.rb:215:10:215:10 | call to y | barrier-guards.rb:215:16:215:18 | foo | true | +| barrier-guards.rb:215:10:215:10 | call to y | barrier-guards.rb:216:5:216:7 | foo | true | +| barrier-guards.rb:215:16:215:27 | ... == ... | barrier-guards.rb:215:4:215:27 | [true] ... and ... | true | +| barrier-guards.rb:215:16:215:27 | ... == ... | barrier-guards.rb:216:5:216:7 | foo | true | +| barrier-guards.rb:219:4:219:15 | ... == ... | barrier-guards.rb:219:4:219:32 | [true] ... and ... | true | +| barrier-guards.rb:219:4:219:15 | ... == ... | barrier-guards.rb:219:21:219:23 | foo | true | +| barrier-guards.rb:219:4:219:15 | ... == ... | barrier-guards.rb:220:5:220:7 | foo | true | +| barrier-guards.rb:219:4:219:32 | [true] ... and ... | barrier-guards.rb:220:5:220:7 | foo | true | +| barrier-guards.rb:219:21:219:32 | ... == ... | barrier-guards.rb:219:4:219:32 | [true] ... and ... | true | +| barrier-guards.rb:219:21:219:32 | ... == ... | barrier-guards.rb:220:5:220:7 | foo | true | +| barrier-guards.rb:223:4:223:4 | call to x | barrier-guards.rb:223:4:223:10 | [true] ... and ... | true | +| barrier-guards.rb:223:4:223:4 | call to x | barrier-guards.rb:223:10:223:10 | self | true | +| barrier-guards.rb:223:4:223:4 | call to x | barrier-guards.rb:224:5:224:7 | foo | true | +| barrier-guards.rb:223:4:223:10 | [true] ... and ... | barrier-guards.rb:224:5:224:7 | foo | true | +| barrier-guards.rb:223:10:223:10 | call to y | barrier-guards.rb:223:4:223:10 | [true] ... and ... | true | +| barrier-guards.rb:223:10:223:10 | call to y | barrier-guards.rb:224:5:224:7 | foo | true | +| barrier-guards.rb:227:4:227:15 | ... == ... | barrier-guards.rb:227:4:227:21 | [true] ... and ... | true | +| barrier-guards.rb:227:4:227:15 | ... == ... | barrier-guards.rb:227:21:227:21 | self | true | +| barrier-guards.rb:227:4:227:15 | ... == ... | barrier-guards.rb:228:5:228:7 | self | true | +| barrier-guards.rb:227:4:227:21 | [true] ... and ... | barrier-guards.rb:228:5:228:7 | self | true | +| barrier-guards.rb:227:21:227:21 | call to y | barrier-guards.rb:227:4:227:21 | [true] ... and ... | true | +| barrier-guards.rb:227:21:227:21 | call to y | barrier-guards.rb:228:5:228:7 | self | true | +| barrier-guards.rb:232:1:233:19 | when ... | barrier-guards.rb:233:5:233:7 | foo | match | +| barrier-guards.rb:232:6:232:17 | ... == ... | barrier-guards.rb:232:1:233:19 | [false] when ... | false | +| barrier-guards.rb:232:6:232:17 | ... == ... | barrier-guards.rb:232:1:233:19 | when ... | true | +| barrier-guards.rb:232:6:232:17 | ... == ... | barrier-guards.rb:233:5:233:7 | foo | true | +| barrier-guards.rb:237:1:237:38 | when ... | barrier-guards.rb:237:24:237:26 | foo | match | +| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:237:1:237:38 | [false] when ... | false | +| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:237:1:237:38 | when ... | true | +| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:237:24:237:26 | foo | true | +| barrier-guards.rb:238:1:238:26 | when ... | barrier-guards.rb:238:24:238:26 | foo | match | +| barrier-guards.rb:238:6:238:17 | ... == ... | barrier-guards.rb:238:1:238:26 | [false] when ... | false | +| barrier-guards.rb:238:6:238:17 | ... == ... | barrier-guards.rb:238:1:238:26 | when ... | true | +| barrier-guards.rb:238:6:238:17 | ... == ... | barrier-guards.rb:238:24:238:26 | foo | true | +| barrier-guards.rb:239:1:239:22 | when ... | barrier-guards.rb:239:20:239:22 | foo | match | +| barrier-guards.rb:239:6:239:13 | ... == ... | barrier-guards.rb:239:1:239:22 | [false] when ... | false | +| barrier-guards.rb:239:6:239:13 | ... == ... | barrier-guards.rb:239:1:239:22 | when ... | true | +| barrier-guards.rb:239:6:239:13 | ... == ... | barrier-guards.rb:239:20:239:22 | foo | true | +| barrier-guards.rb:243:4:243:8 | "foo" | barrier-guards.rb:244:5:244:7 | foo | match | +| barrier-guards.rb:243:4:243:8 | "foo" | barrier-guards.rb:245:1:246:7 | in ... then ... | no-match | +| barrier-guards.rb:243:4:243:8 | "foo" | barrier-guards.rb:246:5:246:7 | foo | no-match | +| barrier-guards.rb:245:4:245:4 | x | barrier-guards.rb:246:5:246:7 | foo | match | +| barrier-guards.rb:250:4:250:8 | "foo" | barrier-guards.rb:251:5:251:7 | foo | match | +| barrier-guards.rb:250:4:250:8 | "foo" | barrier-guards.rb:254:1:256:3 | if ... | match | +| barrier-guards.rb:250:4:250:8 | "foo" | barrier-guards.rb:255:5:255:7 | foo | match | +| barrier-guards.rb:250:4:250:8 | "foo" | barrier-guards.rb:259:1:261:3 | if ... | match | +| barrier-guards.rb:250:4:250:8 | "foo" | barrier-guards.rb:260:5:260:7 | foo | match | +| barrier-guards.rb:250:4:250:8 | "foo" | barrier-guards.rb:264:1:266:3 | if ... | match | +| barrier-guards.rb:250:4:250:8 | "foo" | barrier-guards.rb:265:5:265:7 | foo | match | +| barrier-guards.rb:250:4:250:8 | "foo" | barrier-guards.rb:268:1:268:19 | ... && ... | match | +| barrier-guards.rb:250:4:250:8 | "foo" | barrier-guards.rb:268:17:268:19 | foo | match | +| barrier-guards.rb:250:4:250:8 | "foo" | barrier-guards.rb:269:1:269:19 | ... && ... | match | +| barrier-guards.rb:250:4:250:8 | "foo" | barrier-guards.rb:269:8:269:10 | foo | match | +| barrier-guards.rb:254:4:254:28 | ... == ... | barrier-guards.rb:255:5:255:7 | foo | true | +| barrier-guards.rb:259:4:259:16 | ... == ... | barrier-guards.rb:260:5:260:7 | foo | true | +| barrier-guards.rb:264:4:264:16 | ... == ... | barrier-guards.rb:265:5:265:7 | foo | true | +| barrier-guards.rb:268:1:268:12 | ... == ... | barrier-guards.rb:268:17:268:19 | foo | true | +| barrier-guards.rb:269:1:269:3 | foo | barrier-guards.rb:269:8:269:10 | foo | true | From 23e29e993b882145bbba21bfa3e5e470307f1a10 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 11 Nov 2022 08:24:39 +0100 Subject: [PATCH 0178/1420] C++: Split `std::string::insert` off in a separate class The `insert` function has two different return types: `iterator` and `basic_string&`. --- .../cpp/models/implementations/StdString.qll | 58 +++++++++++++++++-- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll index 3d2eda59799..46e79a6b30a 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll @@ -142,13 +142,13 @@ private class StdStringPlus extends TaintFunction { } /** - * The `std::string` functions `operator+=`, `append`, `insert` and - * `replace`. All of these functions combine the existing string - * with a new string (or character) from one of the arguments. + * The `std::string` functions `operator+=`, `append` and `replace`. + * All of these functions combine the existing string with a new + * string (or character) from one of the arguments. */ private class StdStringAppend extends TaintFunction { StdStringAppend() { - this.getClassAndName(["operator+=", "append", "insert", "replace"]) instanceof StdBasicString + this.getClassAndName(["operator+=", "append", "replace"]) instanceof StdBasicString } /** @@ -186,6 +186,56 @@ private class StdStringAppend extends TaintFunction { } } +/** + * The `std::string` function `insert`. + */ +private class StdStringInsert extends TaintFunction { + StdStringInsert() { + this.getClassAndName("insert") instanceof StdBasicString + } + + /** + * Gets the index of a parameter to this function that is a string (or + * character). + */ + int getAStringParameterIndex() { + this.getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *` + this.getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &` + this.getParameter(result).getUnspecifiedType() = + this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT` + } + + /** + * Gets the index of a parameter to this function that is an iterator. + */ + int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator } + + /** + * Holds if the return type is an iterator. + */ + predicate hasIteratorReturnValue() { this.getType() instanceof Iterator } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from string and parameter to string (qualifier) and return value + ( + input.isQualifierObject() or + input.isParameterDeref(this.getAStringParameterIndex()) or + input.isParameter(this.getAnIteratorParameterIndex()) + ) and + ( + output.isQualifierObject() + or + if this.hasIteratorReturnValue() then output.isReturnValue() else output.isReturnValueDeref() + ) + or + // reverse flow from returned reference to the qualifier (for writes to + // the result) + not this.hasIteratorReturnValue() and + input.isReturnValueDeref() and + output.isQualifierObject() + } +} + /** * The standard function `std::string.assign`. */ From ba00a0f37007c07be3e44b1fe134ae32bf6effe0 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 11 Nov 2022 08:30:22 +0100 Subject: [PATCH 0179/1420] C++: Share parameter logic in `std::string` model --- .../cpp/models/implementations/StdString.qll | 106 +++++------------- 1 file changed, 31 insertions(+), 75 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll index 46e79a6b30a..cf3ecb5e892 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll @@ -16,21 +16,14 @@ private class StdBasicString extends ClassTemplateInstantiation { } /** - * Additional model for `std::string` constructors that reference the character - * type of the container, or an iterator. For example construction from - * iterators: - * ``` - * std::string b(a.begin(), a.end()); - * ``` + * A `std::string` function for which taint should be propagated. */ -private class StdStringConstructor extends Constructor, TaintFunction { - StdStringConstructor() { this.getDeclaringType() instanceof StdBasicString } - +abstract private class StdStringTaintFunction extends TaintFunction { /** * Gets the index of a parameter to this function that is a string (or * character). */ - int getAStringParameterIndex() { + final int getAStringParameterIndex() { exists(Type paramType | paramType = this.getParameter(result).getUnspecifiedType() | // e.g. `std::basic_string::CharT *` paramType instanceof PointerType @@ -41,15 +34,28 @@ private class StdStringConstructor extends Constructor, TaintFunction { this.getDeclaringType().getTemplateArgument(2).(Type).getUnspecifiedType() or // i.e. `std::basic_string::CharT` - this.getParameter(result).getUnspecifiedType() = - this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() + paramType = this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() ) } /** * Gets the index of a parameter to this function that is an iterator. */ - int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator } + final int getAnIteratorParameterIndex() { + this.getParameter(result).getType() instanceof Iterator + } +} + +/** + * Additional model for `std::string` constructors that reference the character + * type of the container, or an iterator. For example construction from + * iterators: + * ``` + * std::string b(a.begin(), a.end()); + * ``` + */ +private class StdStringConstructor extends Constructor, StdStringTaintFunction { + StdStringConstructor() { this.getDeclaringType() instanceof StdBasicString } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // taint flow from any parameter of the value type to the returned object @@ -68,7 +74,7 @@ private class StdStringConstructor extends Constructor, TaintFunction { /** * The `std::string` function `c_str`. */ -private class StdStringCStr extends TaintFunction { +private class StdStringCStr extends StdStringTaintFunction { StdStringCStr() { this.getClassAndName("c_str") instanceof StdBasicString } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { @@ -81,7 +87,7 @@ private class StdStringCStr extends TaintFunction { /** * The `std::string` function `data`. */ -private class StdStringData extends TaintFunction { +private class StdStringData extends StdStringTaintFunction { StdStringData() { this.getClassAndName("data") instanceof StdBasicString } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { @@ -99,7 +105,7 @@ private class StdStringData extends TaintFunction { /** * The `std::string` function `push_back`. */ -private class StdStringPush extends TaintFunction { +private class StdStringPush extends StdStringTaintFunction { StdStringPush() { this.getClassAndName("push_back") instanceof StdBasicString } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { @@ -112,7 +118,7 @@ private class StdStringPush extends TaintFunction { /** * The `std::string` functions `front` and `back`. */ -private class StdStringFrontBack extends TaintFunction { +private class StdStringFrontBack extends StdStringTaintFunction { StdStringFrontBack() { this.getClassAndName(["front", "back"]) instanceof StdBasicString } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { @@ -125,7 +131,7 @@ private class StdStringFrontBack extends TaintFunction { /** * The (non-member) `std::string` function `operator+`. */ -private class StdStringPlus extends TaintFunction { +private class StdStringPlus extends StdStringTaintFunction { StdStringPlus() { this.hasQualifiedName(["std", "bsl"], "operator+") and this.getUnspecifiedType() instanceof StdBasicString @@ -146,27 +152,11 @@ private class StdStringPlus extends TaintFunction { * All of these functions combine the existing string with a new * string (or character) from one of the arguments. */ -private class StdStringAppend extends TaintFunction { +private class StdStringAppend extends StdStringTaintFunction { StdStringAppend() { this.getClassAndName(["operator+=", "append", "replace"]) instanceof StdBasicString } - /** - * Gets the index of a parameter to this function that is a string (or - * character). - */ - int getAStringParameterIndex() { - this.getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *` - this.getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &` - this.getParameter(result).getUnspecifiedType() = - this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT` - } - - /** - * Gets the index of a parameter to this function that is an iterator. - */ - int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator } - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // flow from string and parameter to string (qualifier) and return value ( @@ -189,26 +179,8 @@ private class StdStringAppend extends TaintFunction { /** * The `std::string` function `insert`. */ -private class StdStringInsert extends TaintFunction { - StdStringInsert() { - this.getClassAndName("insert") instanceof StdBasicString - } - - /** - * Gets the index of a parameter to this function that is a string (or - * character). - */ - int getAStringParameterIndex() { - this.getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *` - this.getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &` - this.getParameter(result).getUnspecifiedType() = - this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT` - } - - /** - * Gets the index of a parameter to this function that is an iterator. - */ - int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator } +private class StdStringInsert extends StdStringTaintFunction { + StdStringInsert() { this.getClassAndName("insert") instanceof StdBasicString } /** * Holds if the return type is an iterator. @@ -239,25 +211,9 @@ private class StdStringInsert extends TaintFunction { /** * The standard function `std::string.assign`. */ -private class StdStringAssign extends TaintFunction { +private class StdStringAssign extends StdStringTaintFunction { StdStringAssign() { this.getClassAndName("assign") instanceof StdBasicString } - /** - * Gets the index of a parameter to this function that is a string (or - * character). - */ - int getAStringParameterIndex() { - this.getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *` - this.getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &` - this.getParameter(result).getUnspecifiedType() = - this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT` - } - - /** - * Gets the index of a parameter to this function that is an iterator. - */ - int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator } - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // flow from parameter to string itself (qualifier) and return value ( @@ -279,7 +235,7 @@ private class StdStringAssign extends TaintFunction { /** * The standard function `std::string.copy`. */ -private class StdStringCopy extends TaintFunction { +private class StdStringCopy extends StdStringTaintFunction { StdStringCopy() { this.getClassAndName("copy") instanceof StdBasicString } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { @@ -292,7 +248,7 @@ private class StdStringCopy extends TaintFunction { /** * The standard function `std::string.substr`. */ -private class StdStringSubstr extends TaintFunction { +private class StdStringSubstr extends StdStringTaintFunction { StdStringSubstr() { this.getClassAndName("substr") instanceof StdBasicString } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { @@ -305,7 +261,7 @@ private class StdStringSubstr extends TaintFunction { /** * The `std::string` functions `at` and `operator[]`. */ -private class StdStringAt extends TaintFunction { +private class StdStringAt extends StdStringTaintFunction { StdStringAt() { this.getClassAndName(["at", "operator[]"]) instanceof StdBasicString } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { From 6d9745e5c35796240be0909f5c6c3a05bd4f0beb Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 24 Jun 2022 16:13:32 +0200 Subject: [PATCH 0180/1420] Python: Rewrite call-graph tests to be inline expectation (2/2) I ported the predicates showing difference between points-to and type-tracking, since it's helpful to see the list of differences, instead of having to parse expectations! --- .../library-tests/CallGraph/CallGraphTest.qll | 147 ------------------ .../CallGraph/InlineCallGraphTest.expected | 18 +++ .../CallGraph/InlineCallGraphTest.ql | 39 +++-- .../library-tests/CallGraph/PointsTo.expected | 6 - .../library-tests/CallGraph/PointsTo.ql | 10 -- .../library-tests/CallGraph/README.md | 38 ----- .../library-tests/CallGraph/Relative.expected | 20 --- .../library-tests/CallGraph/Relative.ql | 14 -- .../CallGraph/TypeTracker.expected | 21 --- .../library-tests/CallGraph/TypeTracker.ql | 10 -- .../CallGraph/code/runtime_decision.py | 4 - .../library-tests/CallGraph/code/simple.py | 9 +- .../code/underscore_prefix_func_name.py | 4 - 13 files changed, 48 insertions(+), 292 deletions(-) delete mode 100644 python/ql/test/experimental/library-tests/CallGraph/CallGraphTest.qll delete mode 100644 python/ql/test/experimental/library-tests/CallGraph/PointsTo.expected delete mode 100644 python/ql/test/experimental/library-tests/CallGraph/PointsTo.ql delete mode 100644 python/ql/test/experimental/library-tests/CallGraph/README.md delete mode 100644 python/ql/test/experimental/library-tests/CallGraph/Relative.expected delete mode 100644 python/ql/test/experimental/library-tests/CallGraph/Relative.ql delete mode 100644 python/ql/test/experimental/library-tests/CallGraph/TypeTracker.expected delete mode 100644 python/ql/test/experimental/library-tests/CallGraph/TypeTracker.ql diff --git a/python/ql/test/experimental/library-tests/CallGraph/CallGraphTest.qll b/python/ql/test/experimental/library-tests/CallGraph/CallGraphTest.qll deleted file mode 100644 index 0f8b3162980..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph/CallGraphTest.qll +++ /dev/null @@ -1,147 +0,0 @@ -import python - -/** Gets the comment on the line above `ast` */ -Comment commentFor(AstNode ast) { - exists(int line | line = ast.getLocation().getStartLine() - 1 | - result - .getLocation() - .hasLocationInfo(ast.getLocation().getFile().getAbsolutePath(), line, _, line, _) - ) -} - -/** Gets the value from `tag:value` in the comment for `ast` */ -string getAnnotation(AstNode ast, string tag) { - exists(Comment comment, string match, string theRegex | - theRegex = "([\\w]+):([\\w.]+)" and - comment = commentFor(ast) and - match = comment.getText().regexpFind(theRegex, _, _) and - tag = match.regexpCapture(theRegex, 1) and - result = match.regexpCapture(theRegex, 2) - ) -} - -/** Gets a callable annotated with `name:name` */ -Function annotatedCallable(string name) { name = getAnnotation(result, "name") } - -/** Gets a call annotated with `calls:name` */ -Call annotatedCall(string name) { name = getAnnotation(result, "calls") } - -predicate missingAnnotationForCallable(string name, Call call) { - call = annotatedCall(name) and - not exists(annotatedCallable(name)) -} - -predicate nonUniqueAnnotationForCallable(string name, Function callable) { - strictcount(annotatedCallable(name)) > 1 and - callable = annotatedCallable(name) -} - -predicate missingAnnotationForCall(string name, Function callable) { - not exists(annotatedCall(name)) and - callable = annotatedCallable(name) -} - -/** There is an obvious problem with the annotation `name` */ -predicate nameInErrorState(string name) { - missingAnnotationForCallable(name, _) - or - nonUniqueAnnotationForCallable(name, _) - or - missingAnnotationForCall(name, _) -} - -/** Source code has annotation with `name` showing that `call` will call `callable` */ -predicate annotatedCallEdge(string name, Call call, Function callable) { - not nameInErrorState(name) and - call = annotatedCall(name) and - callable = annotatedCallable(name) -} - -// ------------------------- Annotation debug query predicates ------------------------- -query predicate debug_missingAnnotationForCallable(Call call, string message) { - exists(string name | - message = - "This call is annotated with '" + name + - "', but no callable with that annotation was extracted. Please fix." and - missingAnnotationForCallable(name, call) - ) -} - -query predicate debug_nonUniqueAnnotationForCallable(Function callable, string message) { - exists(string name | - message = "Multiple callables are annotated with '" + name + "'. Please fix." and - nonUniqueAnnotationForCallable(name, callable) - ) -} - -query predicate debug_missingAnnotationForCall(Function callable, string message) { - exists(string name | - message = - "This callable is annotated with '" + name + - "', but no call with that annotation was extracted. Please fix." and - missingAnnotationForCall(name, callable) - ) -} - -// ------------------------- Call Graph resolution ------------------------- -private newtype TCallGraphResolver = - TPointsToResolver() or - TTypeTrackerResolver() - -/** A method of call graph resolution */ -abstract class CallGraphResolver extends TCallGraphResolver { - abstract predicate callEdge(Call call, Function callable); - - /** - * Holds if annotations show that `call` will call `callable`, - * but our call graph resolver was not able to figure that out - */ - predicate expectedCallEdgeNotFound(Call call, Function callable) { - annotatedCallEdge(_, call, callable) and - not this.callEdge(call, callable) - } - - /** - * Holds if there are no annotations that show that `call` will call `callable` (where at least one of these are annotated), - * but the call graph resolver claims that `call` will call `callable` - */ - predicate unexpectedCallEdgeFound(Call call, Function callable, string message) { - this.callEdge(call, callable) and - not annotatedCallEdge(_, call, callable) and - ( - exists(string name | - message = "Call resolved to the callable named '" + name + "' but was not annotated as such" and - callable = annotatedCallable(name) and - not nameInErrorState(name) - ) - or - exists(string name | - message = "Annotated call resolved to unannotated callable" and - call = annotatedCall(name) and - not nameInErrorState(name) and - not exists( | callable = annotatedCallable(_)) - ) - ) - } - - string toString() { result = "CallGraphResolver" } -} - -/** A call graph resolver based on the existing points-to analysis */ -class PointsToResolver extends CallGraphResolver, TPointsToResolver { - override predicate callEdge(Call call, Function callable) { - exists(PythonFunctionValue funcValue | - funcValue.getScope() = callable and - call = funcValue.getACall().getNode() - ) - } - - override string toString() { result = "PointsToResolver" } -} - -/** A call graph resolved based on Type Trackers */ -class TypeTrackerResolver extends CallGraphResolver, TTypeTrackerResolver { - override predicate callEdge(Call call, Function callable) { none() } - - override string toString() { result = "TypeTrackerResolver" } -} diff --git a/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.expected b/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.expected index 2ff4aeb6865..975ac22dd2d 100644 --- a/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.expected +++ b/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.expected @@ -2,3 +2,21 @@ failures debug_callableNotUnique | code/class_advanced.py:18:5:18:18 | Function arg | Qualified function name 'B.arg' is not unique. Please fix. | | code/class_advanced.py:23:5:23:25 | Function arg | Qualified function name 'B.arg' is not unique. Please fix. | +pointsTo_found_typeTracker_notFound +| code/class_simple.py:28:1:28:15 | ControlFlowNode for Attribute() | A.some_method | +| code/class_simple.py:30:1:30:21 | ControlFlowNode for Attribute() | A.some_staticmethod | +| code/class_simple.py:32:1:32:20 | ControlFlowNode for Attribute() | A.some_classmethod | +| code/class_simple.py:35:1:35:21 | ControlFlowNode for Attribute() | A.some_staticmethod | +| code/class_simple.py:37:1:37:20 | ControlFlowNode for Attribute() | A.some_classmethod | +| code/runtime_decision.py:18:1:18:6 | ControlFlowNode for func() | rd_bar | +| code/runtime_decision.py:18:1:18:6 | ControlFlowNode for func() | rd_foo | +| code/runtime_decision.py:26:1:26:7 | ControlFlowNode for func2() | rd_bar | +| code/runtime_decision.py:26:1:26:7 | ControlFlowNode for func2() | rd_foo | +| code/simple.py:15:1:15:5 | ControlFlowNode for foo() | foo | +| code/simple.py:16:1:16:14 | ControlFlowNode for indirect_foo() | foo | +| code/simple.py:17:1:17:5 | ControlFlowNode for bar() | bar | +| code/simple.py:18:1:18:5 | ControlFlowNode for lam() | lambda[simple.py:12:7] | +| code/underscore_prefix_func_name.py:18:5:18:19 | ControlFlowNode for some_function() | some_function | +| code/underscore_prefix_func_name.py:21:5:21:19 | ControlFlowNode for some_function() | some_function | +| code/underscore_prefix_func_name.py:24:1:24:21 | ControlFlowNode for _works_since_called() | _works_since_called | +typeTracker_found_pointsTo_notFound diff --git a/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.ql b/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.ql index 6b59751e43b..50ad10bd191 100644 --- a/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.ql +++ b/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.ql @@ -28,22 +28,41 @@ class CallGraphTest extends InlineExpectationsTest { | location = call.getLocation() and element = call.toString() and - ( - // note: `target.getQualifiedName` for Lambdas is just "lambda", so is not very useful :| - not target.isLambda() and - value = target.getQualifiedName() - or - target.isLambda() and - value = - "lambda[" + target.getLocation().getFile().getShortName() + ":" + - target.getLocation().getStartLine() + ":" + target.getLocation().getStartColumn() + "]" - ) + value = betterQualName(target) ) } } +bindingset[func] +string betterQualName(Function func) { + // note: `target.getQualifiedName` for Lambdas is just "lambda", so is not very useful :| + not func.isLambda() and + result = func.getQualifiedName() + or + func.isLambda() and + result = + "lambda[" + func.getLocation().getFile().getShortName() + ":" + + func.getLocation().getStartLine() + ":" + func.getLocation().getStartColumn() + "]" +} + query predicate debug_callableNotUnique(Function callable, string message) { exists(Function f | f != callable and f.getQualifiedName() = callable.getQualifiedName()) and message = "Qualified function name '" + callable.getQualifiedName() + "' is not unique. Please fix." } + +query predicate pointsTo_found_typeTracker_notFound(CallNode call, string qualname) { + exists(Function target | + pointsToCallEdge(call, target) and + not typeTrackerCallEdge(call, target) and + qualname = betterQualName(target) + ) +} + +query predicate typeTracker_found_pointsTo_notFound(CallNode call, string qualname) { + exists(Function target | + not pointsToCallEdge(call, target) and + typeTrackerCallEdge(call, target) and + qualname = betterQualName(target) + ) +} diff --git a/python/ql/test/experimental/library-tests/CallGraph/PointsTo.expected b/python/ql/test/experimental/library-tests/CallGraph/PointsTo.expected deleted file mode 100644 index e7db0fde98c..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph/PointsTo.expected +++ /dev/null @@ -1,6 +0,0 @@ -debug_missingAnnotationForCallable -debug_nonUniqueAnnotationForCallable -debug_missingAnnotationForCall -expectedCallEdgeNotFound -| code/underscore_prefix_func_name.py:16:5:16:19 | some_function() | code/underscore_prefix_func_name.py:10:1:10:20 | Function some_function | -unexpectedCallEdgeFound diff --git a/python/ql/test/experimental/library-tests/CallGraph/PointsTo.ql b/python/ql/test/experimental/library-tests/CallGraph/PointsTo.ql deleted file mode 100644 index f86842f2fe4..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph/PointsTo.ql +++ /dev/null @@ -1,10 +0,0 @@ -import python -import CallGraphTest - -query predicate expectedCallEdgeNotFound(Call call, Function callable) { - any(PointsToResolver r).expectedCallEdgeNotFound(call, callable) -} - -query predicate unexpectedCallEdgeFound(Call call, Function callable, string message) { - any(PointsToResolver r).unexpectedCallEdgeFound(call, callable, message) -} diff --git a/python/ql/test/experimental/library-tests/CallGraph/README.md b/python/ql/test/experimental/library-tests/CallGraph/README.md deleted file mode 100644 index 0fbf6bdac9d..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Call Graph Tests - -A small testing framework for our call graph resolution. It relies on manual annotation of calls and callables, **and will only include output if something is wrong**. For example, if we are not able to resolve that the `foo()` call will call the `foo` function, that should give an alert. - -```py -# name:foo -def foo(): - pass -# calls:foo -foo() -``` - -This is greatly inspired by [`CallGraphs/AnnotatedTest`](https://github.com/github/codeql/blob/696d19cb1440b6f6a75c6a2c1319e18860ceb436/javascript/ql/test/library-tests/CallGraphs/AnnotatedTest/Test.ql) from JavaScript. - -IMPORTANT: Names used in annotations are not scoped, so must be unique globally. (this is a bit annoying, but makes things simple). If multiple identical annotations are used, an error message will be output. - -Important files: - -- `CallGraphTest.qll`: main code to find annotated calls/callables and setting everything up. -- `PointsTo.ql`: results when using points-to for call graph resolution. -- `TypeTracker.ql`: results when using TypeTracking for call graph resolution. -- `Relative.ql`: differences between using points-to and TypeTracking. -- `code/` contains the actual Python code we test against (included by `test.py`). - -All queries will also execute some `debug_*` predicates. These highlight any obvious problems with the annotation setup, and so there should never be any results committed. To show that this works as expected, see the [CallGraph-xfail](../CallGraph-xfail/) which uses symlinked versions of the files in this directory (can't include as subdir, so has to be a sibling). - -## `options` file - -If the value for `--max-import-depth` is set so that `import random` will extract `random.py` from the standard library, BUT NO transitive imports are extracted, then points-to analysis will fail to handle the following snippet. - -```py -import random -if random.random() < 0.5: - func = foo -else: - func = bar -func() -``` diff --git a/python/ql/test/experimental/library-tests/CallGraph/Relative.expected b/python/ql/test/experimental/library-tests/CallGraph/Relative.expected deleted file mode 100644 index 9882dda21bf..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph/Relative.expected +++ /dev/null @@ -1,20 +0,0 @@ -debug_missingAnnotationForCallable -debug_nonUniqueAnnotationForCallable -debug_missingAnnotationForCall -pointsTo_found_typeTracker_notFound -| code/class_simple.py:28:1:28:15 | Attribute() | code/class_simple.py:8:5:8:26 | Function some_method | -| code/class_simple.py:30:1:30:21 | Attribute() | code/class_simple.py:13:5:13:28 | Function some_staticmethod | -| code/class_simple.py:32:1:32:20 | Attribute() | code/class_simple.py:18:5:18:30 | Function some_classmethod | -| code/class_simple.py:35:1:35:21 | Attribute() | code/class_simple.py:13:5:13:28 | Function some_staticmethod | -| code/class_simple.py:37:1:37:20 | Attribute() | code/class_simple.py:18:5:18:30 | Function some_classmethod | -| code/runtime_decision.py:21:1:21:6 | func() | code/runtime_decision.py:8:1:8:13 | Function rd_foo | -| code/runtime_decision.py:21:1:21:6 | func() | code/runtime_decision.py:12:1:12:13 | Function rd_bar | -| code/runtime_decision.py:30:1:30:7 | func2() | code/runtime_decision.py:8:1:8:13 | Function rd_foo | -| code/runtime_decision.py:30:1:30:7 | func2() | code/runtime_decision.py:12:1:12:13 | Function rd_bar | -| code/simple.py:19:1:19:5 | foo() | code/simple.py:2:1:2:10 | Function foo | -| code/simple.py:21:1:21:14 | indirect_foo() | code/simple.py:2:1:2:10 | Function foo | -| code/simple.py:23:1:23:5 | bar() | code/simple.py:10:1:10:10 | Function bar | -| code/simple.py:25:1:25:5 | lam() | code/simple.py:15:7:15:36 | Function lambda | -| code/underscore_prefix_func_name.py:21:5:21:19 | some_function() | code/underscore_prefix_func_name.py:10:1:10:20 | Function some_function | -| code/underscore_prefix_func_name.py:25:5:25:19 | some_function() | code/underscore_prefix_func_name.py:10:1:10:20 | Function some_function | -pointsTo_notFound_typeTracker_found diff --git a/python/ql/test/experimental/library-tests/CallGraph/Relative.ql b/python/ql/test/experimental/library-tests/CallGraph/Relative.ql deleted file mode 100644 index f62e4d21cbd..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph/Relative.ql +++ /dev/null @@ -1,14 +0,0 @@ -import python -import CallGraphTest - -query predicate pointsTo_found_typeTracker_notFound(Call call, Function callable) { - annotatedCallEdge(_, call, callable) and - any(PointsToResolver r).callEdge(call, callable) and - not any(TypeTrackerResolver r).callEdge(call, callable) -} - -query predicate pointsTo_notFound_typeTracker_found(Call call, Function callable) { - annotatedCallEdge(_, call, callable) and - not any(PointsToResolver r).callEdge(call, callable) and - any(TypeTrackerResolver r).callEdge(call, callable) -} diff --git a/python/ql/test/experimental/library-tests/CallGraph/TypeTracker.expected b/python/ql/test/experimental/library-tests/CallGraph/TypeTracker.expected deleted file mode 100644 index 5fc5376ca25..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph/TypeTracker.expected +++ /dev/null @@ -1,21 +0,0 @@ -debug_missingAnnotationForCallable -debug_nonUniqueAnnotationForCallable -debug_missingAnnotationForCall -expectedCallEdgeNotFound -| code/class_simple.py:28:1:28:15 | Attribute() | code/class_simple.py:8:5:8:26 | Function some_method | -| code/class_simple.py:30:1:30:21 | Attribute() | code/class_simple.py:13:5:13:28 | Function some_staticmethod | -| code/class_simple.py:32:1:32:20 | Attribute() | code/class_simple.py:18:5:18:30 | Function some_classmethod | -| code/class_simple.py:35:1:35:21 | Attribute() | code/class_simple.py:13:5:13:28 | Function some_staticmethod | -| code/class_simple.py:37:1:37:20 | Attribute() | code/class_simple.py:18:5:18:30 | Function some_classmethod | -| code/runtime_decision.py:21:1:21:6 | func() | code/runtime_decision.py:8:1:8:13 | Function rd_foo | -| code/runtime_decision.py:21:1:21:6 | func() | code/runtime_decision.py:12:1:12:13 | Function rd_bar | -| code/runtime_decision.py:30:1:30:7 | func2() | code/runtime_decision.py:8:1:8:13 | Function rd_foo | -| code/runtime_decision.py:30:1:30:7 | func2() | code/runtime_decision.py:12:1:12:13 | Function rd_bar | -| code/simple.py:19:1:19:5 | foo() | code/simple.py:2:1:2:10 | Function foo | -| code/simple.py:21:1:21:14 | indirect_foo() | code/simple.py:2:1:2:10 | Function foo | -| code/simple.py:23:1:23:5 | bar() | code/simple.py:10:1:10:10 | Function bar | -| code/simple.py:25:1:25:5 | lam() | code/simple.py:15:7:15:36 | Function lambda | -| code/underscore_prefix_func_name.py:16:5:16:19 | some_function() | code/underscore_prefix_func_name.py:10:1:10:20 | Function some_function | -| code/underscore_prefix_func_name.py:21:5:21:19 | some_function() | code/underscore_prefix_func_name.py:10:1:10:20 | Function some_function | -| code/underscore_prefix_func_name.py:25:5:25:19 | some_function() | code/underscore_prefix_func_name.py:10:1:10:20 | Function some_function | -unexpectedCallEdgeFound diff --git a/python/ql/test/experimental/library-tests/CallGraph/TypeTracker.ql b/python/ql/test/experimental/library-tests/CallGraph/TypeTracker.ql deleted file mode 100644 index a62332e3839..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph/TypeTracker.ql +++ /dev/null @@ -1,10 +0,0 @@ -import python -import CallGraphTest - -query predicate expectedCallEdgeNotFound(Call call, Function callable) { - any(TypeTrackerResolver r).expectedCallEdgeNotFound(call, callable) -} - -query predicate unexpectedCallEdgeFound(Call call, Function callable, string message) { - any(TypeTrackerResolver r).unexpectedCallEdgeFound(call, callable, message) -} diff --git a/python/ql/test/experimental/library-tests/CallGraph/code/runtime_decision.py b/python/ql/test/experimental/library-tests/CallGraph/code/runtime_decision.py index a271cbd9a6f..3c4ebbb73e1 100644 --- a/python/ql/test/experimental/library-tests/CallGraph/code/runtime_decision.py +++ b/python/ql/test/experimental/library-tests/CallGraph/code/runtime_decision.py @@ -4,11 +4,9 @@ import random # hmm, annoying that you have to keep names unique across files :| # since I like to use foo and bar ALL the time :D -# name:rd_foo def rd_foo(): print('rd_foo') -# name:rd_bar def rd_bar(): print('rd_bar') @@ -17,7 +15,6 @@ if len(sys.argv) >= 2 and not sys.argv[1] in ['0', 'False', 'false']: else: func = rd_bar -# calls:rd_foo calls:rd_bar func() # $ pt=rd_foo pt=rd_bar # Random doesn't work with points-to :O @@ -26,5 +23,4 @@ if random.random() < 0.5: else: func2 = rd_bar -# calls:rd_foo calls:rd_bar func2() # $ pt=rd_foo pt=rd_bar diff --git a/python/ql/test/experimental/library-tests/CallGraph/code/simple.py b/python/ql/test/experimental/library-tests/CallGraph/code/simple.py index 210df4f209e..ac07ace93b2 100644 --- a/python/ql/test/experimental/library-tests/CallGraph/code/simple.py +++ b/python/ql/test/experimental/library-tests/CallGraph/code/simple.py @@ -1,4 +1,3 @@ -# name:foo def foo(): print("foo called") @@ -6,22 +5,16 @@ def foo(): indirect_foo = foo -# name:bar def bar(): print("bar called") -# name:lam lam = lambda: print("lambda called") -# calls:foo foo() # $ pt=foo -# calls:foo indirect_foo() # $ pt=foo -# calls:bar bar() # $ pt=bar -# calls:lam -lam() # $ pt=lambda[simple.py:15:7] +lam() # $ pt=lambda[simple.py:12:7] # python -m trace --trackcalls simple.py diff --git a/python/ql/test/experimental/library-tests/CallGraph/code/underscore_prefix_func_name.py b/python/ql/test/experimental/library-tests/CallGraph/code/underscore_prefix_func_name.py index 1a1efe9d7b6..fb3f5fc45a8 100644 --- a/python/ql/test/experimental/library-tests/CallGraph/code/underscore_prefix_func_name.py +++ b/python/ql/test/experimental/library-tests/CallGraph/code/underscore_prefix_func_name.py @@ -6,22 +6,18 @@ # points-to information about the `open` call in # https://google-gruyere.appspot.com/code/gruyere.py on line 227 -# name:some_function def some_function(): print('some_function') def _ignored(): print('_ignored') - # calls:some_function some_function() def _works_since_called(): print('_works_since_called') - # calls:some_function some_function() # $ pt=some_function def works_even_though_not_called(): - # calls:some_function some_function() # $ pt=some_function globals()['_ignored']() From b60504f40463e1eed8a0aef2ff24b74a307e847b Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 24 Jun 2022 16:15:04 +0200 Subject: [PATCH 0181/1420] Python: Delete `CallGraph-xfail` No longer needed since we're using an established testing framework now --- .../CallGraph-xfail/PointsTo.expected | 18 -------- .../CallGraph-xfail/PointsTo.qlref | 1 - .../library-tests/CallGraph-xfail/README.md | 1 - .../CallGraph-xfail/annotation_xfail.py | 21 --------- .../CallGraph-xfail/call_edge_xfail.py | 43 ------------------- 5 files changed, 84 deletions(-) delete mode 100644 python/ql/test/experimental/library-tests/CallGraph-xfail/PointsTo.expected delete mode 100644 python/ql/test/experimental/library-tests/CallGraph-xfail/PointsTo.qlref delete mode 100644 python/ql/test/experimental/library-tests/CallGraph-xfail/README.md delete mode 100644 python/ql/test/experimental/library-tests/CallGraph-xfail/annotation_xfail.py delete mode 100644 python/ql/test/experimental/library-tests/CallGraph-xfail/call_edge_xfail.py diff --git a/python/ql/test/experimental/library-tests/CallGraph-xfail/PointsTo.expected b/python/ql/test/experimental/library-tests/CallGraph-xfail/PointsTo.expected deleted file mode 100644 index 680cee0e8b2..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-xfail/PointsTo.expected +++ /dev/null @@ -1,18 +0,0 @@ -debug_missingAnnotationForCallable -| annotation_xfail.py:10:1:10:24 | callable_not_annotated() | This call is annotated with 'callable_not_annotated', but no callable with that annotation was extracted. Please fix. | -debug_nonUniqueAnnotationForCallable -| annotation_xfail.py:13:1:13:17 | Function non_unique | Multiple callables are annotated with 'non_unique'. Please fix. | -| annotation_xfail.py:17:1:17:26 | Function too_much_copy_paste | Multiple callables are annotated with 'non_unique'. Please fix. | -debug_missingAnnotationForCall -| annotation_xfail.py:2:1:2:24 | Function no_annotated_call | This callable is annotated with 'no_annotated_call', but no call with that annotation was extracted. Please fix. | -expectedCallEdgeNotFound -| call_edge_xfail.py:36:1:36:11 | xfail_foo() | call_edge_xfail.py:8:1:8:16 | Function xfail_bar | -| call_edge_xfail.py:39:1:39:11 | xfail_baz() | call_edge_xfail.py:8:1:8:16 | Function xfail_bar | -unexpectedCallEdgeFound -| call_edge_xfail.py:29:1:29:6 | func() | call_edge_xfail.py:4:1:4:16 | Function xfail_foo | Call resolved to the callable named 'xfail_foo' but was not annotated as such | -| call_edge_xfail.py:29:1:29:6 | func() | call_edge_xfail.py:8:1:8:16 | Function xfail_bar | Call resolved to the callable named 'xfail_bar' but was not annotated as such | -| call_edge_xfail.py:30:1:30:11 | xfail_foo() | call_edge_xfail.py:4:1:4:16 | Function xfail_foo | Call resolved to the callable named 'xfail_foo' but was not annotated as such | -| call_edge_xfail.py:31:1:31:14 | xfail_lambda() | call_edge_xfail.py:15:16:15:44 | Function lambda | Call resolved to the callable named 'xfail_lambda' but was not annotated as such | -| call_edge_xfail.py:36:1:36:11 | xfail_foo() | call_edge_xfail.py:4:1:4:16 | Function xfail_foo | Call resolved to the callable named 'xfail_foo' but was not annotated as such | -| call_edge_xfail.py:39:1:39:11 | xfail_baz() | call_edge_xfail.py:11:1:11:16 | Function xfail_baz | Annotated call resolved to unannotated callable | -| call_edge_xfail.py:43:1:43:6 | func() | call_edge_xfail.py:8:1:8:16 | Function xfail_bar | Call resolved to the callable named 'xfail_bar' but was not annotated as such | diff --git a/python/ql/test/experimental/library-tests/CallGraph-xfail/PointsTo.qlref b/python/ql/test/experimental/library-tests/CallGraph-xfail/PointsTo.qlref deleted file mode 100644 index da8a0d1631a..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-xfail/PointsTo.qlref +++ /dev/null @@ -1 +0,0 @@ -../CallGraph/PointsTo.ql diff --git a/python/ql/test/experimental/library-tests/CallGraph-xfail/README.md b/python/ql/test/experimental/library-tests/CallGraph-xfail/README.md deleted file mode 100644 index 39021bce2a1..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-xfail/README.md +++ /dev/null @@ -1 +0,0 @@ -Test that show our failure handling in [CallGraph](../CallGraph/) works as expected. diff --git a/python/ql/test/experimental/library-tests/CallGraph-xfail/annotation_xfail.py b/python/ql/test/experimental/library-tests/CallGraph-xfail/annotation_xfail.py deleted file mode 100644 index f8dbf88aa02..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-xfail/annotation_xfail.py +++ /dev/null @@ -1,21 +0,0 @@ -# name:no_annotated_call -def no_annotated_call(): - pass - -def callable_not_annotated(): - pass - -no_annotated_call() -# calls:callable_not_annotated -callable_not_annotated() - -# name:non_unique -def non_unique(): - pass - -# name:non_unique -def too_much_copy_paste(): - pass - -# calls:non_unique -non_unique() diff --git a/python/ql/test/experimental/library-tests/CallGraph-xfail/call_edge_xfail.py b/python/ql/test/experimental/library-tests/CallGraph-xfail/call_edge_xfail.py deleted file mode 100644 index e72e02e376b..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-xfail/call_edge_xfail.py +++ /dev/null @@ -1,43 +0,0 @@ -import sys - -# name:xfail_foo -def xfail_foo(): - print('xfail_foo') - -# name:xfail_bar -def xfail_bar(): - print('xfail_bar') - -def xfail_baz(): - print('xfail_baz') - -# name:xfail_lambda -xfail_lambda = lambda: print('xfail_lambda') - -if len(sys.argv) >= 2 and not sys.argv[1] in ['0', 'False', 'false']: - func = xfail_foo -else: - func = xfail_bar - -# Correct usage to suppress bad annotation errors -# calls:xfail_foo calls:xfail_bar -func() -# calls:xfail_lambda -xfail_lambda() - -# These are not annotated, and will give rise to unexpectedCallEdgeFound -func() -xfail_foo() -xfail_lambda() - -# These are annotated wrongly, and will give rise to unexpectedCallEdgeFound - -# calls:xfail_bar -xfail_foo() - -# calls:xfail_bar -xfail_baz() - -# The annotation is incomplete (does not include the call to xfail_bar) -# calls:xfail_foo -func() From ab42521906e8caff7eedd751c4d5584422cbea57 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 24 Jun 2022 16:17:26 +0200 Subject: [PATCH 0182/1420] Python: Port `CallGraph-implicit-init` tests to the new call-graph test setup. Nice that we can write `MISSING:` now! --- .../InlineCallGraphTest.expected | 5 +++++ .../CallGraph-implicit-init/InlineCallGraphTest.qlref | 1 + .../CallGraph-implicit-init/PointsTo.expected | 6 ------ .../CallGraph-implicit-init/PointsTo.qlref | 1 - .../CallGraph-implicit-init/Relative.expected | 6 ------ .../CallGraph-implicit-init/Relative.qlref | 1 - .../CallGraph-implicit-init/TypeTracker.expected | 7 ------- .../CallGraph-implicit-init/TypeTracker.qlref | 1 - .../library-tests/CallGraph-implicit-init/example.py | 10 +++++----- .../library-tests/CallGraph-implicit-init/foo/bar/a.py | 1 - .../CallGraph-implicit-init/foo_explicit/bar/a.py | 1 - 11 files changed, 11 insertions(+), 29 deletions(-) create mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/InlineCallGraphTest.expected create mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/InlineCallGraphTest.qlref delete mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected delete mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.qlref delete mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.expected delete mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.qlref delete mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected delete mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.qlref diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/InlineCallGraphTest.expected b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/InlineCallGraphTest.expected new file mode 100644 index 00000000000..c847f9a8aa2 --- /dev/null +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/InlineCallGraphTest.expected @@ -0,0 +1,5 @@ +failures +debug_callableNotUnique +pointsTo_found_typeTracker_notFound +| example.py:22:1:22:16 | ControlFlowNode for explicit_afunc() | explicit_afunc | +typeTracker_found_pointsTo_notFound diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/InlineCallGraphTest.qlref b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/InlineCallGraphTest.qlref new file mode 100644 index 00000000000..25117a4582b --- /dev/null +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/InlineCallGraphTest.qlref @@ -0,0 +1 @@ +../CallGraph/InlineCallGraphTest.ql diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected deleted file mode 100644 index 349d99b46d8..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected +++ /dev/null @@ -1,6 +0,0 @@ -debug_missingAnnotationForCallable -debug_nonUniqueAnnotationForCallable -debug_missingAnnotationForCall -expectedCallEdgeNotFound -| example.py:19:1:19:7 | afunc() | foo/bar/a.py:2:1:2:12 | Function afunc | -unexpectedCallEdgeFound diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.qlref b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.qlref deleted file mode 100644 index da8a0d1631a..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.qlref +++ /dev/null @@ -1 +0,0 @@ -../CallGraph/PointsTo.ql diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.expected b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.expected deleted file mode 100644 index 38aab0b5888..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.expected +++ /dev/null @@ -1,6 +0,0 @@ -debug_missingAnnotationForCallable -debug_nonUniqueAnnotationForCallable -debug_missingAnnotationForCall -pointsTo_found_typeTracker_notFound -| example.py:22:1:22:16 | explicit_afunc() | foo_explicit/bar/a.py:2:1:2:21 | Function explicit_afunc | -pointsTo_notFound_typeTracker_found diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.qlref b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.qlref deleted file mode 100644 index 2ffa6c10d51..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.qlref +++ /dev/null @@ -1 +0,0 @@ -../CallGraph/Relative.ql diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected deleted file mode 100644 index 5283683a6a5..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected +++ /dev/null @@ -1,7 +0,0 @@ -debug_missingAnnotationForCallable -debug_nonUniqueAnnotationForCallable -debug_missingAnnotationForCall -expectedCallEdgeNotFound -| example.py:19:1:19:7 | afunc() | foo/bar/a.py:2:1:2:12 | Function afunc | -| example.py:22:1:22:16 | explicit_afunc() | foo_explicit/bar/a.py:2:1:2:21 | Function explicit_afunc | -unexpectedCallEdgeFound diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.qlref b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.qlref deleted file mode 100644 index 60c029a5510..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.qlref +++ /dev/null @@ -1 +0,0 @@ -../CallGraph/TypeTracker.ql diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/example.py b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/example.py index 35d56620af7..75ad8a9db11 100644 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/example.py +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/example.py @@ -5,18 +5,18 @@ This is not included in the standard `CallGraph/code` folder, since we're testin understanding import work properly, so it's better to have a clean test setup that is obviously correct (the other one isn't in regards to imports). -Technically this is part of PEP 420 -- Implicit Namespace Packages, but does use the +Technically this is part of PEP 420 -- Implicit Namespace Packages, but does not use the *real* namespace package feature of allowing source code for a single package to reside in multiple places. +Maybe this should have been an import resolution test, and not a call-graph test ¯\_(ツ)_/¯ + Since PEP 420 was accepted in Python 3, this test is Python 3 only. """ from foo.bar.a import afunc from foo_explicit.bar.a import explicit_afunc -# calls:afunc -afunc() +afunc() # $ MISSING: pt,tt=afunc -# calls:explicit_afunc -explicit_afunc() +explicit_afunc() # $ pt=explicit_afunc MISSING: tt=explicit_afunc diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo/bar/a.py b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo/bar/a.py index a294b33191e..bc639f6c537 100644 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo/bar/a.py +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo/bar/a.py @@ -1,4 +1,3 @@ -# name:afunc def afunc(): print("afunc called") return 1 diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo_explicit/bar/a.py b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo_explicit/bar/a.py index 616c3fddca1..c84d63cfce2 100644 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo_explicit/bar/a.py +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo_explicit/bar/a.py @@ -1,4 +1,3 @@ -# name:explicit_afunc def explicit_afunc(): print("explicit_afunc called") return 1 From 03a479fd60097e7559bf2169fff47b0d7af9bfd4 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Fri, 11 Nov 2022 10:47:03 +0100 Subject: [PATCH 0183/1420] use `find` in the format check to fix it --- .github/workflows/compile-queries.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/compile-queries.yml b/.github/workflows/compile-queries.yml index 149cb052de9..bdcf0ba8d8a 100644 --- a/.github/workflows/compile-queries.yml +++ b/.github/workflows/compile-queries.yml @@ -46,7 +46,7 @@ jobs: with: channel: 'release' - name: check formatting - run: codeql query format */ql/**/*.{qll,ql} --check-only + run: find */ql -type f \( -name "*.qll" -o -name "*.ql" \) -print0 | xargs -0 codeql query format --check-only - name: compile queries - check-only # run with --check-only if running in a PR (github.sha != main) if : ${{ github.event_name == 'pull_request' }} From 887062d339cb7a367699aef4923ca3873665b2bb Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Thu, 10 Nov 2022 14:22:10 +0100 Subject: [PATCH 0184/1420] update cs/assembly-path-injection and cs/hardcoded-key to path-problems --- .../CWE-114/AssemblyPathInjection.ql | 10 ++-- .../CWE-321/HardcodedEncryptionKey.ql | 15 ++++-- .../AssemblyPathInjection.expected | 12 ++++- .../HardcodedSymmetricEncryptionKey.expected | 47 ++++++++++++++++--- 4 files changed, 68 insertions(+), 16 deletions(-) diff --git a/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjection.ql b/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjection.ql index 3ca894db15a..3705ece0e72 100644 --- a/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjection.ql +++ b/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjection.ql @@ -3,7 +3,7 @@ * @description Loading a .NET assembly based on a path constructed from user-controlled sources * may allow a malicious user to load code which modifies the program in unintended * ways. - * @kind problem + * @kind path-problem * @id cs/assembly-path-injection * @problem.severity error * @security-severity 8.2 @@ -15,6 +15,7 @@ import csharp import semmle.code.csharp.security.dataflow.flowsources.Remote import semmle.code.csharp.commons.Util +import DataFlow::PathGraph /** * A taint-tracking configuration for untrusted user input used to load a DLL. @@ -47,6 +48,7 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration { } } -from TaintTrackingConfiguration c, DataFlow::Node source, DataFlow::Node sink -where c.hasFlow(source, sink) -select sink, "This assembly path depends on a $@.", source, "user-provided value" +from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink +where c.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "This assembly path depends on a $@.", source, + "user-provided value" diff --git a/csharp/ql/src/Security Features/CWE-321/HardcodedEncryptionKey.ql b/csharp/ql/src/Security Features/CWE-321/HardcodedEncryptionKey.ql index 8f30847cde0..8e516f44d4a 100644 --- a/csharp/ql/src/Security Features/CWE-321/HardcodedEncryptionKey.ql +++ b/csharp/ql/src/Security Features/CWE-321/HardcodedEncryptionKey.ql @@ -1,7 +1,7 @@ /** * @name Hard-coded encryption key * @description The .Key property or rgbKey parameter of a SymmetricAlgorithm should never be a hard-coded value. - * @kind problem + * @kind path-problem * @id cs/hardcoded-key * @problem.severity error * @security-severity 8.1 @@ -15,6 +15,7 @@ import csharp import semmle.code.csharp.security.cryptography.EncryptionKeyDataFlowQuery +import DataFlow::PathGraph /** * The creation of a literal byte array. @@ -36,7 +37,13 @@ class StringLiteralSource extends KeySource { StringLiteralSource() { this.asExpr() instanceof StringLiteral } } -from SymmetricKeyTaintTrackingConfiguration keyFlow, KeySource src, SymmetricEncryptionKeySink sink -where keyFlow.hasFlow(src, sink) -select sink, "This hard-coded $@ is used in symmetric algorithm in " + sink.getDescription(), src, +from + SymmetricKeyTaintTrackingConfiguration keyFlow, DataFlow::PathNode source, + DataFlow::PathNode sink, KeySource srcNode, SymmetricEncryptionKeySink sinkNode +where + keyFlow.hasFlowPath(source, sink) and + source.getNode() = srcNode and + sink.getNode() = sinkNode +select sink.getNode(), source, sink, + "This hard-coded $@ is used in symmetric algorithm in " + sinkNode.getDescription(), srcNode, "symmetric key" diff --git a/csharp/ql/test/query-tests/Security Features/CWE-114/AssemblyPathInjection/AssemblyPathInjection.expected b/csharp/ql/test/query-tests/Security Features/CWE-114/AssemblyPathInjection/AssemblyPathInjection.expected index 2f3c3bbb4a6..396d766c3e6 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-114/AssemblyPathInjection/AssemblyPathInjection.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-114/AssemblyPathInjection/AssemblyPathInjection.expected @@ -1 +1,11 @@ -| Test.cs:10:36:10:46 | access to local variable libraryName | This assembly path depends on a $@. | Test.cs:7:26:7:48 | access to property QueryString | user-provided value | +edges +| Test.cs:7:26:7:48 | access to property QueryString : NameValueCollection | Test.cs:7:26:7:63 | access to indexer : String | +| Test.cs:7:26:7:48 | access to property QueryString : NameValueCollection | Test.cs:10:36:10:46 | access to local variable libraryName | +| Test.cs:7:26:7:63 | access to indexer : String | Test.cs:10:36:10:46 | access to local variable libraryName | +nodes +| Test.cs:7:26:7:48 | access to property QueryString : NameValueCollection | semmle.label | access to property QueryString : NameValueCollection | +| Test.cs:7:26:7:63 | access to indexer : String | semmle.label | access to indexer : String | +| Test.cs:10:36:10:46 | access to local variable libraryName | semmle.label | access to local variable libraryName | +subpaths +#select +| Test.cs:10:36:10:46 | access to local variable libraryName | Test.cs:7:26:7:48 | access to property QueryString : NameValueCollection | Test.cs:10:36:10:46 | access to local variable libraryName | This assembly path depends on a $@. | Test.cs:7:26:7:48 | access to property QueryString : NameValueCollection | user-provided value | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected index 7c3a2a7339c..10cb82ff67e 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected @@ -1,7 +1,40 @@ -| HardcodedSymmetricEncryptionKey.cs:17:21:17:97 | array creation of type Byte[] | This hard-coded $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:17:21:17:97 | array creation of type Byte[] | symmetric key | -| HardcodedSymmetricEncryptionKey.cs:22:23:22:99 | array creation of type Byte[] | This hard-coded $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:22:23:22:99 | array creation of type Byte[] | symmetric key | -| HardcodedSymmetricEncryptionKey.cs:31:21:31:21 | access to local variable d | This hard-coded $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | symmetric key | -| HardcodedSymmetricEncryptionKey.cs:68:87:68:94 | access to parameter password | This hard-coded $@ is used in symmetric algorithm in Decryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | symmetric key | -| HardcodedSymmetricEncryptionKey.cs:108:23:108:25 | access to parameter key | This hard-coded $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | symmetric key | -| HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | This hard-coded $@ is used in symmetric algorithm in Encryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | symmetric key | -| HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | This hard-coded $@ is used in symmetric algorithm in Encryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:28:62:28:115 | "Hello, world: here is a very bad way to create a key" | symmetric key | +edges +| HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] : Byte[] | HardcodedSymmetricEncryptionKey.cs:31:21:31:21 | access to local variable d | +| HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] : Byte[] | HardcodedSymmetricEncryptionKey.cs:36:37:36:37 | access to local variable d : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] : Byte[] | HardcodedSymmetricEncryptionKey.cs:41:50:41:50 | access to local variable c : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] : Byte[] | HardcodedSymmetricEncryptionKey.cs:50:35:50:35 | access to local variable c : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:28:39:28:116 | call to method GetBytes : Byte[] | HardcodedSymmetricEncryptionKey.cs:44:51:44:69 | access to local variable byteArrayFromString : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:28:62:28:115 | "Hello, world: here is a very bad way to create a key" : String | HardcodedSymmetricEncryptionKey.cs:28:39:28:116 | call to method GetBytes : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:36:37:36:37 | access to local variable d : Byte[] | HardcodedSymmetricEncryptionKey.cs:103:57:103:59 | key : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:41:50:41:50 | access to local variable c : Byte[] | HardcodedSymmetricEncryptionKey.cs:112:63:112:65 | key : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:44:51:44:69 | access to local variable byteArrayFromString : Byte[] | HardcodedSymmetricEncryptionKey.cs:112:63:112:65 | key : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:50:35:50:35 | access to local variable c : Byte[] | HardcodedSymmetricEncryptionKey.cs:59:64:59:71 | password : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:59:64:59:71 | password : Byte[] | HardcodedSymmetricEncryptionKey.cs:68:87:68:94 | access to parameter password | +| HardcodedSymmetricEncryptionKey.cs:103:57:103:59 | key : Byte[] | HardcodedSymmetricEncryptionKey.cs:108:23:108:25 | access to parameter key | +| HardcodedSymmetricEncryptionKey.cs:112:63:112:65 | key : Byte[] | HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | +nodes +| HardcodedSymmetricEncryptionKey.cs:17:21:17:97 | array creation of type Byte[] | semmle.label | array creation of type Byte[] | +| HardcodedSymmetricEncryptionKey.cs:22:23:22:99 | array creation of type Byte[] | semmle.label | array creation of type Byte[] | +| HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] : Byte[] | semmle.label | array creation of type Byte[] : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:28:39:28:116 | call to method GetBytes : Byte[] | semmle.label | call to method GetBytes : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:28:62:28:115 | "Hello, world: here is a very bad way to create a key" : String | semmle.label | "Hello, world: here is a very bad way to create a key" : String | +| HardcodedSymmetricEncryptionKey.cs:31:21:31:21 | access to local variable d | semmle.label | access to local variable d | +| HardcodedSymmetricEncryptionKey.cs:36:37:36:37 | access to local variable d : Byte[] | semmle.label | access to local variable d : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:41:50:41:50 | access to local variable c : Byte[] | semmle.label | access to local variable c : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:44:51:44:69 | access to local variable byteArrayFromString : Byte[] | semmle.label | access to local variable byteArrayFromString : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:50:35:50:35 | access to local variable c : Byte[] | semmle.label | access to local variable c : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:59:64:59:71 | password : Byte[] | semmle.label | password : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:68:87:68:94 | access to parameter password | semmle.label | access to parameter password | +| HardcodedSymmetricEncryptionKey.cs:103:57:103:59 | key : Byte[] | semmle.label | key : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:108:23:108:25 | access to parameter key | semmle.label | access to parameter key | +| HardcodedSymmetricEncryptionKey.cs:112:63:112:65 | key : Byte[] | semmle.label | key : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | semmle.label | access to parameter key | +subpaths +#select +| HardcodedSymmetricEncryptionKey.cs:17:21:17:97 | array creation of type Byte[] | HardcodedSymmetricEncryptionKey.cs:17:21:17:97 | array creation of type Byte[] | HardcodedSymmetricEncryptionKey.cs:17:21:17:97 | array creation of type Byte[] | This hard-coded $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:17:21:17:97 | array creation of type Byte[] | symmetric key | +| HardcodedSymmetricEncryptionKey.cs:22:23:22:99 | array creation of type Byte[] | HardcodedSymmetricEncryptionKey.cs:22:23:22:99 | array creation of type Byte[] | HardcodedSymmetricEncryptionKey.cs:22:23:22:99 | array creation of type Byte[] | This hard-coded $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:22:23:22:99 | array creation of type Byte[] | symmetric key | +| HardcodedSymmetricEncryptionKey.cs:31:21:31:21 | access to local variable d | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] : Byte[] | HardcodedSymmetricEncryptionKey.cs:31:21:31:21 | access to local variable d | This hard-coded $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | symmetric key | +| HardcodedSymmetricEncryptionKey.cs:68:87:68:94 | access to parameter password | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] : Byte[] | HardcodedSymmetricEncryptionKey.cs:68:87:68:94 | access to parameter password | This hard-coded $@ is used in symmetric algorithm in Decryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | symmetric key | +| HardcodedSymmetricEncryptionKey.cs:108:23:108:25 | access to parameter key | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] : Byte[] | HardcodedSymmetricEncryptionKey.cs:108:23:108:25 | access to parameter key | This hard-coded $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | symmetric key | +| HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] : Byte[] | HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | This hard-coded $@ is used in symmetric algorithm in Encryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | symmetric key | +| HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | HardcodedSymmetricEncryptionKey.cs:28:62:28:115 | "Hello, world: here is a very bad way to create a key" : String | HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | This hard-coded $@ is used in symmetric algorithm in Encryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:28:62:28:115 | "Hello, world: here is a very bad way to create a key" | symmetric key | From 8147d2048e4821991886b17b6aa95f808ac55560 Mon Sep 17 00:00:00 2001 From: Bas van Schaik <5082246+sj@users.noreply.github.com> Date: Fri, 11 Nov 2022 10:36:26 +0000 Subject: [PATCH 0185/1420] Remove issue template for LGTM.com false positive reports --- .../lgtm-com---false-positive.md | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/lgtm-com---false-positive.md diff --git a/.github/ISSUE_TEMPLATE/lgtm-com---false-positive.md b/.github/ISSUE_TEMPLATE/lgtm-com---false-positive.md deleted file mode 100644 index 94a84906f24..00000000000 --- a/.github/ISSUE_TEMPLATE/lgtm-com---false-positive.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -name: LGTM.com - false positive -about: Tell us about an alert that shouldn't be reported -title: LGTM.com - false positive -labels: false-positive -assignees: '' - ---- - -**Description of the false positive** - - - -**URL to the alert on the project page on LGTM.com** - - From 85233b3cbf2d6184e93c664b6fff5bc2df24ae94 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 11 Nov 2022 11:57:18 +0100 Subject: [PATCH 0186/1420] Swift: add new children to `VarDecl` --- .../codeql/swift/generated/ParentChild.qll | 41 ++- swift/ql/lib/codeql/swift/generated/Raw.qll | 22 ++ .../codeql/swift/generated/decl/ParamDecl.qll | 61 ++++ .../codeql/swift/generated/decl/VarDecl.qll | 117 +++++++ swift/ql/lib/swift.dbscheme | 36 +++ ...eteVarDecl_getPropertyWrapperBackingVar.ql | 7 + ...ecl_getPropertyWrapperBackingVarBinding.ql | 7 + ...VarDecl_getPropertyWrapperProjectionVar.ql | 7 + ..._getPropertyWrapperProjectionVarBinding.ql | 7 + .../ParamDecl_getPropertyWrapperBackingVar.ql | 7 + ...ecl_getPropertyWrapperBackingVarBinding.ql | 7 + ...mDecl_getPropertyWrapperLocalWrappedVar.ql | 7 + ...etPropertyWrapperLocalWrappedVarBinding.ql | 7 + ...ramDecl_getPropertyWrapperProjectionVar.ql | 7 + ..._getPropertyWrapperProjectionVarBinding.ql | 7 + swift/schema.py | 305 ++++++++++++++++++ 16 files changed, 650 insertions(+), 2 deletions(-) create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperBackingVar.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperBackingVarBinding.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperProjectionVar.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperProjectionVarBinding.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperBackingVar.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperBackingVarBinding.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperLocalWrappedVar.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperLocalWrappedVarBinding.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperProjectionVar.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperProjectionVarBinding.ql diff --git a/swift/ql/lib/codeql/swift/generated/ParentChild.qll b/swift/ql/lib/codeql/swift/generated/ParentChild.qll index b56253575e2..d081978c64a 100644 --- a/swift/ql/lib/codeql/swift/generated/ParentChild.qll +++ b/swift/ql/lib/codeql/swift/generated/ParentChild.qll @@ -680,15 +680,39 @@ private module Impl { } private Element getImmediateChildOfVarDecl(VarDecl e, int index, string partialPredicateCall) { - exists(int b, int bAbstractStorageDecl, int n | + exists( + int b, int bAbstractStorageDecl, int n, int nPropertyWrapperBackingVarBinding, + int nPropertyWrapperBackingVar, int nPropertyWrapperProjectionVarBinding, + int nPropertyWrapperProjectionVar + | b = 0 and bAbstractStorageDecl = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfAbstractStorageDecl(e, i, _)) | i) and n = bAbstractStorageDecl and + nPropertyWrapperBackingVarBinding = n + 1 and + nPropertyWrapperBackingVar = nPropertyWrapperBackingVarBinding + 1 and + nPropertyWrapperProjectionVarBinding = nPropertyWrapperBackingVar + 1 and + nPropertyWrapperProjectionVar = nPropertyWrapperProjectionVarBinding + 1 and ( none() or result = getImmediateChildOfAbstractStorageDecl(e, index - b, partialPredicateCall) + or + index = n and + result = e.getImmediatePropertyWrapperBackingVarBinding() and + partialPredicateCall = "PropertyWrapperBackingVarBinding()" + or + index = nPropertyWrapperBackingVarBinding and + result = e.getImmediatePropertyWrapperBackingVar() and + partialPredicateCall = "PropertyWrapperBackingVar()" + or + index = nPropertyWrapperBackingVar and + result = e.getImmediatePropertyWrapperProjectionVarBinding() and + partialPredicateCall = "PropertyWrapperProjectionVarBinding()" + or + index = nPropertyWrapperProjectionVarBinding and + result = e.getImmediatePropertyWrapperProjectionVar() and + partialPredicateCall = "PropertyWrapperProjectionVar()" ) ) } @@ -809,14 +833,27 @@ private module Impl { } private Element getImmediateChildOfParamDecl(ParamDecl e, int index, string partialPredicateCall) { - exists(int b, int bVarDecl, int n | + exists( + int b, int bVarDecl, int n, int nPropertyWrapperLocalWrappedVarBinding, + int nPropertyWrapperLocalWrappedVar + | b = 0 and bVarDecl = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfVarDecl(e, i, _)) | i) and n = bVarDecl and + nPropertyWrapperLocalWrappedVarBinding = n + 1 and + nPropertyWrapperLocalWrappedVar = nPropertyWrapperLocalWrappedVarBinding + 1 and ( none() or result = getImmediateChildOfVarDecl(e, index - b, partialPredicateCall) + or + index = n and + result = e.getImmediatePropertyWrapperLocalWrappedVarBinding() and + partialPredicateCall = "PropertyWrapperLocalWrappedVarBinding()" + or + index = nPropertyWrapperLocalWrappedVarBinding and + result = e.getImmediatePropertyWrapperLocalWrappedVar() and + partialPredicateCall = "PropertyWrapperLocalWrappedVar()" ) ) } diff --git a/swift/ql/lib/codeql/swift/generated/Raw.qll b/swift/ql/lib/codeql/swift/generated/Raw.qll index 2730e58e09b..ab4b7c5efd8 100644 --- a/swift/ql/lib/codeql/swift/generated/Raw.qll +++ b/swift/ql/lib/codeql/swift/generated/Raw.qll @@ -235,6 +235,20 @@ module Raw { Pattern getParentPattern() { var_decl_parent_patterns(this, result) } Expr getParentInitializer() { var_decl_parent_initializers(this, result) } + + PatternBindingDecl getPropertyWrapperBackingVarBinding() { + var_decl_property_wrapper_backing_var_bindings(this, result) + } + + VarDecl getPropertyWrapperBackingVar() { var_decl_property_wrapper_backing_vars(this, result) } + + PatternBindingDecl getPropertyWrapperProjectionVarBinding() { + var_decl_property_wrapper_projection_var_bindings(this, result) + } + + VarDecl getPropertyWrapperProjectionVar() { + var_decl_property_wrapper_projection_vars(this, result) + } } class AccessorDecl extends @accessor_decl, FuncDecl { @@ -285,6 +299,14 @@ module Raw { override string toString() { result = "ParamDecl" } predicate isInout() { param_decl_is_inout(this) } + + PatternBindingDecl getPropertyWrapperLocalWrappedVarBinding() { + param_decl_property_wrapper_local_wrapped_var_bindings(this, result) + } + + VarDecl getPropertyWrapperLocalWrappedVar() { + param_decl_property_wrapper_local_wrapped_vars(this, result) + } } class TypeAliasDecl extends @type_alias_decl, GenericTypeDecl { diff --git a/swift/ql/lib/codeql/swift/generated/decl/ParamDecl.qll b/swift/ql/lib/codeql/swift/generated/decl/ParamDecl.qll index 727f169234d..1c153a5e0b6 100644 --- a/swift/ql/lib/codeql/swift/generated/decl/ParamDecl.qll +++ b/swift/ql/lib/codeql/swift/generated/decl/ParamDecl.qll @@ -1,6 +1,7 @@ // generated by codegen/codegen.py private import codeql.swift.generated.Synth private import codeql.swift.generated.Raw +import codeql.swift.elements.decl.PatternBindingDecl import codeql.swift.elements.decl.VarDecl module Generated { @@ -11,5 +12,65 @@ module Generated { * Holds if this is an `inout` parameter. */ predicate isInout() { Synth::convertParamDeclToRaw(this).(Raw::ParamDecl).isInout() } + + /** + * Gets the property wrapper local wrapped var binding of this parameter declaration, if it exists. + * + * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the + * behavior of both the `Immediate` and non-`Immediate` versions. + */ + PatternBindingDecl getImmediatePropertyWrapperLocalWrappedVarBinding() { + result = + Synth::convertPatternBindingDeclFromRaw(Synth::convertParamDeclToRaw(this) + .(Raw::ParamDecl) + .getPropertyWrapperLocalWrappedVarBinding()) + } + + /** + * Gets the property wrapper local wrapped var binding of this parameter declaration, if it exists. + * + * This is the synthesized binding introducing the property wrapper local wrapped projection + * variable for this variable, if any. + */ + final PatternBindingDecl getPropertyWrapperLocalWrappedVarBinding() { + result = getImmediatePropertyWrapperLocalWrappedVarBinding().resolve() + } + + /** + * Holds if `getPropertyWrapperLocalWrappedVarBinding()` exists. + */ + final predicate hasPropertyWrapperLocalWrappedVarBinding() { + exists(getPropertyWrapperLocalWrappedVarBinding()) + } + + /** + * Gets the property wrapper local wrapped var of this parameter declaration, if it exists. + * + * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the + * behavior of both the `Immediate` and non-`Immediate` versions. + */ + VarDecl getImmediatePropertyWrapperLocalWrappedVar() { + result = + Synth::convertVarDeclFromRaw(Synth::convertParamDeclToRaw(this) + .(Raw::ParamDecl) + .getPropertyWrapperLocalWrappedVar()) + } + + /** + * Gets the property wrapper local wrapped var of this parameter declaration, if it exists. + * + * This is the synthesized local wrapped value, shadowing this parameter declaration in case it + * has a property wrapper. + */ + final VarDecl getPropertyWrapperLocalWrappedVar() { + result = getImmediatePropertyWrapperLocalWrappedVar().resolve() + } + + /** + * Holds if `getPropertyWrapperLocalWrappedVar()` exists. + */ + final predicate hasPropertyWrapperLocalWrappedVar() { + exists(getPropertyWrapperLocalWrappedVar()) + } } } diff --git a/swift/ql/lib/codeql/swift/generated/decl/VarDecl.qll b/swift/ql/lib/codeql/swift/generated/decl/VarDecl.qll index f92e0bdb768..f4b414ea1bd 100644 --- a/swift/ql/lib/codeql/swift/generated/decl/VarDecl.qll +++ b/swift/ql/lib/codeql/swift/generated/decl/VarDecl.qll @@ -4,6 +4,7 @@ private import codeql.swift.generated.Raw import codeql.swift.elements.decl.AbstractStorageDecl import codeql.swift.elements.expr.Expr import codeql.swift.elements.pattern.Pattern +import codeql.swift.elements.decl.PatternBindingDecl import codeql.swift.elements.type.Type module Generated { @@ -98,5 +99,121 @@ module Generated { * Holds if `getParentInitializer()` exists. */ final predicate hasParentInitializer() { exists(getParentInitializer()) } + + /** + * Gets the property wrapper backing var binding of this var declaration, if it exists. + * + * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the + * behavior of both the `Immediate` and non-`Immediate` versions. + */ + PatternBindingDecl getImmediatePropertyWrapperBackingVarBinding() { + result = + Synth::convertPatternBindingDeclFromRaw(Synth::convertVarDeclToRaw(this) + .(Raw::VarDecl) + .getPropertyWrapperBackingVarBinding()) + } + + /** + * Gets the property wrapper backing var binding of this var declaration, if it exists. + * + * This is the synthesized binding introducing the property wrapper backing variable for this + * variable, if any. + */ + final PatternBindingDecl getPropertyWrapperBackingVarBinding() { + result = getImmediatePropertyWrapperBackingVarBinding().resolve() + } + + /** + * Holds if `getPropertyWrapperBackingVarBinding()` exists. + */ + final predicate hasPropertyWrapperBackingVarBinding() { + exists(getPropertyWrapperBackingVarBinding()) + } + + /** + * Gets the property wrapper backing var of this var declaration, if it exists. + * + * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the + * behavior of both the `Immediate` and non-`Immediate` versions. + */ + VarDecl getImmediatePropertyWrapperBackingVar() { + result = + Synth::convertVarDeclFromRaw(Synth::convertVarDeclToRaw(this) + .(Raw::VarDecl) + .getPropertyWrapperBackingVar()) + } + + /** + * Gets the property wrapper backing var of this var declaration, if it exists. + * + * This is the synthesized variable holding the property wrapper for this variable, if any. + */ + final VarDecl getPropertyWrapperBackingVar() { + result = getImmediatePropertyWrapperBackingVar().resolve() + } + + /** + * Holds if `getPropertyWrapperBackingVar()` exists. + */ + final predicate hasPropertyWrapperBackingVar() { exists(getPropertyWrapperBackingVar()) } + + /** + * Gets the property wrapper projection var binding of this var declaration, if it exists. + * + * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the + * behavior of both the `Immediate` and non-`Immediate` versions. + */ + PatternBindingDecl getImmediatePropertyWrapperProjectionVarBinding() { + result = + Synth::convertPatternBindingDeclFromRaw(Synth::convertVarDeclToRaw(this) + .(Raw::VarDecl) + .getPropertyWrapperProjectionVarBinding()) + } + + /** + * Gets the property wrapper projection var binding of this var declaration, if it exists. + * + * This is the synthesized binding introducing the property wrapper projection variable for this + * variable, if any. + */ + final PatternBindingDecl getPropertyWrapperProjectionVarBinding() { + result = getImmediatePropertyWrapperProjectionVarBinding().resolve() + } + + /** + * Holds if `getPropertyWrapperProjectionVarBinding()` exists. + */ + final predicate hasPropertyWrapperProjectionVarBinding() { + exists(getPropertyWrapperProjectionVarBinding()) + } + + /** + * Gets the property wrapper projection var of this var declaration, if it exists. + * + * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the + * behavior of both the `Immediate` and non-`Immediate` versions. + */ + VarDecl getImmediatePropertyWrapperProjectionVar() { + result = + Synth::convertVarDeclFromRaw(Synth::convertVarDeclToRaw(this) + .(Raw::VarDecl) + .getPropertyWrapperProjectionVar()) + } + + /** + * Gets the property wrapper projection var of this var declaration, if it exists. + * + * If this variable has a property wrapper with a projected value, this is the corresponding + * synthesized variable holding that projected value, accessible with this variable's name + * prefixed with `$`. + */ + final VarDecl getPropertyWrapperProjectionVar() { + result = getImmediatePropertyWrapperProjectionVar().resolve() + } + + /** + * Holds if `getPropertyWrapperProjectionVar()` exists. + */ + final predicate hasPropertyWrapperProjectionVar() { exists(getPropertyWrapperProjectionVar()) } } } diff --git a/swift/ql/lib/swift.dbscheme b/swift/ql/lib/swift.dbscheme index 594381ec16e..510cc5ca65c 100644 --- a/swift/ql/lib/swift.dbscheme +++ b/swift/ql/lib/swift.dbscheme @@ -483,6 +483,30 @@ var_decl_parent_initializers( //dir=decl int parent_initializer: @expr_or_none ref ); +#keyset[id] +var_decl_property_wrapper_backing_var_bindings( //dir=decl + int id: @var_decl ref, + int property_wrapper_backing_var_binding: @pattern_binding_decl_or_none ref +); + +#keyset[id] +var_decl_property_wrapper_backing_vars( //dir=decl + int id: @var_decl ref, + int property_wrapper_backing_var: @var_decl_or_none ref +); + +#keyset[id] +var_decl_property_wrapper_projection_var_bindings( //dir=decl + int id: @var_decl ref, + int property_wrapper_projection_var_binding: @pattern_binding_decl_or_none ref +); + +#keyset[id] +var_decl_property_wrapper_projection_vars( //dir=decl + int id: @var_decl ref, + int property_wrapper_projection_var: @var_decl_or_none ref +); + accessor_decls( //dir=decl unique int id: @accessor_decl ); @@ -558,6 +582,18 @@ param_decl_is_inout( //dir=decl int id: @param_decl ref ); +#keyset[id] +param_decl_property_wrapper_local_wrapped_var_bindings( //dir=decl + int id: @param_decl ref, + int property_wrapper_local_wrapped_var_binding: @pattern_binding_decl_or_none ref +); + +#keyset[id] +param_decl_property_wrapper_local_wrapped_vars( //dir=decl + int id: @param_decl ref, + int property_wrapper_local_wrapped_var: @var_decl_or_none ref +); + type_alias_decls( //dir=decl unique int id: @type_alias_decl ); diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperBackingVar.ql b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperBackingVar.ql new file mode 100644 index 00000000000..a70361143c6 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperBackingVar.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ConcreteVarDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getPropertyWrapperBackingVar() diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperBackingVarBinding.ql b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperBackingVarBinding.ql new file mode 100644 index 00000000000..dea82691f5d --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperBackingVarBinding.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ConcreteVarDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getPropertyWrapperBackingVarBinding() diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperProjectionVar.ql b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperProjectionVar.ql new file mode 100644 index 00000000000..3855735ab02 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperProjectionVar.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ConcreteVarDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getPropertyWrapperProjectionVar() diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperProjectionVarBinding.ql b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperProjectionVarBinding.ql new file mode 100644 index 00000000000..836726bc36a --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperProjectionVarBinding.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ConcreteVarDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getPropertyWrapperProjectionVarBinding() diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperBackingVar.ql b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperBackingVar.ql new file mode 100644 index 00000000000..91be55e732a --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperBackingVar.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ParamDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getPropertyWrapperBackingVar() diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperBackingVarBinding.ql b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperBackingVarBinding.ql new file mode 100644 index 00000000000..4a331c68504 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperBackingVarBinding.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ParamDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getPropertyWrapperBackingVarBinding() diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperLocalWrappedVar.ql b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperLocalWrappedVar.ql new file mode 100644 index 00000000000..2c824acc88e --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperLocalWrappedVar.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ParamDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getPropertyWrapperLocalWrappedVar() diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperLocalWrappedVarBinding.ql b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperLocalWrappedVarBinding.ql new file mode 100644 index 00000000000..018c0cbf18a --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperLocalWrappedVarBinding.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ParamDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getPropertyWrapperLocalWrappedVarBinding() diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperProjectionVar.ql b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperProjectionVar.ql new file mode 100644 index 00000000000..03c2584c693 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperProjectionVar.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ParamDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getPropertyWrapperProjectionVar() diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperProjectionVarBinding.ql b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperProjectionVarBinding.ql new file mode 100644 index 00000000000..090122697ab --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperProjectionVarBinding.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ParamDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getPropertyWrapperProjectionVarBinding() diff --git a/swift/schema.py b/swift/schema.py index 97c8cd143e9..665058cbb02 100644 --- a/swift/schema.py +++ b/swift/schema.py @@ -11,14 +11,17 @@ from swift.codegen.lib.schema.defs import * include("prefix.dbscheme") + @qltest.skip class Element: is_unknown: predicate | cpp.skip + @qltest.collapse_hierarchy class File(Element): name: string + @qltest.skip @qltest.collapse_hierarchy class Location(Element): @@ -28,14 +31,17 @@ class Location(Element): end_line: int end_column: int + @qltest.skip class Locatable(Element): location: optional[Location] | cpp.skip | doc("location associated with this element in the code") + @qltest.collapse_hierarchy class UnresolvedElement(Locatable): pass + @use_for_null class UnspecifiedElement(Locatable): parent: optional[Element] @@ -43,187 +49,260 @@ class UnspecifiedElement(Locatable): index: optional[int] error: string + class Comment(Locatable): text: string + class Diagnostics(Locatable): text: string kind: int + class DbFile(File): pass + class DbLocation(Location): pass + @synth.on_arguments() class UnknownFile(File): pass + @synth.on_arguments() class UnknownLocation(Location): pass + class AstNode(Locatable): pass + @group("type") class Type(Element): name: string canonical_type: "Type" + @group("decl") class Decl(AstNode): module: "ModuleDecl" + @group("expr") class Expr(AstNode): """The base class for all expressions in Swift.""" type: optional[Type] + @group("pattern") class Pattern(AstNode): pass + @group("stmt") class Stmt(AstNode): pass + @group("decl") class GenericContext(Element): generic_type_params: list["GenericTypeParamDecl"] | child + @group("decl") class IterableDeclContext(Element): members: list[Decl] | child + class EnumCaseDecl(Decl): elements: list["EnumElementDecl"] + class ExtensionDecl(GenericContext, IterableDeclContext, Decl): extended_type_decl: "NominalTypeDecl" + class IfConfigDecl(Decl): active_elements: list[AstNode] + class ImportDecl(Decl): is_exported: predicate imported_module: optional["ModuleDecl"] declarations: list["ValueDecl"] + @qltest.skip class MissingMemberDecl(Decl): """A placeholder for missing declarations that can arise on object deserialization.""" name: string + class OperatorDecl(Decl): name: string + class PatternBindingDecl(Decl): inits: list[optional[Expr]] | child patterns: list[Pattern] | child + class PoundDiagnosticDecl(Decl): """ A diagnostic directive, which is either `#error` or `#warning`.""" kind: int | doc("""This is 1 for `#error` and 2 for `#warning`""") message: "StringLiteralExpr" | child + class PrecedenceGroupDecl(Decl): pass + class TopLevelCodeDecl(Decl): body: "BraceStmt" | child + class ValueDecl(Decl): interface_type: Type + class AbstractStorageDecl(ValueDecl): accessor_decls: list["AccessorDecl"] | child + class VarDecl(AbstractStorageDecl): name: string type: Type attached_property_wrapper_type: optional[Type] parent_pattern: optional[Pattern] parent_initializer: optional[Expr] + property_wrapper_backing_var_binding: optional[PatternBindingDecl] | child | desc(""" + This is the synthesized binding introducing the property wrapper backing variable for this + variable, if any. + """) + property_wrapper_backing_var: optional["VarDecl"] | child | desc(""" + This is the synthesized variable holding the property wrapper for this variable, if any. + """) + property_wrapper_projection_var_binding: optional[PatternBindingDecl] | child | desc(""" + This is the synthesized binding introducing the property wrapper projection variable for this + variable, if any. + """) + property_wrapper_projection_var: optional["VarDecl"] | child | desc(""" + If this variable has a property wrapper with a projected value, this is the corresponding + synthesized variable holding that projected value, accessible with this variable's name + prefixed with `$`. + """) + class ParamDecl(VarDecl): is_inout: predicate | doc("this is an `inout` parameter") + property_wrapper_local_wrapped_var_binding: optional[PatternBindingDecl] | child | desc(""" + This is the synthesized binding introducing the property wrapper local wrapped projection + variable for this variable, if any. + """) + property_wrapper_local_wrapped_var: optional["VarDecl"] | child | desc(""" + This is the synthesized local wrapped value, shadowing this parameter declaration in case it + has a property wrapper. + """) + class Callable(Element): self_param: optional[ParamDecl] | child params: list[ParamDecl] | child body: optional["BraceStmt"] | child | desc("The body is absent within protocol declarations.") + class AbstractFunctionDecl(GenericContext, ValueDecl, Callable): name: string | doc("name of this function") + class EnumElementDecl(ValueDecl): name: string params: list[ParamDecl] | child + class InfixOperatorDecl(OperatorDecl): precedence_group: optional[PrecedenceGroupDecl] + class PostfixOperatorDecl(OperatorDecl): pass + class PrefixOperatorDecl(OperatorDecl): pass + class TypeDecl(ValueDecl): name: string base_types: list[Type] + class AbstractTypeParamDecl(TypeDecl): pass + class ConstructorDecl(AbstractFunctionDecl): pass + class DestructorDecl(AbstractFunctionDecl): pass + class FuncDecl(AbstractFunctionDecl): pass + class GenericTypeDecl(GenericContext, TypeDecl): pass + class ModuleDecl(TypeDecl): is_builtin_module: predicate | doc("this module is the built-in one") is_system_module: predicate | doc("this module is a system one") imported_modules: list["ModuleDecl"] exported_modules: list["ModuleDecl"] + class SubscriptDecl(AbstractStorageDecl, GenericContext): params: list[ParamDecl] | child element_type: Type element_type: Type + class AccessorDecl(FuncDecl): is_getter: predicate | doc('this accessor is a getter') is_setter: predicate | doc('this accessor is a setter') is_will_set: predicate | doc('this accessor is a `willSet`, called before the property is set') is_did_set: predicate | doc('this accessor is a `didSet`, called after the property is set') + class AssociatedTypeDecl(AbstractTypeParamDecl): pass + class ConcreteFuncDecl(FuncDecl): pass + class ConcreteVarDecl(VarDecl): introducer_int: int | doc("introducer enumeration value") | desc(""" This is 0 if the variable was introduced with `let` and 1 if it was introduced with `var`. """) + class GenericTypeParamDecl(AbstractTypeParamDecl): pass + class NominalTypeDecl(GenericTypeDecl, IterableDeclContext): type: Type + class OpaqueTypeDecl(GenericTypeDecl): """ A declaration of an opaque type, that is formally equivalent to a given type but abstracts it @@ -240,56 +319,71 @@ class OpaqueTypeDecl(GenericTypeDecl): opaque_generic_params: list["GenericTypeParamType"] opaque_generic_params: list["GenericTypeParamType"] + class TypeAliasDecl(GenericTypeDecl): pass + class ClassDecl(NominalTypeDecl): pass + class EnumDecl(NominalTypeDecl): pass + class ProtocolDecl(NominalTypeDecl): pass + class StructDecl(NominalTypeDecl): pass + @group("expr") class Argument(Locatable): label: string expr: Expr | child + class AbstractClosureExpr(Expr, Callable): pass + class AnyTryExpr(Expr): sub_expr: Expr | child + class AppliedPropertyWrapperExpr(Expr): """An implicit application of a property wrapper on the argument of a call.""" kind: int | desc("This is 1 for a wrapped value and 2 for a projected one.") value: Expr | child | desc("The value on which the wrapper is applied.") param: ParamDecl | doc("parameter declaration owning this wrapper application") + class ApplyExpr(Expr): function: Expr | child | doc("function being applied") arguments: list[Argument] | child | doc("arguments passed to the applied function") + class AssignExpr(Expr): dest: Expr | child source: Expr | child + class BindOptionalExpr(Expr): sub_expr: Expr | child + class CaptureListExpr(Expr): binding_decls: list[PatternBindingDecl] | child closure_body: "ClosureExpr" | child + class CollectionExpr(Expr): pass + class DeclRefExpr(Expr): decl: Decl replacement_types: list[Type] @@ -297,99 +391,125 @@ class DeclRefExpr(Expr): has_direct_to_implementation_semantics: predicate has_ordinary_semantics: predicate + class DefaultArgumentExpr(Expr): param_decl: ParamDecl param_index: int caller_side_default: optional[Expr] + class DiscardAssignmentExpr(Expr): pass + class DotSyntaxBaseIgnoredExpr(Expr): qualifier: Expr | child sub_expr: Expr | child + class DynamicTypeExpr(Expr): base: Expr | child + class EnumIsCaseExpr(Expr): sub_expr: Expr | child element: EnumElementDecl + @qltest.skip class ErrorExpr(Expr): pass + class ExplicitCastExpr(Expr): sub_expr: Expr | child + class ForceValueExpr(Expr): sub_expr: Expr | child + @qltest.collapse_hierarchy class IdentityExpr(Expr): sub_expr: Expr | child + class IfExpr(Expr): condition: Expr | child then_expr: Expr | child else_expr: Expr | child + @qltest.collapse_hierarchy class ImplicitConversionExpr(Expr): sub_expr: Expr | child + class InOutExpr(Expr): sub_expr: Expr | child + class KeyPathApplicationExpr(Expr): base: Expr | child key_path: Expr | child + class KeyPathDotExpr(Expr): pass + class KeyPathExpr(Expr): root: optional["TypeRepr"] | child parsed_path: optional[Expr] | child + class LazyInitializerExpr(Expr): sub_expr: Expr | child + class LiteralExpr(Expr): pass + class LookupExpr(Expr): base: Expr | child member: optional[Decl] + class MakeTemporarilyEscapableExpr(Expr): escaping_closure: "OpaqueValueExpr" | child nonescaping_closure: Expr | child sub_expr: Expr | child + @qltest.skip class ObjCSelectorExpr(Expr): sub_expr: Expr | child method: AbstractFunctionDecl + class OneWayExpr(Expr): sub_expr: Expr | child + class OpaqueValueExpr(Expr): pass + class OpenExistentialExpr(Expr): sub_expr: Expr | child existential: Expr | child opaque_expr: OpaqueValueExpr | child + class OptionalEvaluationExpr(Expr): sub_expr: Expr | child + class OtherConstructorDeclRefExpr(Expr): constructor_decl: ConstructorDecl + class PropertyWrapperValuePlaceholderExpr(Expr): """ A placeholder substituting property initializations with `=` when the property has a property @@ -398,183 +518,238 @@ class PropertyWrapperValuePlaceholderExpr(Expr): wrapped_value: optional[Expr] placeholder: OpaqueValueExpr + class RebindSelfInConstructorExpr(Expr): sub_expr: Expr | child self: VarDecl + @qltest.skip class SequenceExpr(Expr): elements: list[Expr] | child + class SuperRefExpr(Expr): self: VarDecl + class TapExpr(Expr): sub_expr: optional[Expr] | child body: "BraceStmt" | child var: VarDecl + class TupleElementExpr(Expr): sub_expr: Expr | child index: int + class TupleExpr(Expr): elements: list[Expr] | child + class TypeExpr(Expr): type_repr: optional["TypeRepr"] | child + class UnresolvedDeclRefExpr(Expr, UnresolvedElement): name: optional[string] + class UnresolvedDotExpr(Expr, UnresolvedElement): base: Expr | child name: string + class UnresolvedMemberExpr(Expr, UnresolvedElement): name: string + class UnresolvedPatternExpr(Expr, UnresolvedElement): sub_pattern: Pattern | child + class UnresolvedSpecializeExpr(Expr, UnresolvedElement): sub_expr: Expr | child + class VarargExpansionExpr(Expr): sub_expr: Expr | child + class AnyHashableErasureExpr(ImplicitConversionExpr): pass + class ArchetypeToSuperExpr(ImplicitConversionExpr): pass + class ArrayExpr(CollectionExpr): elements: list[Expr] | child + class ArrayToPointerExpr(ImplicitConversionExpr): pass + class AutoClosureExpr(AbstractClosureExpr): pass + class AwaitExpr(IdentityExpr): pass + class BinaryExpr(ApplyExpr): pass + @qltest.skip class BridgeFromObjCExpr(ImplicitConversionExpr): pass + @qltest.skip class BridgeToObjCExpr(ImplicitConversionExpr): pass + class BuiltinLiteralExpr(LiteralExpr): pass + class CallExpr(ApplyExpr): pass + class CheckedCastExpr(ExplicitCastExpr): pass + class ClassMetatypeToObjectExpr(ImplicitConversionExpr): pass + class ClosureExpr(AbstractClosureExpr): pass + class CoerceExpr(ExplicitCastExpr): pass + class CollectionUpcastConversionExpr(ImplicitConversionExpr): pass + @qltest.skip class ConditionalBridgeFromObjCExpr(ImplicitConversionExpr): pass + class CovariantFunctionConversionExpr(ImplicitConversionExpr): pass + class CovariantReturnConversionExpr(ImplicitConversionExpr): pass + class DerivedToBaseExpr(ImplicitConversionExpr): pass + class DestructureTupleExpr(ImplicitConversionExpr): pass + class DictionaryExpr(CollectionExpr): elements: list[Expr] | child + class DifferentiableFunctionExpr(ImplicitConversionExpr): pass + class DifferentiableFunctionExtractOriginalExpr(ImplicitConversionExpr): pass + class DotSelfExpr(IdentityExpr): pass + @qltest.collapse_hierarchy class DynamicLookupExpr(LookupExpr): pass + class ErasureExpr(ImplicitConversionExpr): pass + class ExistentialMetatypeToObjectExpr(ImplicitConversionExpr): pass + class ForceTryExpr(AnyTryExpr): pass + class ForeignObjectConversionExpr(ImplicitConversionExpr): pass + class FunctionConversionExpr(ImplicitConversionExpr): pass + class InOutToPointerExpr(ImplicitConversionExpr): pass + class InjectIntoOptionalExpr(ImplicitConversionExpr): pass + class InterpolatedStringLiteralExpr(LiteralExpr): interpolation_expr: optional[OpaqueValueExpr] interpolation_count_expr: optional[Expr] | child literal_capacity_expr: optional[Expr] | child appending_expr: optional[TapExpr] | child + class LinearFunctionExpr(ImplicitConversionExpr): pass + class LinearFunctionExtractOriginalExpr(ImplicitConversionExpr): pass + class LinearToDifferentiableFunctionExpr(ImplicitConversionExpr): pass + class LoadExpr(ImplicitConversionExpr): pass + class MemberRefExpr(LookupExpr): has_direct_to_storage_semantics: predicate has_direct_to_implementation_semantics: predicate has_ordinary_semantics: predicate + class MetatypeConversionExpr(ImplicitConversionExpr): pass + class NilLiteralExpr(LiteralExpr): pass + class ObjectLiteralExpr(LiteralExpr): """ An instance of `#fileLiteral`, `#imageLiteral` or `#colorLiteral` expressions, which are used in playgrounds. @@ -582,9 +757,11 @@ class ObjectLiteralExpr(LiteralExpr): kind: int | desc("""This is 0 for `#fileLiteral`, 1 for `#imageLiteral` and 2 for `#colorLiteral`.""") arguments: list[Argument] | child + class OptionalTryExpr(AnyTryExpr): pass + class OverloadedDeclRefExpr(Expr): """ An ambiguous expression that might refer to multiple declarations. This will be present only @@ -592,225 +769,290 @@ class OverloadedDeclRefExpr(Expr): """ possible_declarations: list[ValueDecl] + class ParenExpr(IdentityExpr): pass + class PointerToPointerExpr(ImplicitConversionExpr): pass + class PostfixUnaryExpr(ApplyExpr): pass + class PrefixUnaryExpr(ApplyExpr): pass + class ProtocolMetatypeToObjectExpr(ImplicitConversionExpr): pass + class RegexLiteralExpr(LiteralExpr): pass + class SelfApplyExpr(ApplyExpr): base: Expr + class StringToPointerExpr(ImplicitConversionExpr): pass + class SubscriptExpr(LookupExpr): arguments: list[Argument] | child has_direct_to_storage_semantics: predicate has_direct_to_implementation_semantics: predicate has_ordinary_semantics: predicate + class TryExpr(AnyTryExpr): pass + class UnderlyingToOpaqueExpr(ImplicitConversionExpr): pass + class UnevaluatedInstanceExpr(ImplicitConversionExpr): pass + class UnresolvedMemberChainResultExpr(IdentityExpr, UnresolvedElement): pass + class UnresolvedTypeConversionExpr(ImplicitConversionExpr, UnresolvedElement): pass + class BooleanLiteralExpr(BuiltinLiteralExpr): value: boolean + class ConditionalCheckedCastExpr(CheckedCastExpr): pass + class ConstructorRefCallExpr(SelfApplyExpr): pass + class DotSyntaxCallExpr(SelfApplyExpr): pass + @synth.from_class(DotSyntaxCallExpr) class MethodRefExpr(LookupExpr): pass + class DynamicMemberRefExpr(DynamicLookupExpr): pass + class DynamicSubscriptExpr(DynamicLookupExpr): pass + class ForcedCheckedCastExpr(CheckedCastExpr): pass + class IsExpr(CheckedCastExpr): pass + class MagicIdentifierLiteralExpr(BuiltinLiteralExpr): kind: string + class NumberLiteralExpr(BuiltinLiteralExpr): pass + class StringLiteralExpr(BuiltinLiteralExpr): value: string + class FloatLiteralExpr(NumberLiteralExpr): string_value: string + class IntegerLiteralExpr(NumberLiteralExpr): string_value: string + class AnyPattern(Pattern): pass + class BindingPattern(Pattern): sub_pattern: Pattern | child + class BoolPattern(Pattern): value: boolean + class EnumElementPattern(Pattern): element: EnumElementDecl sub_pattern: optional[Pattern] | child + class ExprPattern(Pattern): sub_expr: Expr | child + class IsPattern(Pattern): cast_type_repr: optional["TypeRepr"] | child sub_pattern: optional[Pattern] | child + class NamedPattern(Pattern): name: string + class OptionalSomePattern(Pattern): sub_pattern: Pattern | child + class ParenPattern(Pattern): sub_pattern: Pattern | child + class TuplePattern(Pattern): elements: list[Pattern] | child + class TypedPattern(Pattern): sub_pattern: Pattern | child type_repr: optional["TypeRepr"] | child + @group("stmt") class CaseLabelItem(AstNode): pattern: Pattern | child guard: optional[Expr] | child + @group("stmt") class ConditionElement(AstNode): boolean: optional[Expr] | child pattern: optional[Pattern] | child initializer: optional[Expr] | child + @group("stmt") class StmtCondition(AstNode): elements: list[ConditionElement] | child + class BraceStmt(Stmt): elements: list[AstNode] | child + class BreakStmt(Stmt): target_name: optional[string] target: optional[Stmt] + class CaseStmt(Stmt): body: Stmt | child labels: list[CaseLabelItem] | child variables: list[VarDecl] + class ContinueStmt(Stmt): target_name: optional[string] target: optional[Stmt] + class DeferStmt(Stmt): body: BraceStmt | child + class FailStmt(Stmt): pass + class FallthroughStmt(Stmt): fallthrough_source: CaseStmt fallthrough_dest: CaseStmt + class LabeledStmt(Stmt): label: optional[string] + class PoundAssertStmt(Stmt): condition: Expr message: string + class ReturnStmt(Stmt): result: optional[Expr] | child + class ThrowStmt(Stmt): sub_expr: Expr | child + class YieldStmt(Stmt): results: list[Expr] | child + class DoCatchStmt(LabeledStmt): body: Stmt | child catches: list[CaseStmt] | child + class DoStmt(LabeledStmt): body: BraceStmt | child + class ForEachStmt(LabeledStmt): pattern: Pattern | child sequence: Expr | child where: optional[Expr] | child body: BraceStmt | child + class LabeledConditionalStmt(LabeledStmt): condition: StmtCondition | child + class RepeatWhileStmt(LabeledStmt): condition: Expr | child body: Stmt | child + class SwitchStmt(LabeledStmt): expr: Expr | child cases: list[CaseStmt] | child + class GuardStmt(LabeledConditionalStmt): body: BraceStmt | child + class IfStmt(LabeledConditionalStmt): then: Stmt | child else_: optional[Stmt] | child + class WhileStmt(LabeledConditionalStmt): body: Stmt | child + @group("type") class TypeRepr(AstNode): type: Type + @ql.default_doc_name("function type") class AnyFunctionType(Type): result: Type @@ -819,203 +1061,266 @@ class AnyFunctionType(Type): is_throwing: predicate | doc("this type refers to a throwing function") is_async: predicate | doc("this type refers to an `async` function") + class AnyGenericType(Type): parent: optional[Type] declaration: Decl + class AnyMetatypeType(Type): pass + @qltest.collapse_hierarchy class BuiltinType(Type): pass + class DependentMemberType(Type): base_type: Type associated_type_decl: AssociatedTypeDecl + class DynamicSelfType(Type): static_self_type: Type + class ErrorType(Type): pass + class ExistentialType(Type): constraint: Type + class InOutType(Type): object_type: Type + class LValueType(Type): object_type: Type + class ModuleType(Type): module: ModuleDecl + class ProtocolCompositionType(Type): members: list[Type] + class ReferenceStorageType(Type): referent_type: Type + class SubstitutableType(Type): pass + class SugarType(Type): pass + class TupleType(Type): types: list[Type] names: list[optional[string]] + class TypeVariableType(Type): pass + class UnresolvedType(Type, UnresolvedElement): pass + class AnyBuiltinIntegerType(BuiltinType): pass + class ArchetypeType(SubstitutableType): interface_type: Type superclass: optional[Type] protocols: list[ProtocolDecl] + class BuiltinBridgeObjectType(BuiltinType): pass + class BuiltinDefaultActorStorageType(BuiltinType): pass + class BuiltinExecutorType(BuiltinType): pass + class BuiltinFloatType(BuiltinType): pass + class BuiltinJobType(BuiltinType): pass + class BuiltinNativeObjectType(BuiltinType): pass + class BuiltinRawPointerType(BuiltinType): pass + class BuiltinRawUnsafeContinuationType(BuiltinType): pass + class BuiltinUnsafeValueBufferType(BuiltinType): pass + class BuiltinVectorType(BuiltinType): pass + class ExistentialMetatypeType(AnyMetatypeType): pass + class FunctionType(AnyFunctionType): pass + class GenericFunctionType(AnyFunctionType): """ The type of a generic function with type parameters """ generic_params: list["GenericTypeParamType"] | doc("type {parameters} of this generic type") + class GenericTypeParamType(SubstitutableType): pass + class MetatypeType(AnyMetatypeType): pass + class NominalOrBoundGenericNominalType(AnyGenericType): pass + class ParenType(SugarType): type: Type + class SyntaxSugarType(SugarType): pass + class TypeAliasType(SugarType): decl: TypeAliasDecl + class UnboundGenericType(AnyGenericType): pass + class UnmanagedStorageType(ReferenceStorageType): pass + class UnownedStorageType(ReferenceStorageType): pass + class WeakStorageType(ReferenceStorageType): pass + class BoundGenericType(NominalOrBoundGenericNominalType): arg_types: list[Type] + class BuiltinIntegerLiteralType(AnyBuiltinIntegerType): pass + @qltest.uncollapse_hierarchy class BuiltinIntegerType(AnyBuiltinIntegerType): width: optional[int] + class DictionaryType(SyntaxSugarType): key_type: Type value_type: Type + class NominalType(NominalOrBoundGenericNominalType): pass + class OpaqueTypeArchetypeType(ArchetypeType): """An opaque type, that is a type formally equivalent to an underlying type but abstracting it away. See https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html.""" declaration: OpaqueTypeDecl + class OpenedArchetypeType(ArchetypeType): pass + class PrimaryArchetypeType(ArchetypeType): pass + class SequenceArchetypeType(ArchetypeType): pass + class UnarySyntaxSugarType(SyntaxSugarType): base_type: Type + class ArraySliceType(UnarySyntaxSugarType): pass + class BoundGenericClassType(BoundGenericType): pass + class BoundGenericEnumType(BoundGenericType): pass + class BoundGenericStructType(BoundGenericType): pass + class ClassType(NominalType): pass + class EnumType(NominalType): pass + class OptionalType(UnarySyntaxSugarType): pass + class ProtocolType(NominalType): pass + class StructType(NominalType): pass + class VariadicSequenceType(UnarySyntaxSugarType): pass + class ParameterizedProtocolType(Type): pass From fc98fd30944f0cfabef959fba052f605b1e5b395 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 11 Nov 2022 12:00:12 +0100 Subject: [PATCH 0187/1420] Swift: add `var` to the list of doc expanded abbreviations --- swift/codegen/generators/qlgen.py | 1 + .../codeql/swift/generated/decl/ParamDecl.qll | 8 ++--- .../codeql/swift/generated/decl/VarDecl.qll | 34 +++++++++---------- .../codeql/swift/generated/expr/TapExpr.qll | 4 +-- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/swift/codegen/generators/qlgen.py b/swift/codegen/generators/qlgen.py index 2172a9517e6..662a782dbd7 100755 --- a/swift/codegen/generators/qlgen.py +++ b/swift/codegen/generators/qlgen.py @@ -63,6 +63,7 @@ abbreviations = { "repr": "representation", "param": "parameter", "int": "integer", + "var": "variable", } abbreviations.update({f"{k}s": f"{v}s" for k, v in abbreviations.items()}) diff --git a/swift/ql/lib/codeql/swift/generated/decl/ParamDecl.qll b/swift/ql/lib/codeql/swift/generated/decl/ParamDecl.qll index 1c153a5e0b6..daa8b3e94dc 100644 --- a/swift/ql/lib/codeql/swift/generated/decl/ParamDecl.qll +++ b/swift/ql/lib/codeql/swift/generated/decl/ParamDecl.qll @@ -14,7 +14,7 @@ module Generated { predicate isInout() { Synth::convertParamDeclToRaw(this).(Raw::ParamDecl).isInout() } /** - * Gets the property wrapper local wrapped var binding of this parameter declaration, if it exists. + * Gets the property wrapper local wrapped variable binding of this parameter declaration, if it exists. * * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the * behavior of both the `Immediate` and non-`Immediate` versions. @@ -27,7 +27,7 @@ module Generated { } /** - * Gets the property wrapper local wrapped var binding of this parameter declaration, if it exists. + * Gets the property wrapper local wrapped variable binding of this parameter declaration, if it exists. * * This is the synthesized binding introducing the property wrapper local wrapped projection * variable for this variable, if any. @@ -44,7 +44,7 @@ module Generated { } /** - * Gets the property wrapper local wrapped var of this parameter declaration, if it exists. + * Gets the property wrapper local wrapped variable of this parameter declaration, if it exists. * * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the * behavior of both the `Immediate` and non-`Immediate` versions. @@ -57,7 +57,7 @@ module Generated { } /** - * Gets the property wrapper local wrapped var of this parameter declaration, if it exists. + * Gets the property wrapper local wrapped variable of this parameter declaration, if it exists. * * This is the synthesized local wrapped value, shadowing this parameter declaration in case it * has a property wrapper. diff --git a/swift/ql/lib/codeql/swift/generated/decl/VarDecl.qll b/swift/ql/lib/codeql/swift/generated/decl/VarDecl.qll index f4b414ea1bd..9cc585e0623 100644 --- a/swift/ql/lib/codeql/swift/generated/decl/VarDecl.qll +++ b/swift/ql/lib/codeql/swift/generated/decl/VarDecl.qll @@ -10,12 +10,12 @@ import codeql.swift.elements.type.Type module Generated { class VarDecl extends Synth::TVarDecl, AbstractStorageDecl { /** - * Gets the name of this var declaration. + * Gets the name of this variable declaration. */ string getName() { result = Synth::convertVarDeclToRaw(this).(Raw::VarDecl).getName() } /** - * Gets the type of this var declaration. + * Gets the type of this variable declaration. * * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the * behavior of both the `Immediate` and non-`Immediate` versions. @@ -25,12 +25,12 @@ module Generated { } /** - * Gets the type of this var declaration. + * Gets the type of this variable declaration. */ final Type getType() { result = getImmediateType().resolve() } /** - * Gets the attached property wrapper type of this var declaration, if it exists. + * Gets the attached property wrapper type of this variable declaration, if it exists. * * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the * behavior of both the `Immediate` and non-`Immediate` versions. @@ -43,7 +43,7 @@ module Generated { } /** - * Gets the attached property wrapper type of this var declaration, if it exists. + * Gets the attached property wrapper type of this variable declaration, if it exists. */ final Type getAttachedPropertyWrapperType() { result = getImmediateAttachedPropertyWrapperType().resolve() @@ -55,7 +55,7 @@ module Generated { final predicate hasAttachedPropertyWrapperType() { exists(getAttachedPropertyWrapperType()) } /** - * Gets the parent pattern of this var declaration, if it exists. + * Gets the parent pattern of this variable declaration, if it exists. * * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the * behavior of both the `Immediate` and non-`Immediate` versions. @@ -68,7 +68,7 @@ module Generated { } /** - * Gets the parent pattern of this var declaration, if it exists. + * Gets the parent pattern of this variable declaration, if it exists. */ final Pattern getParentPattern() { result = getImmediateParentPattern().resolve() } @@ -78,7 +78,7 @@ module Generated { final predicate hasParentPattern() { exists(getParentPattern()) } /** - * Gets the parent initializer of this var declaration, if it exists. + * Gets the parent initializer of this variable declaration, if it exists. * * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the * behavior of both the `Immediate` and non-`Immediate` versions. @@ -91,7 +91,7 @@ module Generated { } /** - * Gets the parent initializer of this var declaration, if it exists. + * Gets the parent initializer of this variable declaration, if it exists. */ final Expr getParentInitializer() { result = getImmediateParentInitializer().resolve() } @@ -101,7 +101,7 @@ module Generated { final predicate hasParentInitializer() { exists(getParentInitializer()) } /** - * Gets the property wrapper backing var binding of this var declaration, if it exists. + * Gets the property wrapper backing variable binding of this variable declaration, if it exists. * * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the * behavior of both the `Immediate` and non-`Immediate` versions. @@ -114,7 +114,7 @@ module Generated { } /** - * Gets the property wrapper backing var binding of this var declaration, if it exists. + * Gets the property wrapper backing variable binding of this variable declaration, if it exists. * * This is the synthesized binding introducing the property wrapper backing variable for this * variable, if any. @@ -131,7 +131,7 @@ module Generated { } /** - * Gets the property wrapper backing var of this var declaration, if it exists. + * Gets the property wrapper backing variable of this variable declaration, if it exists. * * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the * behavior of both the `Immediate` and non-`Immediate` versions. @@ -144,7 +144,7 @@ module Generated { } /** - * Gets the property wrapper backing var of this var declaration, if it exists. + * Gets the property wrapper backing variable of this variable declaration, if it exists. * * This is the synthesized variable holding the property wrapper for this variable, if any. */ @@ -158,7 +158,7 @@ module Generated { final predicate hasPropertyWrapperBackingVar() { exists(getPropertyWrapperBackingVar()) } /** - * Gets the property wrapper projection var binding of this var declaration, if it exists. + * Gets the property wrapper projection variable binding of this variable declaration, if it exists. * * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the * behavior of both the `Immediate` and non-`Immediate` versions. @@ -171,7 +171,7 @@ module Generated { } /** - * Gets the property wrapper projection var binding of this var declaration, if it exists. + * Gets the property wrapper projection variable binding of this variable declaration, if it exists. * * This is the synthesized binding introducing the property wrapper projection variable for this * variable, if any. @@ -188,7 +188,7 @@ module Generated { } /** - * Gets the property wrapper projection var of this var declaration, if it exists. + * Gets the property wrapper projection variable of this variable declaration, if it exists. * * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the * behavior of both the `Immediate` and non-`Immediate` versions. @@ -201,7 +201,7 @@ module Generated { } /** - * Gets the property wrapper projection var of this var declaration, if it exists. + * Gets the property wrapper projection variable of this variable declaration, if it exists. * * If this variable has a property wrapper with a projected value, this is the corresponding * synthesized variable holding that projected value, accessible with this variable's name diff --git a/swift/ql/lib/codeql/swift/generated/expr/TapExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/TapExpr.qll index 37b928dac22..734c8d311ad 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/TapExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/TapExpr.qll @@ -47,7 +47,7 @@ module Generated { final BraceStmt getBody() { result = getImmediateBody().resolve() } /** - * Gets the var of this tap expression. + * Gets the variable of this tap expression. * * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the * behavior of both the `Immediate` and non-`Immediate` versions. @@ -58,7 +58,7 @@ module Generated { } /** - * Gets the var of this tap expression. + * Gets the variable of this tap expression. */ final VarDecl getVar() { result = getImmediateVar().resolve() } } From 3de650e19dbc6ac9a12449dd594374546866600c Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 11 Nov 2022 12:01:46 +0100 Subject: [PATCH 0188/1420] Swift: make `toBeTested` and `shouldPrint` propagate to children --- .../ide-contextual-queries/printAst.ql | 7 +- swift/ql/test/TestUtils.qll | 5 +- .../test/library-tests/ast/PrintAst.expected | 810 +++++++++++++++++- 3 files changed, 795 insertions(+), 27 deletions(-) diff --git a/swift/ql/src/queries/ide-contextual-queries/printAst.ql b/swift/ql/src/queries/ide-contextual-queries/printAst.ql index 26a9df55c1d..03c106c0051 100644 --- a/swift/ql/src/queries/ide-contextual-queries/printAst.ql +++ b/swift/ql/src/queries/ide-contextual-queries/printAst.ql @@ -10,6 +10,7 @@ import swift import codeql.swift.printast.PrintAst import IDEContextual +import codeql.swift.generated.ParentChild /** * Gets the source file to generate an AST from. @@ -23,6 +24,10 @@ class PrintAstConfigurationOverride extends PrintAstConfiguration { */ override predicate shouldPrint(Locatable e) { super.shouldPrint(e) and - e.getFile() = getFileBySourceArchiveName(selectedSourceFile()) + ( + e.getFile() = getFileBySourceArchiveName(selectedSourceFile()) + or + exists(Locatable parent | this.shouldPrint(parent) and parent = getImmediateParent(e)) + ) } } diff --git a/swift/ql/test/TestUtils.qll b/swift/ql/test/TestUtils.qll index 92fcc4de137..35b54d31747 100644 --- a/swift/ql/test/TestUtils.qll +++ b/swift/ql/test/TestUtils.qll @@ -1,11 +1,10 @@ private import codeql.swift.elements +private import codeql.swift.generated.ParentChild cached predicate toBeTested(Element e) { e instanceof File or - e instanceof AppliedPropertyWrapperExpr - or exists(ModuleDecl m | m = e and not m.isBuiltinModule() and @@ -32,6 +31,8 @@ predicate toBeTested(Element e) { e.(UnspecifiedElement).getParent() = tested or e.(OpaqueTypeDecl).getNamingDeclaration() = tested + or + tested = getImmediateParent(e) ) ) } diff --git a/swift/ql/test/library-tests/ast/PrintAst.expected b/swift/ql/test/library-tests/ast/PrintAst.expected index 8dbfecabeee..4fc35515561 100644 --- a/swift/ql/test/library-tests/ast/PrintAst.expected +++ b/swift/ql/test/library-tests/ast/PrintAst.expected @@ -206,9 +206,13 @@ cfg.swift: # 40| getExpr(): [VarargExpansionExpr] [...] # 40| getSubExpr(): [ArrayExpr] [...] # 40| getElement(0): [InterpolatedStringLiteralExpr] "..." +#-----| getInterpolationCountExpr(): [IntegerLiteralExpr] 1 +#-----| getLiteralCapacityExpr(): [IntegerLiteralExpr] 14 # 40| getAppendingExpr(): [TapExpr] TapExpr # 40| getSubExpr(): [OpaqueValueExpr] OpaqueValueExpr # 40| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ConcreteVarDecl] $interpolation +#-----| Type = DefaultStringInterpolation # 40| getElement(1): [CallExpr] call to appendLiteral(_:) # 40| getFunction(): [MethodRefExpr] .appendLiteral(_:) # 40| getBase(): [InOutExpr] &... @@ -461,6 +465,9 @@ cfg.swift: # 99| getSelfParam(): [ParamDecl] self # 99| Type = C # 99| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .myInt +#-----| getBase(): [DeclRefExpr] self # 100| getMember(2): [ConstructorDecl] init(n:) # 100| InterfaceType = (C.Type) -> (Int) -> C # 100| getSelfParam(): [ParamDecl] self @@ -1358,9 +1365,13 @@ cfg.swift: # 262| getBody(): [BraceStmt] { ... } # 263| getElement(0): [ReturnStmt] return ... # 263| getResult(): [InterpolatedStringLiteralExpr] "..." +#-----| getInterpolationCountExpr(): [IntegerLiteralExpr] 4 +#-----| getLiteralCapacityExpr(): [IntegerLiteralExpr] 37 # 263| getAppendingExpr(): [TapExpr] TapExpr # 263| getSubExpr(): [OpaqueValueExpr] OpaqueValueExpr # 263| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ConcreteVarDecl] $interpolation +#-----| Type = DefaultStringInterpolation # 263| getElement(1): [CallExpr] call to appendLiteral(_:) # 263| getFunction(): [MethodRefExpr] .appendLiteral(_:) # 263| getBase(): [InOutExpr] &... @@ -2229,6 +2240,9 @@ cfg.swift: # 353| getSelfParam(): [ParamDecl] self # 353| Type = OptionalC # 353| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .c +#-----| getBase(): [DeclRefExpr] self # 354| getMember(2): [ConstructorDecl] init(arg:) # 354| InterfaceType = (OptionalC.Type) -> (C?) -> OptionalC # 354| getSelfParam(): [ParamDecl] self @@ -2351,23 +2365,27 @@ cfg.swift: # 377| InterfaceType = (Derived.Type) -> (Int) -> Derived # 377| getSelfParam(): [ParamDecl] self # 377| Type = Derived +#-----| getParam(0): [ParamDecl] n +#-----| Type = Int +#-----| getBody(): [BraceStmt] { ... } +# 377| getElement(0): [CallExpr] call to _unimplementedInitializer(className:initName:file:line:column:) +# 377| getFunction(): [DeclRefExpr] _unimplementedInitializer(className:initName:file:line:column:) +# 377| getArgument(0): [Argument] : cfg.Derived +# 377| getExpr(): [StringLiteralExpr] cfg.Derived +# 377| getArgument(1): [Argument] : #... +# 377| getExpr(): [MagicIdentifierLiteralExpr] #... +# 377| getArgument(2): [Argument] : #... +# 377| getExpr(): [MagicIdentifierLiteralExpr] #... +# 377| getArgument(3): [Argument] : #... +# 377| getExpr(): [MagicIdentifierLiteralExpr] #... +# 377| getArgument(4): [Argument] : #... +# 377| getExpr(): [MagicIdentifierLiteralExpr] #... +#-----| getElement(1): [ReturnStmt] return # 377| getMember(2): [DestructorDecl] deinit() # 377| InterfaceType = (Derived) -> () -> () # 377| getSelfParam(): [ParamDecl] self # 377| Type = Derived # 377| getBody(): [BraceStmt] { ... } -# 377| [CallExpr] call to _unimplementedInitializer(className:initName:file:line:column:) -# 377| getFunction(): [DeclRefExpr] _unimplementedInitializer(className:initName:file:line:column:) -# 377| getArgument(0): [Argument] : cfg.Derived -# 377| getExpr(): [StringLiteralExpr] cfg.Derived -# 377| getArgument(1): [Argument] : #... -# 377| getExpr(): [MagicIdentifierLiteralExpr] #... -# 377| getArgument(2): [Argument] : #... -# 377| getExpr(): [MagicIdentifierLiteralExpr] #... -# 377| getArgument(3): [Argument] : #... -# 377| getExpr(): [MagicIdentifierLiteralExpr] #... -# 377| getArgument(4): [Argument] : #... -# 377| getExpr(): [MagicIdentifierLiteralExpr] #... # 383| [ConcreteFuncDecl] doWithoutCatch(x:) # 383| InterfaceType = (Int) throws -> Int # 383| getParam(0): [ParamDecl] x @@ -2421,6 +2439,9 @@ cfg.swift: # 394| getSelfParam(): [ParamDecl] self # 394| Type = Structors # 394| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .field +#-----| getBase(): [DeclRefExpr] self # 394| getAccessorDecl(1): [AccessorDecl] set # 394| InterfaceType = (Structors) -> (Int) -> () # 394| getSelfParam(): [ParamDecl] self @@ -2428,12 +2449,19 @@ cfg.swift: # 394| getParam(0): [ParamDecl] value # 394| Type = Int # 394| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .field +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 394| getAccessorDecl(2): [AccessorDecl] (unnamed function decl) # 394| InterfaceType = (Structors) -> () -> () # 394| getSelfParam(): [ParamDecl] self # 394| Type = Structors # 394| getBody(): [BraceStmt] { ... } # 394| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .field +#-----| getBase(): [DeclRefExpr] self # 395| getMember(2): [ConstructorDecl] init() # 395| InterfaceType = (Structors.Type) -> () -> Structors # 395| getSelfParam(): [ParamDecl] self @@ -2483,6 +2511,9 @@ cfg.swift: # 410| getSelfParam(): [ParamDecl] self # 410| Type = MyLocalClass # 410| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self # 410| getAccessorDecl(1): [AccessorDecl] set # 410| InterfaceType = (MyLocalClass) -> (Int) -> () # 410| getSelfParam(): [ParamDecl] self @@ -2490,12 +2521,19 @@ cfg.swift: # 410| getParam(0): [ParamDecl] value # 410| Type = Int # 410| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 410| getAccessorDecl(2): [AccessorDecl] (unnamed function decl) # 410| InterfaceType = (MyLocalClass) -> () -> () # 410| getSelfParam(): [ParamDecl] self # 410| Type = MyLocalClass # 410| getBody(): [BraceStmt] { ... } # 410| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self # 411| getMember(2): [ConstructorDecl] init() # 411| InterfaceType = (MyLocalClass.Type) -> () -> MyLocalClass # 411| getSelfParam(): [ParamDecl] self @@ -2523,6 +2561,9 @@ cfg.swift: # 417| getSelfParam(): [ParamDecl] self # 417| Type = MyLocalStruct # 417| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self # 417| getAccessorDecl(1): [AccessorDecl] set # 417| InterfaceType = (inout MyLocalStruct) -> (Int) -> () # 417| getSelfParam(): [ParamDecl] self @@ -2530,12 +2571,19 @@ cfg.swift: # 417| getParam(0): [ParamDecl] value # 417| Type = Int # 417| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 417| getAccessorDecl(2): [AccessorDecl] (unnamed function decl) # 417| InterfaceType = (inout MyLocalStruct) -> () -> () # 417| getSelfParam(): [ParamDecl] self # 417| Type = MyLocalStruct # 417| getBody(): [BraceStmt] { ... } # 417| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self # 418| getMember(2): [ConstructorDecl] init() # 418| InterfaceType = (MyLocalStruct.Type) -> () -> MyLocalStruct # 418| getSelfParam(): [ParamDecl] self @@ -2551,6 +2599,107 @@ cfg.swift: # 424| getMember(1): [EnumElementDecl] A # 425| getMember(2): [EnumCaseDecl] case ... # 425| getMember(3): [EnumElementDecl] B +#-----| getMember(4): [ConcreteFuncDecl] __derived_enum_equals(_:_:) +#-----| InterfaceType = (MyLocalEnum.Type) -> (MyLocalEnum, MyLocalEnum) -> Bool +#-----| getSelfParam(): [ParamDecl] self +#-----| Type = MyLocalEnum.Type +#-----| getParam(0): [ParamDecl] a +#-----| Type = MyLocalEnum +#-----| getParam(1): [ParamDecl] b +#-----| Type = MyLocalEnum +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [PatternBindingDecl] var ... = ... +#-----| getPattern(0): [TypedPattern] ... as ... +#-----| getSubPattern(): [NamedPattern] index_a +#-----| getElement(1): [SwitchStmt] switch a { ... } +#-----| getExpr(): [DeclRefExpr] a +#-----| getCase(0): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] index_a +#-----| getSource(): [IntegerLiteralExpr] 0 +#-----| getLabel(0): [CaseLabelItem] .A +#-----| getPattern(): [EnumElementPattern] .A +#-----| getCase(1): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] index_a +#-----| getSource(): [IntegerLiteralExpr] 1 +#-----| getLabel(0): [CaseLabelItem] .B +#-----| getPattern(): [EnumElementPattern] .B +#-----| getElement(2): [PatternBindingDecl] var ... = ... +#-----| getPattern(0): [TypedPattern] ... as ... +#-----| getSubPattern(): [NamedPattern] index_b +#-----| getElement(3): [SwitchStmt] switch b { ... } +#-----| getExpr(): [DeclRefExpr] b +#-----| getCase(0): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] index_b +#-----| getSource(): [IntegerLiteralExpr] 0 +#-----| getLabel(0): [CaseLabelItem] .A +#-----| getPattern(): [EnumElementPattern] .A +#-----| getCase(1): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] index_b +#-----| getSource(): [IntegerLiteralExpr] 1 +#-----| getLabel(0): [CaseLabelItem] .B +#-----| getPattern(): [EnumElementPattern] .B +#-----| getElement(4): [ReturnStmt] return ... +#-----| getResult(): [BinaryExpr] ... .==(_:_:) ... +#-----| getFunction(): [MethodRefExpr] .==(_:_:) +#-----| getBase(): [TypeExpr] Int.Type +#-----| getArgument(0): [Argument] : index_a +#-----| getExpr(): [DeclRefExpr] index_a +#-----| getArgument(1): [Argument] : index_b +#-----| getExpr(): [DeclRefExpr] index_b +#-----| getMember(5): [PatternBindingDecl] var ... = ... +#-----| getPattern(0): [TypedPattern] ... as ... +#-----| getSubPattern(): [NamedPattern] hashValue +#-----| getMember(6): [ConcreteFuncDecl] hash(into:) +#-----| InterfaceType = (MyLocalEnum) -> (inout Hasher) -> () +#-----| getSelfParam(): [ParamDecl] self +#-----| Type = MyLocalEnum +#-----| getParam(0): [ParamDecl] hasher +#-----| Type = Hasher +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [PatternBindingDecl] var ... = ... +#-----| getPattern(0): [TypedPattern] ... as ... +#-----| getSubPattern(): [NamedPattern] discriminator +#-----| getElement(1): [SwitchStmt] switch self { ... } +#-----| getExpr(): [DeclRefExpr] self +#-----| getCase(0): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] discriminator +#-----| getSource(): [IntegerLiteralExpr] 0 +#-----| getLabel(0): [CaseLabelItem] .A +#-----| getPattern(): [EnumElementPattern] .A +#-----| getCase(1): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] discriminator +#-----| getSource(): [IntegerLiteralExpr] 1 +#-----| getLabel(0): [CaseLabelItem] .B +#-----| getPattern(): [EnumElementPattern] .B +#-----| getElement(2): [CallExpr] call to ... +#-----| getFunction(): [UnresolvedDotExpr] ... .combine(_:) +#-----| getBase(): [DeclRefExpr] hasher +#-----| getArgument(0): [Argument] : discriminator +#-----| getExpr(): [DeclRefExpr] discriminator +#-----| getMember(7): [ConcreteVarDecl] hashValue +#-----| Type = Int +#-----| getAccessorDecl(0): [AccessorDecl] get +#-----| InterfaceType = (MyLocalEnum) -> () -> Int +#-----| getSelfParam(): [ParamDecl] self +#-----| Type = MyLocalEnum +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [CallExpr] call to _hashValue(for:) +#-----| getFunction(): [DeclRefExpr] _hashValue(for:) +#-----| getArgument(0): [Argument] for: self +#-----| getExpr(): [DeclRefExpr] self # 428| getElement(3): [PatternBindingDecl] var ... = ... # 428| getPattern(0): [TypedPattern] ... as ... # 428| getSubPattern(): [NamedPattern] myLocalVar @@ -2591,6 +2740,9 @@ cfg.swift: # 446| getSelfParam(): [ParamDecl] self # 446| Type = B # 446| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self # 446| getAccessorDecl(1): [AccessorDecl] set # 446| InterfaceType = (inout B) -> (Int) -> () # 446| getSelfParam(): [ParamDecl] self @@ -2598,12 +2750,19 @@ cfg.swift: # 446| getParam(0): [ParamDecl] value # 446| Type = Int # 446| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 446| getAccessorDecl(2): [AccessorDecl] (unnamed function decl) # 446| InterfaceType = (inout B) -> () -> () # 446| getSelfParam(): [ParamDecl] self # 446| Type = B # 446| getBody(): [BraceStmt] { ... } # 446| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self # 445| getMember(2): [ConstructorDecl] init(x:) # 445| InterfaceType = (B.Type) -> (Int) -> B # 445| getSelfParam(): [ParamDecl] self @@ -2622,6 +2781,9 @@ cfg.swift: # 450| getSelfParam(): [ParamDecl] self # 450| Type = A # 450| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .b +#-----| getBase(): [DeclRefExpr] self # 450| getAccessorDecl(1): [AccessorDecl] set # 450| InterfaceType = (inout A) -> (B) -> () # 450| getSelfParam(): [ParamDecl] self @@ -2629,12 +2791,19 @@ cfg.swift: # 450| getParam(0): [ParamDecl] value # 450| Type = B # 450| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .b +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 450| getAccessorDecl(2): [AccessorDecl] (unnamed function decl) # 450| InterfaceType = (inout A) -> () -> () # 450| getSelfParam(): [ParamDecl] self # 450| Type = A # 450| getBody(): [BraceStmt] { ... } # 450| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .b +#-----| getBase(): [DeclRefExpr] self # 451| getMember(2): [PatternBindingDecl] var ... = ... # 451| getPattern(0): [TypedPattern] ... as ... # 451| getSubPattern(): [NamedPattern] bs @@ -2646,6 +2815,9 @@ cfg.swift: # 451| getSelfParam(): [ParamDecl] self # 451| Type = A # 451| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .bs +#-----| getBase(): [DeclRefExpr] self # 451| getAccessorDecl(1): [AccessorDecl] set # 451| InterfaceType = (inout A) -> ([B]) -> () # 451| getSelfParam(): [ParamDecl] self @@ -2653,13 +2825,21 @@ cfg.swift: # 451| getParam(0): [ParamDecl] value # 451| Type = [B] # 451| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .bs +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 451| getAccessorDecl(2): [AccessorDecl] (unnamed function decl) # 451| InterfaceType = (inout A) -> () -> () # 451| getSelfParam(): [ParamDecl] self # 451| Type = A # 451| getBody(): [BraceStmt] { ... } # 451| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .bs +#-----| getBase(): [DeclRefExpr] self # 452| getMember(4): [PatternBindingDecl] var ... = ... +#-----| getInit(0): [NilLiteralExpr] nil # 452| getPattern(0): [TypedPattern] ... as ... # 452| getSubPattern(): [NamedPattern] mayB # 452| getTypeRepr(): [TypeRepr] B? @@ -2670,6 +2850,9 @@ cfg.swift: # 452| getSelfParam(): [ParamDecl] self # 452| Type = A # 452| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .mayB +#-----| getBase(): [DeclRefExpr] self # 452| getAccessorDecl(1): [AccessorDecl] set # 452| InterfaceType = (inout A) -> (B?) -> () # 452| getSelfParam(): [ParamDecl] self @@ -2677,12 +2860,19 @@ cfg.swift: # 452| getParam(0): [ParamDecl] value # 452| Type = B? # 452| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .mayB +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 452| getAccessorDecl(2): [AccessorDecl] (unnamed function decl) # 452| InterfaceType = (inout A) -> () -> () # 452| getSelfParam(): [ParamDecl] self # 452| Type = A # 452| getBody(): [BraceStmt] { ... } # 452| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .mayB +#-----| getBase(): [DeclRefExpr] self # 449| getMember(6): [ConstructorDecl] init(b:bs:mayB:) # 449| InterfaceType = (A.Type) -> (B, [B], B?) -> A # 449| getSelfParam(): [ParamDecl] self @@ -2779,6 +2969,9 @@ declarations.swift: # 2| getSelfParam(): [ParamDecl] self # 2| Type = Foo # 2| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self # 2| getAccessorDecl(1): [AccessorDecl] set # 2| InterfaceType = (inout Foo) -> (Int) -> () # 2| getSelfParam(): [ParamDecl] self @@ -2786,12 +2979,19 @@ declarations.swift: # 2| getParam(0): [ParamDecl] value # 2| Type = Int # 2| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 2| getAccessorDecl(2): [AccessorDecl] (unnamed function decl) # 2| InterfaceType = (inout Foo) -> () -> () # 2| getSelfParam(): [ParamDecl] self # 2| Type = Foo # 2| getBody(): [BraceStmt] { ... } # 2| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self # 3| getMember(2): [PatternBindingDecl] var ... = ... # 3| getPattern(0): [TypedPattern] ... as ... # 3| getSubPattern(): [NamedPattern] next @@ -2837,6 +3037,9 @@ declarations.swift: # 3| Type = Foo # 3| getBody(): [BraceStmt] { ... } # 3| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .next +#-----| getBase(): [DeclRefExpr] self # 1| getMember(4): [ConstructorDecl] init() # 1| InterfaceType = (Foo.Type) -> () -> Foo # 1| getSelfParam(): [ParamDecl] self @@ -2862,6 +3065,9 @@ declarations.swift: # 9| getSelfParam(): [ParamDecl] self # 9| Type = Bar # 9| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self # 9| getAccessorDecl(1): [AccessorDecl] set # 9| InterfaceType = (Bar) -> (Double) -> () # 9| getSelfParam(): [ParamDecl] self @@ -2869,12 +3075,19 @@ declarations.swift: # 9| getParam(0): [ParamDecl] value # 9| Type = Double # 9| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 9| getAccessorDecl(2): [AccessorDecl] (unnamed function decl) # 9| InterfaceType = (Bar) -> () -> () # 9| getSelfParam(): [ParamDecl] self # 9| Type = Bar # 9| getBody(): [BraceStmt] { ... } # 9| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self # 9| getMember(2): [DestructorDecl] deinit() # 9| InterfaceType = (Bar) -> () -> () # 9| getSelfParam(): [ParamDecl] self @@ -2894,6 +3107,170 @@ declarations.swift: # 13| getMember(4): [EnumElementDecl] value3 # 13| getMember(5): [EnumElementDecl] value4 # 13| getMember(6): [EnumElementDecl] value5 +#-----| getMember(7): [ConcreteFuncDecl] __derived_enum_equals(_:_:) +#-----| InterfaceType = (EnumValues.Type) -> (EnumValues, EnumValues) -> Bool +#-----| getSelfParam(): [ParamDecl] self +#-----| Type = EnumValues.Type +#-----| getParam(0): [ParamDecl] a +#-----| Type = EnumValues +#-----| getParam(1): [ParamDecl] b +#-----| Type = EnumValues +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [PatternBindingDecl] var ... = ... +#-----| getPattern(0): [TypedPattern] ... as ... +#-----| getSubPattern(): [NamedPattern] index_a +#-----| getElement(1): [SwitchStmt] switch a { ... } +#-----| getExpr(): [DeclRefExpr] a +#-----| getCase(0): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] index_a +#-----| getSource(): [IntegerLiteralExpr] 0 +#-----| getLabel(0): [CaseLabelItem] .value1 +#-----| getPattern(): [EnumElementPattern] .value1 +#-----| getCase(1): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] index_a +#-----| getSource(): [IntegerLiteralExpr] 1 +#-----| getLabel(0): [CaseLabelItem] .value2 +#-----| getPattern(): [EnumElementPattern] .value2 +#-----| getCase(2): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] index_a +#-----| getSource(): [IntegerLiteralExpr] 2 +#-----| getLabel(0): [CaseLabelItem] .value3 +#-----| getPattern(): [EnumElementPattern] .value3 +#-----| getCase(3): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] index_a +#-----| getSource(): [IntegerLiteralExpr] 3 +#-----| getLabel(0): [CaseLabelItem] .value4 +#-----| getPattern(): [EnumElementPattern] .value4 +#-----| getCase(4): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] index_a +#-----| getSource(): [IntegerLiteralExpr] 4 +#-----| getLabel(0): [CaseLabelItem] .value5 +#-----| getPattern(): [EnumElementPattern] .value5 +#-----| getElement(2): [PatternBindingDecl] var ... = ... +#-----| getPattern(0): [TypedPattern] ... as ... +#-----| getSubPattern(): [NamedPattern] index_b +#-----| getElement(3): [SwitchStmt] switch b { ... } +#-----| getExpr(): [DeclRefExpr] b +#-----| getCase(0): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] index_b +#-----| getSource(): [IntegerLiteralExpr] 0 +#-----| getLabel(0): [CaseLabelItem] .value1 +#-----| getPattern(): [EnumElementPattern] .value1 +#-----| getCase(1): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] index_b +#-----| getSource(): [IntegerLiteralExpr] 1 +#-----| getLabel(0): [CaseLabelItem] .value2 +#-----| getPattern(): [EnumElementPattern] .value2 +#-----| getCase(2): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] index_b +#-----| getSource(): [IntegerLiteralExpr] 2 +#-----| getLabel(0): [CaseLabelItem] .value3 +#-----| getPattern(): [EnumElementPattern] .value3 +#-----| getCase(3): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] index_b +#-----| getSource(): [IntegerLiteralExpr] 3 +#-----| getLabel(0): [CaseLabelItem] .value4 +#-----| getPattern(): [EnumElementPattern] .value4 +#-----| getCase(4): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] index_b +#-----| getSource(): [IntegerLiteralExpr] 4 +#-----| getLabel(0): [CaseLabelItem] .value5 +#-----| getPattern(): [EnumElementPattern] .value5 +#-----| getElement(4): [ReturnStmt] return ... +#-----| getResult(): [BinaryExpr] ... .==(_:_:) ... +#-----| getFunction(): [MethodRefExpr] .==(_:_:) +#-----| getBase(): [TypeExpr] Int.Type +#-----| getArgument(0): [Argument] : index_a +#-----| getExpr(): [DeclRefExpr] index_a +#-----| getArgument(1): [Argument] : index_b +#-----| getExpr(): [DeclRefExpr] index_b +#-----| getMember(8): [PatternBindingDecl] var ... = ... +#-----| getPattern(0): [TypedPattern] ... as ... +#-----| getSubPattern(): [NamedPattern] hashValue +#-----| getMember(9): [ConcreteFuncDecl] hash(into:) +#-----| InterfaceType = (EnumValues) -> (inout Hasher) -> () +#-----| getSelfParam(): [ParamDecl] self +#-----| Type = EnumValues +#-----| getParam(0): [ParamDecl] hasher +#-----| Type = Hasher +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [PatternBindingDecl] var ... = ... +#-----| getPattern(0): [TypedPattern] ... as ... +#-----| getSubPattern(): [NamedPattern] discriminator +#-----| getElement(1): [SwitchStmt] switch self { ... } +#-----| getExpr(): [DeclRefExpr] self +#-----| getCase(0): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] discriminator +#-----| getSource(): [IntegerLiteralExpr] 0 +#-----| getLabel(0): [CaseLabelItem] .value1 +#-----| getPattern(): [EnumElementPattern] .value1 +#-----| getCase(1): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] discriminator +#-----| getSource(): [IntegerLiteralExpr] 1 +#-----| getLabel(0): [CaseLabelItem] .value2 +#-----| getPattern(): [EnumElementPattern] .value2 +#-----| getCase(2): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] discriminator +#-----| getSource(): [IntegerLiteralExpr] 2 +#-----| getLabel(0): [CaseLabelItem] .value3 +#-----| getPattern(): [EnumElementPattern] .value3 +#-----| getCase(3): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] discriminator +#-----| getSource(): [IntegerLiteralExpr] 3 +#-----| getLabel(0): [CaseLabelItem] .value4 +#-----| getPattern(): [EnumElementPattern] .value4 +#-----| getCase(4): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] discriminator +#-----| getSource(): [IntegerLiteralExpr] 4 +#-----| getLabel(0): [CaseLabelItem] .value5 +#-----| getPattern(): [EnumElementPattern] .value5 +#-----| getElement(2): [CallExpr] call to ... +#-----| getFunction(): [UnresolvedDotExpr] ... .combine(_:) +#-----| getBase(): [DeclRefExpr] hasher +#-----| getArgument(0): [Argument] : discriminator +#-----| getExpr(): [DeclRefExpr] discriminator +#-----| getMember(10): [ConcreteVarDecl] hashValue +#-----| Type = Int +#-----| getAccessorDecl(0): [AccessorDecl] get +#-----| InterfaceType = (EnumValues) -> () -> Int +#-----| getSelfParam(): [ParamDecl] self +#-----| Type = EnumValues +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [CallExpr] call to _hashValue(for:) +#-----| getFunction(): [DeclRefExpr] _hashValue(for:) +#-----| getArgument(0): [Argument] for: self +#-----| getExpr(): [DeclRefExpr] self # 16| [EnumDecl] EnumWithParams # 17| getMember(0): [EnumCaseDecl] case ... # 17| getMember(1): [EnumElementDecl] nodata1 @@ -2912,6 +3289,7 @@ declarations.swift: # 19| getParam(2): [ParamDecl] _ # 19| Type = Double # 22| [ProtocolDecl] MyProtocol +#-----| getGenericTypeParam(0): [GenericTypeParamDecl] Self # 23| getMember(0): [PatternBindingDecl] var ... = ... # 23| getPattern(0): [TypedPattern] ... as ... # 23| getSubPattern(): [NamedPattern] mustBeSettable @@ -3001,6 +3379,9 @@ declarations.swift: # 41| getSelfParam(): [ParamDecl] self # 41| Type = Baz # 41| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .field +#-----| getBase(): [DeclRefExpr] self # 41| getAccessorDecl(1): [AccessorDecl] set # 41| InterfaceType = (Baz) -> (Int) -> () # 41| getSelfParam(): [ParamDecl] self @@ -3008,12 +3389,19 @@ declarations.swift: # 41| getParam(0): [ParamDecl] value # 41| Type = Int # 41| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .field +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 41| getAccessorDecl(2): [AccessorDecl] (unnamed function decl) # 41| InterfaceType = (Baz) -> () -> () # 41| getSelfParam(): [ParamDecl] self # 41| Type = Baz # 41| getBody(): [BraceStmt] { ... } # 41| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .field +#-----| getBase(): [DeclRefExpr] self # 42| getMember(2): [ConstructorDecl] init() # 42| InterfaceType = (Baz.Type) -> () -> Baz # 42| getSelfParam(): [ParamDecl] self @@ -3078,6 +3466,9 @@ declarations.swift: # 77| getAccessorDecl(0): [AccessorDecl] get # 77| InterfaceType = () -> Int # 77| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .wrappedValue +#-----| getBase(): [DeclRefExpr] _x # 78| getElement(2): [ReturnStmt] return ... # 78| getResult(): [DeclRefExpr] x # 77| [CallExpr] call to init() @@ -3117,6 +3508,9 @@ declarations.swift: # 82| Type = HasPropertyAndObserver # 82| getBody(): [BraceStmt] { ... } # 82| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .settableField +#-----| getBase(): [DeclRefExpr] self # 91| getMember(2): [PatternBindingDecl] var ... = ... # 91| getPattern(0): [TypedPattern] ... as ... # 91| getSubPattern(): [NamedPattern] readOnlyField1 @@ -3154,6 +3548,9 @@ declarations.swift: # 102| getSelfParam(): [ParamDecl] self # 102| Type = HasPropertyAndObserver # 102| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .normalField +#-----| getBase(): [DeclRefExpr] self # 102| getAccessorDecl(1): [AccessorDecl] set # 102| InterfaceType = (inout HasPropertyAndObserver) -> (Int) -> () # 102| getSelfParam(): [ParamDecl] self @@ -3161,12 +3558,19 @@ declarations.swift: # 102| getParam(0): [ParamDecl] value # 102| Type = Int # 102| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .normalField +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 102| getAccessorDecl(2): [AccessorDecl] (unnamed function decl) # 102| InterfaceType = (inout HasPropertyAndObserver) -> () -> () # 102| getSelfParam(): [ParamDecl] self # 102| Type = HasPropertyAndObserver # 102| getBody(): [BraceStmt] { ... } # 102| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .normalField +#-----| getBase(): [DeclRefExpr] self # 104| getMember(8): [SubscriptDecl] subscript ... # 105| getAccessorDecl(0): [AccessorDecl] get # 105| InterfaceType = (HasPropertyAndObserver) -> (Int) -> Int @@ -3190,8 +3594,15 @@ declarations.swift: # 104| InterfaceType = (inout HasPropertyAndObserver) -> (Int) -> () # 104| getSelfParam(): [ParamDecl] self # 104| Type = HasPropertyAndObserver +#-----| getParam(0): [ParamDecl] x +#-----| Type = Int # 104| getBody(): [BraceStmt] { ... } # 104| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [SubscriptExpr] ...[...] +#-----| getBase(): [DeclRefExpr] self +#-----| getArgument(0): [Argument] : x +#-----| getExpr(): [DeclRefExpr] x # 104| getParam(0): [ParamDecl] x # 104| Type = Int # 111| getMember(9): [SubscriptDecl] subscript ... @@ -3228,6 +3639,9 @@ declarations.swift: # 115| getSelfParam(): [ParamDecl] self # 115| Type = HasPropertyAndObserver # 115| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .hasWillSet1 +#-----| getBase(): [DeclRefExpr] self # 115| getAccessorDecl(2): [AccessorDecl] set # 115| InterfaceType = (inout HasPropertyAndObserver) -> (Int) -> () # 115| getSelfParam(): [ParamDecl] self @@ -3235,12 +3649,25 @@ declarations.swift: # 115| getParam(0): [ParamDecl] value # 115| Type = Int # 115| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [CallExpr] call to willSet +#-----| getFunction(): [MethodRefExpr] .willSet +#-----| getBase(): [InOutExpr] &... +#-----| getSubExpr(): [DeclRefExpr] self +#-----| getArgument(0): [Argument] : value +#-----| getExpr(): [DeclRefExpr] value +#-----| getElement(1): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .hasWillSet1 +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 115| getAccessorDecl(3): [AccessorDecl] (unnamed function decl) # 115| InterfaceType = (inout HasPropertyAndObserver) -> () -> () # 115| getSelfParam(): [ParamDecl] self # 115| Type = HasPropertyAndObserver # 115| getBody(): [BraceStmt] { ... } # 115| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .hasWillSet1 +#-----| getBase(): [DeclRefExpr] self # 119| getMember(12): [PatternBindingDecl] var ... = ... # 119| getPattern(0): [TypedPattern] ... as ... # 119| getSubPattern(): [NamedPattern] hasWillSet2 @@ -3259,6 +3686,9 @@ declarations.swift: # 119| getSelfParam(): [ParamDecl] self # 119| Type = HasPropertyAndObserver # 119| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .hasWillSet2 +#-----| getBase(): [DeclRefExpr] self # 119| getAccessorDecl(2): [AccessorDecl] set # 119| InterfaceType = (inout HasPropertyAndObserver) -> (Int) -> () # 119| getSelfParam(): [ParamDecl] self @@ -3266,12 +3696,25 @@ declarations.swift: # 119| getParam(0): [ParamDecl] value # 119| Type = Int # 119| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [CallExpr] call to willSet +#-----| getFunction(): [MethodRefExpr] .willSet +#-----| getBase(): [InOutExpr] &... +#-----| getSubExpr(): [DeclRefExpr] self +#-----| getArgument(0): [Argument] : value +#-----| getExpr(): [DeclRefExpr] value +#-----| getElement(1): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .hasWillSet2 +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 119| getAccessorDecl(3): [AccessorDecl] (unnamed function decl) # 119| InterfaceType = (inout HasPropertyAndObserver) -> () -> () # 119| getSelfParam(): [ParamDecl] self # 119| Type = HasPropertyAndObserver # 119| getBody(): [BraceStmt] { ... } # 119| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .hasWillSet2 +#-----| getBase(): [DeclRefExpr] self # 123| getMember(14): [PatternBindingDecl] var ... = ... # 123| getPattern(0): [TypedPattern] ... as ... # 123| getSubPattern(): [NamedPattern] hasDidSet1 @@ -3290,6 +3733,9 @@ declarations.swift: # 123| getSelfParam(): [ParamDecl] self # 123| Type = HasPropertyAndObserver # 123| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .hasDidSet1 +#-----| getBase(): [DeclRefExpr] self # 123| getAccessorDecl(2): [AccessorDecl] set # 123| InterfaceType = (inout HasPropertyAndObserver) -> (Int) -> () # 123| getSelfParam(): [ParamDecl] self @@ -3297,12 +3743,32 @@ declarations.swift: # 123| getParam(0): [ParamDecl] value # 123| Type = Int # 123| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [PatternBindingDecl] var ... = ... +#-----| getInit(0): [MemberRefExpr] .hasDidSet1 +#-----| getBase(): [DeclRefExpr] self +#-----| getInit(0).getFullyConverted(): [LoadExpr] (Int) ... +#-----| getPattern(0): [NamedPattern] tmp +#-----| getElement(1): [ConcreteVarDecl] tmp +#-----| Type = Int +#-----| getElement(2): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .hasDidSet1 +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value +#-----| getElement(3): [CallExpr] call to didSet +#-----| getFunction(): [MethodRefExpr] .didSet +#-----| getBase(): [InOutExpr] &... +#-----| getSubExpr(): [DeclRefExpr] self +#-----| getArgument(0): [Argument] : tmp +#-----| getExpr(): [DeclRefExpr] tmp # 123| getAccessorDecl(3): [AccessorDecl] (unnamed function decl) # 123| InterfaceType = (inout HasPropertyAndObserver) -> () -> () # 123| getSelfParam(): [ParamDecl] self # 123| Type = HasPropertyAndObserver # 123| getBody(): [BraceStmt] { ... } # 123| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .hasDidSet1 +#-----| getBase(): [DeclRefExpr] self # 127| getMember(16): [PatternBindingDecl] var ... = ... # 127| getPattern(0): [TypedPattern] ... as ... # 127| getSubPattern(): [NamedPattern] hasDidSet2 @@ -3319,6 +3785,9 @@ declarations.swift: # 127| getSelfParam(): [ParamDecl] self # 127| Type = HasPropertyAndObserver # 127| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .hasDidSet2 +#-----| getBase(): [DeclRefExpr] self # 127| getAccessorDecl(2): [AccessorDecl] set # 127| InterfaceType = (inout HasPropertyAndObserver) -> (Int) -> () # 127| getSelfParam(): [ParamDecl] self @@ -3326,12 +3795,27 @@ declarations.swift: # 127| getParam(0): [ParamDecl] value # 127| Type = Int # 127| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .hasDidSet2 +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value +#-----| getElement(1): [CallExpr] call to didSet +#-----| getFunction(): [MethodRefExpr] .didSet +#-----| getBase(): [InOutExpr] &... +#-----| getSubExpr(): [DeclRefExpr] self # 127| getAccessorDecl(3): [AccessorDecl] (unnamed function decl) # 127| InterfaceType = (inout HasPropertyAndObserver) -> () -> () # 127| getSelfParam(): [ParamDecl] self # 127| Type = HasPropertyAndObserver # 127| getBody(): [BraceStmt] { ... } # 127| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .hasDidSet2 +#-----| getBase(): [DeclRefExpr] self +#-----| getElement(1): [CallExpr] call to didSet +#-----| getFunction(): [MethodRefExpr] .didSet +#-----| getBase(): [InOutExpr] &... +#-----| getSubExpr(): [DeclRefExpr] self # 131| getMember(18): [PatternBindingDecl] var ... = ... # 131| getPattern(0): [TypedPattern] ... as ... # 131| getSubPattern(): [NamedPattern] hasBoth @@ -3355,6 +3839,9 @@ declarations.swift: # 131| getSelfParam(): [ParamDecl] self # 131| Type = HasPropertyAndObserver # 131| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .hasBoth +#-----| getBase(): [DeclRefExpr] self # 131| getAccessorDecl(3): [AccessorDecl] set # 131| InterfaceType = (inout HasPropertyAndObserver) -> (Int) -> () # 131| getSelfParam(): [ParamDecl] self @@ -3362,12 +3849,29 @@ declarations.swift: # 131| getParam(0): [ParamDecl] value # 131| Type = Int # 131| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [CallExpr] call to willSet +#-----| getFunction(): [MethodRefExpr] .willSet +#-----| getBase(): [InOutExpr] &... +#-----| getSubExpr(): [DeclRefExpr] self +#-----| getArgument(0): [Argument] : value +#-----| getExpr(): [DeclRefExpr] value +#-----| getElement(1): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .hasBoth +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value +#-----| getElement(2): [CallExpr] call to didSet +#-----| getFunction(): [MethodRefExpr] .didSet +#-----| getBase(): [InOutExpr] &... +#-----| getSubExpr(): [DeclRefExpr] self # 131| getAccessorDecl(4): [AccessorDecl] (unnamed function decl) # 131| InterfaceType = (inout HasPropertyAndObserver) -> () -> () # 131| getSelfParam(): [ParamDecl] self # 131| Type = HasPropertyAndObserver # 131| getBody(): [BraceStmt] { ... } # 131| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .hasBoth +#-----| getBase(): [DeclRefExpr] self # 81| getMember(20): [ConstructorDecl] init(normalField:hasWillSet1:hasWillSet2:hasDidSet1:hasDidSet2:hasBoth:) # 81| InterfaceType = (HasPropertyAndObserver.Type) -> (Int, Int, Int, Int, Int, Int) -> HasPropertyAndObserver # 81| getSelfParam(): [ParamDecl] self @@ -3446,6 +3950,14 @@ declarations.swift: # 152| InterfaceType = (Derived.Type) -> () -> Derived # 152| getSelfParam(): [ParamDecl] self # 152| Type = Derived +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [RebindSelfInConstructorExpr] self = ... +#-----| getSubExpr(): [CallExpr] call to ... +#-----| getFunction(): [DotSyntaxCallExpr] call to ... +#-----| getFunction(): [OtherConstructorDeclRefExpr] init() +#-----| getArgument(0): [Argument] : super +#-----| getExpr(): [SuperRefExpr] super +#-----| getElement(1): [ReturnStmt] return # 152| getMember(1): [DestructorDecl] deinit() # 152| InterfaceType = (Derived) -> () -> () # 152| getSelfParam(): [ParamDecl] self @@ -3528,9 +4040,13 @@ expressions.swift: # 7| getBody(): [BraceStmt] { ... } # 7| getElement(0): [PatternBindingDecl] var ... = ... # 7| getInit(0): [InterpolatedStringLiteralExpr] "..." +#-----| getInterpolationCountExpr(): [IntegerLiteralExpr] 1 +#-----| getLiteralCapacityExpr(): [IntegerLiteralExpr] 6 # 7| getAppendingExpr(): [TapExpr] TapExpr # 7| getSubExpr(): [OpaqueValueExpr] OpaqueValueExpr # 7| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ConcreteVarDecl] $interpolation +#-----| Type = DefaultStringInterpolation # 7| getElement(1): [CallExpr] call to appendLiteral(_:) # 7| getFunction(): [MethodRefExpr] .appendLiteral(_:) # 7| getBase(): [InOutExpr] &... @@ -3564,6 +4080,86 @@ expressions.swift: # 10| [EnumDecl] AnError # 11| getMember(0): [EnumCaseDecl] case ... # 11| getMember(1): [EnumElementDecl] failed +#-----| getMember(2): [ConcreteFuncDecl] __derived_enum_equals(_:_:) +#-----| InterfaceType = (AnError.Type) -> (AnError, AnError) -> Bool +#-----| getSelfParam(): [ParamDecl] self +#-----| Type = AnError.Type +#-----| getParam(0): [ParamDecl] a +#-----| Type = AnError +#-----| getParam(1): [ParamDecl] b +#-----| Type = AnError +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [PatternBindingDecl] var ... = ... +#-----| getPattern(0): [TypedPattern] ... as ... +#-----| getSubPattern(): [NamedPattern] index_a +#-----| getElement(1): [SwitchStmt] switch a { ... } +#-----| getExpr(): [DeclRefExpr] a +#-----| getCase(0): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] index_a +#-----| getSource(): [IntegerLiteralExpr] 0 +#-----| getLabel(0): [CaseLabelItem] .failed +#-----| getPattern(): [EnumElementPattern] .failed +#-----| getElement(2): [PatternBindingDecl] var ... = ... +#-----| getPattern(0): [TypedPattern] ... as ... +#-----| getSubPattern(): [NamedPattern] index_b +#-----| getElement(3): [SwitchStmt] switch b { ... } +#-----| getExpr(): [DeclRefExpr] b +#-----| getCase(0): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] index_b +#-----| getSource(): [IntegerLiteralExpr] 0 +#-----| getLabel(0): [CaseLabelItem] .failed +#-----| getPattern(): [EnumElementPattern] .failed +#-----| getElement(4): [ReturnStmt] return ... +#-----| getResult(): [BinaryExpr] ... .==(_:_:) ... +#-----| getFunction(): [MethodRefExpr] .==(_:_:) +#-----| getBase(): [TypeExpr] Int.Type +#-----| getArgument(0): [Argument] : index_a +#-----| getExpr(): [DeclRefExpr] index_a +#-----| getArgument(1): [Argument] : index_b +#-----| getExpr(): [DeclRefExpr] index_b +#-----| getMember(3): [PatternBindingDecl] var ... = ... +#-----| getPattern(0): [TypedPattern] ... as ... +#-----| getSubPattern(): [NamedPattern] hashValue +#-----| getMember(4): [ConcreteFuncDecl] hash(into:) +#-----| InterfaceType = (AnError) -> (inout Hasher) -> () +#-----| getSelfParam(): [ParamDecl] self +#-----| Type = AnError +#-----| getParam(0): [ParamDecl] hasher +#-----| Type = Hasher +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [PatternBindingDecl] var ... = ... +#-----| getPattern(0): [TypedPattern] ... as ... +#-----| getSubPattern(): [NamedPattern] discriminator +#-----| getElement(1): [SwitchStmt] switch self { ... } +#-----| getExpr(): [DeclRefExpr] self +#-----| getCase(0): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] discriminator +#-----| getSource(): [IntegerLiteralExpr] 0 +#-----| getLabel(0): [CaseLabelItem] .failed +#-----| getPattern(): [EnumElementPattern] .failed +#-----| getElement(2): [CallExpr] call to ... +#-----| getFunction(): [UnresolvedDotExpr] ... .combine(_:) +#-----| getBase(): [DeclRefExpr] hasher +#-----| getArgument(0): [Argument] : discriminator +#-----| getExpr(): [DeclRefExpr] discriminator +#-----| getMember(5): [ConcreteVarDecl] hashValue +#-----| Type = Int +#-----| getAccessorDecl(0): [AccessorDecl] get +#-----| InterfaceType = (AnError) -> () -> Int +#-----| getSelfParam(): [ParamDecl] self +#-----| Type = AnError +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [CallExpr] call to _hashValue(for:) +#-----| getFunction(): [DeclRefExpr] _hashValue(for:) +#-----| getArgument(0): [Argument] for: self +#-----| getExpr(): [DeclRefExpr] self # 14| [ConcreteFuncDecl] failure(_:) # 14| InterfaceType = (Int) throws -> () # 14| getParam(0): [ParamDecl] x @@ -3783,6 +4379,9 @@ expressions.swift: # 51| getSelfParam(): [ParamDecl] self # 51| Type = S # 51| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self # 50| getMember(2): [ConstructorDecl] init(x:) # 50| InterfaceType = (S.Type) -> (Int) -> S # 50| getSelfParam(): [ParamDecl] self @@ -3872,6 +4471,9 @@ expressions.swift: # 71| getSelfParam(): [ParamDecl] self # 71| Type = Base # 71| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .xx +#-----| getBase(): [DeclRefExpr] self # 72| getMember(2): [ConstructorDecl] init(x:) # 72| InterfaceType = (Base.Type) -> (Int) -> Base # 72| getSelfParam(): [ParamDecl] self @@ -3908,23 +4510,27 @@ expressions.swift: # 77| InterfaceType = (Derived.Type) -> (Int) -> Derived # 77| getSelfParam(): [ParamDecl] self # 77| Type = Derived +#-----| getParam(0): [ParamDecl] x +#-----| Type = Int +#-----| getBody(): [BraceStmt] { ... } +# 77| getElement(0): [CallExpr] call to _unimplementedInitializer(className:initName:file:line:column:) +# 77| getFunction(): [DeclRefExpr] _unimplementedInitializer(className:initName:file:line:column:) +# 77| getArgument(0): [Argument] : expressions.Derived +# 77| getExpr(): [StringLiteralExpr] expressions.Derived +# 77| getArgument(1): [Argument] : #... +# 77| getExpr(): [MagicIdentifierLiteralExpr] #... +# 77| getArgument(2): [Argument] : #... +# 77| getExpr(): [MagicIdentifierLiteralExpr] #... +# 77| getArgument(3): [Argument] : #... +# 77| getExpr(): [MagicIdentifierLiteralExpr] #... +# 77| getArgument(4): [Argument] : #... +# 77| getExpr(): [MagicIdentifierLiteralExpr] #... +#-----| getElement(1): [ReturnStmt] return # 77| getMember(2): [DestructorDecl] deinit() # 77| InterfaceType = (Derived) -> () -> () # 77| getSelfParam(): [ParamDecl] self # 77| Type = Derived # 77| getBody(): [BraceStmt] { ... } -# 77| [CallExpr] call to _unimplementedInitializer(className:initName:file:line:column:) -# 77| getFunction(): [DeclRefExpr] _unimplementedInitializer(className:initName:file:line:column:) -# 77| getArgument(0): [Argument] : expressions.Derived -# 77| getExpr(): [StringLiteralExpr] expressions.Derived -# 77| getArgument(1): [Argument] : #... -# 77| getExpr(): [MagicIdentifierLiteralExpr] #... -# 77| getArgument(2): [Argument] : #... -# 77| getExpr(): [MagicIdentifierLiteralExpr] #... -# 77| getArgument(3): [Argument] : #... -# 77| getExpr(): [MagicIdentifierLiteralExpr] #... -# 77| getArgument(4): [Argument] : #... -# 77| getExpr(): [MagicIdentifierLiteralExpr] #... # 83| [TopLevelCodeDecl] { ... } # 83| getBody(): [BraceStmt] { ... } # 83| getElement(0): [PatternBindingDecl] var ... = ... @@ -3947,6 +4553,7 @@ expressions.swift: # 86| [TopLevelCodeDecl] { ... } # 86| getBody(): [BraceStmt] { ... } # 86| getElement(0): [PatternBindingDecl] var ... = ... +#-----| getInit(0): [NilLiteralExpr] nil # 86| getPattern(0): [TypedPattern] ... as ... # 86| getSubPattern(): [NamedPattern] opt # 86| getTypeRepr(): [TypeRepr] Int? @@ -4030,6 +4637,9 @@ expressions.swift: # 96| Type = HasProperty # 96| getBody(): [BraceStmt] { ... } # 96| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .settableField +#-----| getBase(): [DeclRefExpr] self # 105| getMember(2): [PatternBindingDecl] var ... = ... # 105| getPattern(0): [TypedPattern] ... as ... # 105| getSubPattern(): [NamedPattern] readOnlyField1 @@ -4067,6 +4677,9 @@ expressions.swift: # 116| getSelfParam(): [ParamDecl] self # 116| Type = HasProperty # 116| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .normalField +#-----| getBase(): [DeclRefExpr] self # 116| getAccessorDecl(1): [AccessorDecl] set # 116| InterfaceType = (inout HasProperty) -> (Int) -> () # 116| getSelfParam(): [ParamDecl] self @@ -4074,12 +4687,19 @@ expressions.swift: # 116| getParam(0): [ParamDecl] value # 116| Type = Int # 116| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .normalField +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 116| getAccessorDecl(2): [AccessorDecl] (unnamed function decl) # 116| InterfaceType = (inout HasProperty) -> () -> () # 116| getSelfParam(): [ParamDecl] self # 116| Type = HasProperty # 116| getBody(): [BraceStmt] { ... } # 116| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .normalField +#-----| getBase(): [DeclRefExpr] self # 118| getMember(8): [SubscriptDecl] subscript ... # 119| getAccessorDecl(0): [AccessorDecl] get # 119| InterfaceType = (HasProperty) -> (Int) -> Int @@ -4103,8 +4723,15 @@ expressions.swift: # 118| InterfaceType = (inout HasProperty) -> (Int) -> () # 118| getSelfParam(): [ParamDecl] self # 118| Type = HasProperty +#-----| getParam(0): [ParamDecl] x +#-----| Type = Int # 118| getBody(): [BraceStmt] { ... } # 118| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [SubscriptExpr] ...[...] +#-----| getBase(): [DeclRefExpr] self +#-----| getArgument(0): [Argument] : x +#-----| getExpr(): [DeclRefExpr] x # 118| getParam(0): [ParamDecl] x # 118| Type = Int # 125| getMember(9): [SubscriptDecl] subscript ... @@ -4203,6 +4830,9 @@ expressions.swift: # 142| getSelfParam(): [ParamDecl] self # 142| Type = B # 142| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self # 142| getAccessorDecl(1): [AccessorDecl] set # 142| InterfaceType = (inout B) -> (Int) -> () # 142| getSelfParam(): [ParamDecl] self @@ -4210,12 +4840,19 @@ expressions.swift: # 142| getParam(0): [ParamDecl] value # 142| Type = Int # 142| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 142| getAccessorDecl(2): [AccessorDecl] (unnamed function decl) # 142| InterfaceType = (inout B) -> () -> () # 142| getSelfParam(): [ParamDecl] self # 142| Type = B # 142| getBody(): [BraceStmt] { ... } # 142| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self # 141| getMember(2): [ConstructorDecl] init(x:) # 141| InterfaceType = (B.Type) -> (Int) -> B # 141| getSelfParam(): [ParamDecl] self @@ -4234,6 +4871,9 @@ expressions.swift: # 146| getSelfParam(): [ParamDecl] self # 146| Type = A # 146| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .b +#-----| getBase(): [DeclRefExpr] self # 146| getAccessorDecl(1): [AccessorDecl] set # 146| InterfaceType = (inout A) -> (B) -> () # 146| getSelfParam(): [ParamDecl] self @@ -4241,12 +4881,19 @@ expressions.swift: # 146| getParam(0): [ParamDecl] value # 146| Type = B # 146| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .b +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 146| getAccessorDecl(2): [AccessorDecl] (unnamed function decl) # 146| InterfaceType = (inout A) -> () -> () # 146| getSelfParam(): [ParamDecl] self # 146| Type = A # 146| getBody(): [BraceStmt] { ... } # 146| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .b +#-----| getBase(): [DeclRefExpr] self # 147| getMember(2): [PatternBindingDecl] var ... = ... # 147| getPattern(0): [TypedPattern] ... as ... # 147| getSubPattern(): [NamedPattern] bs @@ -4258,6 +4905,9 @@ expressions.swift: # 147| getSelfParam(): [ParamDecl] self # 147| Type = A # 147| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .bs +#-----| getBase(): [DeclRefExpr] self # 147| getAccessorDecl(1): [AccessorDecl] set # 147| InterfaceType = (inout A) -> ([B]) -> () # 147| getSelfParam(): [ParamDecl] self @@ -4265,13 +4915,21 @@ expressions.swift: # 147| getParam(0): [ParamDecl] value # 147| Type = [B] # 147| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .bs +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 147| getAccessorDecl(2): [AccessorDecl] (unnamed function decl) # 147| InterfaceType = (inout A) -> () -> () # 147| getSelfParam(): [ParamDecl] self # 147| Type = A # 147| getBody(): [BraceStmt] { ... } # 147| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .bs +#-----| getBase(): [DeclRefExpr] self # 148| getMember(4): [PatternBindingDecl] var ... = ... +#-----| getInit(0): [NilLiteralExpr] nil # 148| getPattern(0): [TypedPattern] ... as ... # 148| getSubPattern(): [NamedPattern] mayB # 148| getTypeRepr(): [TypeRepr] B? @@ -4282,6 +4940,9 @@ expressions.swift: # 148| getSelfParam(): [ParamDecl] self # 148| Type = A # 148| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .mayB +#-----| getBase(): [DeclRefExpr] self # 148| getAccessorDecl(1): [AccessorDecl] set # 148| InterfaceType = (inout A) -> (B?) -> () # 148| getSelfParam(): [ParamDecl] self @@ -4289,12 +4950,19 @@ expressions.swift: # 148| getParam(0): [ParamDecl] value # 148| Type = B? # 148| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .mayB +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 148| getAccessorDecl(2): [AccessorDecl] (unnamed function decl) # 148| InterfaceType = (inout A) -> () -> () # 148| getSelfParam(): [ParamDecl] self # 148| Type = A # 148| getBody(): [BraceStmt] { ... } # 148| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .mayB +#-----| getBase(): [DeclRefExpr] self # 145| getMember(6): [ConstructorDecl] init(b:bs:mayB:) # 145| InterfaceType = (A.Type) -> (B, [B], B?) -> A # 145| getSelfParam(): [ParamDecl] self @@ -4742,6 +5410,86 @@ statements.swift: # 34| [EnumDecl] AnError # 35| getMember(0): [EnumCaseDecl] case ... # 35| getMember(1): [EnumElementDecl] failed +#-----| getMember(2): [ConcreteFuncDecl] __derived_enum_equals(_:_:) +#-----| InterfaceType = (AnError.Type) -> (AnError, AnError) -> Bool +#-----| getSelfParam(): [ParamDecl] self +#-----| Type = AnError.Type +#-----| getParam(0): [ParamDecl] a +#-----| Type = AnError +#-----| getParam(1): [ParamDecl] b +#-----| Type = AnError +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [PatternBindingDecl] var ... = ... +#-----| getPattern(0): [TypedPattern] ... as ... +#-----| getSubPattern(): [NamedPattern] index_a +#-----| getElement(1): [SwitchStmt] switch a { ... } +#-----| getExpr(): [DeclRefExpr] a +#-----| getCase(0): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] index_a +#-----| getSource(): [IntegerLiteralExpr] 0 +#-----| getLabel(0): [CaseLabelItem] .failed +#-----| getPattern(): [EnumElementPattern] .failed +#-----| getElement(2): [PatternBindingDecl] var ... = ... +#-----| getPattern(0): [TypedPattern] ... as ... +#-----| getSubPattern(): [NamedPattern] index_b +#-----| getElement(3): [SwitchStmt] switch b { ... } +#-----| getExpr(): [DeclRefExpr] b +#-----| getCase(0): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] index_b +#-----| getSource(): [IntegerLiteralExpr] 0 +#-----| getLabel(0): [CaseLabelItem] .failed +#-----| getPattern(): [EnumElementPattern] .failed +#-----| getElement(4): [ReturnStmt] return ... +#-----| getResult(): [BinaryExpr] ... .==(_:_:) ... +#-----| getFunction(): [MethodRefExpr] .==(_:_:) +#-----| getBase(): [TypeExpr] Int.Type +#-----| getArgument(0): [Argument] : index_a +#-----| getExpr(): [DeclRefExpr] index_a +#-----| getArgument(1): [Argument] : index_b +#-----| getExpr(): [DeclRefExpr] index_b +#-----| getMember(3): [PatternBindingDecl] var ... = ... +#-----| getPattern(0): [TypedPattern] ... as ... +#-----| getSubPattern(): [NamedPattern] hashValue +#-----| getMember(4): [ConcreteFuncDecl] hash(into:) +#-----| InterfaceType = (AnError) -> (inout Hasher) -> () +#-----| getSelfParam(): [ParamDecl] self +#-----| Type = AnError +#-----| getParam(0): [ParamDecl] hasher +#-----| Type = Hasher +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [PatternBindingDecl] var ... = ... +#-----| getPattern(0): [TypedPattern] ... as ... +#-----| getSubPattern(): [NamedPattern] discriminator +#-----| getElement(1): [SwitchStmt] switch self { ... } +#-----| getExpr(): [DeclRefExpr] self +#-----| getCase(0): [CaseStmt] case ... +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [DeclRefExpr] discriminator +#-----| getSource(): [IntegerLiteralExpr] 0 +#-----| getLabel(0): [CaseLabelItem] .failed +#-----| getPattern(): [EnumElementPattern] .failed +#-----| getElement(2): [CallExpr] call to ... +#-----| getFunction(): [UnresolvedDotExpr] ... .combine(_:) +#-----| getBase(): [DeclRefExpr] hasher +#-----| getArgument(0): [Argument] : discriminator +#-----| getExpr(): [DeclRefExpr] discriminator +#-----| getMember(5): [ConcreteVarDecl] hashValue +#-----| Type = Int +#-----| getAccessorDecl(0): [AccessorDecl] get +#-----| InterfaceType = (AnError) -> () -> Int +#-----| getSelfParam(): [ParamDecl] self +#-----| Type = AnError +#-----| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [CallExpr] call to _hashValue(for:) +#-----| getFunction(): [DeclRefExpr] _hashValue(for:) +#-----| getArgument(0): [Argument] for: self +#-----| getExpr(): [DeclRefExpr] self # 38| [ConcreteFuncDecl] failure(_:) # 38| InterfaceType = (Int) throws -> () # 38| getParam(0): [ParamDecl] x @@ -4932,6 +5680,9 @@ statements.swift: # 75| getSelfParam(): [ParamDecl] self # 75| Type = HasModifyAccessorDecl # 75| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [ReturnStmt] return ... +#-----| getResult(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self # 75| getAccessorDecl(1): [AccessorDecl] set # 75| InterfaceType = (inout HasModifyAccessorDecl) -> (Int) -> () # 75| getSelfParam(): [ParamDecl] self @@ -4939,12 +5690,19 @@ statements.swift: # 75| getParam(0): [ParamDecl] value # 75| Type = Int # 75| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 75| getAccessorDecl(2): [AccessorDecl] (unnamed function decl) # 75| InterfaceType = (inout HasModifyAccessorDecl) -> () -> () # 75| getSelfParam(): [ParamDecl] self # 75| Type = HasModifyAccessorDecl # 75| getBody(): [BraceStmt] { ... } # 75| getElement(0): [YieldStmt] yield ... +#-----| getResult(0): [InOutExpr] &... +#-----| getSubExpr(): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self # 76| getMember(2): [PatternBindingDecl] var ... = ... # 76| getPattern(0): [TypedPattern] ... as ... # 76| getSubPattern(): [NamedPattern] hasModify @@ -4974,6 +5732,10 @@ statements.swift: # 76| getParam(0): [ParamDecl] value # 76| Type = Int # 76| getBody(): [BraceStmt] { ... } +#-----| getElement(0): [AssignExpr] ... = ... +#-----| getDest(): [MemberRefExpr] .hasModify +#-----| getBase(): [DeclRefExpr] self +#-----| getSource(): [DeclRefExpr] value # 74| getMember(4): [ConstructorDecl] init(x:) # 74| InterfaceType = (HasModifyAccessorDecl.Type) -> (Int) -> HasModifyAccessorDecl # 74| getSelfParam(): [ParamDecl] self From 41977d1dbb764c43ac1e64d1530806c2dbb3f488 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 11 Nov 2022 12:17:07 +0100 Subject: [PATCH 0189/1420] Swift: implement extraction of new properties --- .../extractor/translators/DeclTranslator.cpp | 16 ++++++++++++++ .../test/library-tests/ast/PrintAst.expected | 21 ++++++++++--------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/swift/extractor/translators/DeclTranslator.cpp b/swift/extractor/translators/DeclTranslator.cpp index 9e9b6009703..2482499a32c 100644 --- a/swift/extractor/translators/DeclTranslator.cpp +++ b/swift/extractor/translators/DeclTranslator.cpp @@ -3,6 +3,7 @@ #include #include #include "swift/extractor/infra/SwiftDiagnosticKind.h" +#include "swift/AST/PropertyWrappers.h" namespace codeql { namespace { @@ -85,6 +86,11 @@ std::optional DeclTranslator::translateParamDecl(const swift: } fillVarDecl(decl, *entry); entry->is_inout = decl.isInOut(); + if (auto wrapped = decl.getPropertyWrapperWrappedValueVar()) { + entry->property_wrapper_local_wrapped_var = dispatcher.fetchLabel(wrapped); + entry->property_wrapper_local_wrapped_var_binding = + dispatcher.fetchLabel(wrapped->getParentPatternBinding()); + } return entry; } @@ -355,6 +361,16 @@ void DeclTranslator::fillVarDecl(const swift::VarDecl& decl, codeql::VarDecl& en dispatcher.fetchOptionalLabel(decl.getPropertyWrapperBackingPropertyType()); } fillAbstractStorageDecl(decl, entry); + if (auto backing = decl.getPropertyWrapperBackingProperty()) { + entry.property_wrapper_backing_var = dispatcher.fetchLabel(backing); + entry.property_wrapper_backing_var_binding = + dispatcher.fetchLabel(backing->getParentPatternBinding()); + } + if (auto projection = decl.getPropertyWrapperProjectionVar()) { + entry.property_wrapper_projection_var = dispatcher.fetchLabel(projection); + entry.property_wrapper_projection_var_binding = + dispatcher.fetchLabel(projection->getParentPatternBinding()); + } } void DeclTranslator::fillNominalTypeDecl(const swift::NominalTypeDecl& decl, diff --git a/swift/ql/test/library-tests/ast/PrintAst.expected b/swift/ql/test/library-tests/ast/PrintAst.expected index 4fc35515561..2397614f5fe 100644 --- a/swift/ql/test/library-tests/ast/PrintAst.expected +++ b/swift/ql/test/library-tests/ast/PrintAst.expected @@ -3469,18 +3469,19 @@ declarations.swift: #-----| getElement(0): [ReturnStmt] return ... #-----| getResult(): [MemberRefExpr] .wrappedValue #-----| getBase(): [DeclRefExpr] _x +#-----| getPropertyWrapperBackingVarBinding(): [PatternBindingDecl] var ... = ... +# 77| getInit(0): [CallExpr] call to init() +# 77| getFunction(): [ConstructorRefCallExpr] call to init() +# 77| getFunction(): [DeclRefExpr] init() +# 77| getArgument(0): [Argument] : ZeroWrapper.Type +# 77| getExpr(): [TypeExpr] ZeroWrapper.Type +# 77| getTypeRepr(): [TypeRepr] ZeroWrapper +# 77| getPattern(0): [TypedPattern] ... as ... +# 77| getSubPattern(): [NamedPattern] _x +# 77| getPropertyWrapperBackingVar(): [ConcreteVarDecl] _x +# 77| Type = ZeroWrapper # 78| getElement(2): [ReturnStmt] return ... # 78| getResult(): [DeclRefExpr] x -# 77| [CallExpr] call to init() -# 77| getFunction(): [ConstructorRefCallExpr] call to init() -# 77| getFunction(): [DeclRefExpr] init() -# 77| getArgument(0): [Argument] : ZeroWrapper.Type -# 77| getExpr(): [TypeExpr] ZeroWrapper.Type -# 77| getTypeRepr(): [TypeRepr] ZeroWrapper -# 77| [TypedPattern] ... as ... -# 77| getSubPattern(): [NamedPattern] _x -# 77| [ConcreteVarDecl] _x -# 77| Type = ZeroWrapper # 81| [StructDecl] HasPropertyAndObserver # 82| getMember(0): [PatternBindingDecl] var ... = ... # 82| getPattern(0): [TypedPattern] ... as ... From 3bb5505063726167fbbf0a44ee69ad8fd73e7aa9 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 11 Nov 2022 12:41:00 +0100 Subject: [PATCH 0190/1420] Swift: expand and fix `VarDecl` tests --- .../ConcreteVarDecl/ConcreteVarDecl.expected | 15 +++++++ .../ConcreteVarDecl_getAccessorDecl.expected | 27 +++++++++++ ...cl_getAttachedPropertyWrapperType.expected | 4 ++ ...creteVarDecl_getParentInitializer.expected | 10 +++++ .../ConcreteVarDecl_getParentPattern.expected | 15 +++++++ ...Decl_getPropertyWrapperBackingVar.expected | 5 +++ ...tPropertyWrapperBackingVarBinding.expected | 5 +++ ...l_getPropertyWrapperProjectionVar.expected | 2 + ...opertyWrapperProjectionVarBinding.expected | 2 + .../decl/ConcreteVarDecl/var_decls.swift | 33 ++++++++++++++ .../decl/ParamDecl/ParamDecl.expected | 45 +++++++++++++++++++ ...cl_getAttachedPropertyWrapperType.expected | 4 ++ ...Decl_getPropertyWrapperBackingVar.expected | 4 ++ ...tPropertyWrapperBackingVarBinding.expected | 2 + ...getPropertyWrapperLocalWrappedVar.expected | 4 ++ ...ertyWrapperLocalWrappedVarBinding.expected | 0 ...l_getPropertyWrapperProjectionVar.expected | 2 + ...opertyWrapperProjectionVarBinding.expected | 2 + .../decl/ParamDecl/param_decls.swift | 37 +++++++++++++++ 19 files changed, 218 insertions(+) create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperBackingVar.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperBackingVarBinding.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperProjectionVar.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperProjectionVarBinding.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperBackingVar.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperBackingVarBinding.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperLocalWrappedVar.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperLocalWrappedVarBinding.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperProjectionVar.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperProjectionVarBinding.expected diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl.expected index 7cf37701b0c..80ea4e75e95 100644 --- a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl.expected +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl.expected @@ -5,3 +5,18 @@ | var_decls.swift:20:7:20:7 | wrappedValue | getModule: | file://:0:0:0:0 | var_decls | getInterfaceType: | Int | getName: | wrappedValue | getType: | Int | getIntroducerInt: | 1 | | var_decls.swift:24:15:24:15 | _wrapped | getModule: | file://:0:0:0:0 | var_decls | getInterfaceType: | X | getName: | _wrapped | getType: | X | getIntroducerInt: | 1 | | var_decls.swift:24:15:24:15 | wrapped | getModule: | file://:0:0:0:0 | var_decls | getInterfaceType: | Int | getName: | wrapped | getType: | Int | getIntroducerInt: | 1 | +| var_decls.swift:28:7:28:7 | wrappedValue | getModule: | file://:0:0:0:0 | var_decls | getInterfaceType: | Int | getName: | wrappedValue | getType: | Int | getIntroducerInt: | 1 | +| var_decls.swift:34:7:34:7 | wrappedValue | getModule: | file://:0:0:0:0 | var_decls | getInterfaceType: | Int | getName: | wrappedValue | getType: | Int | getIntroducerInt: | 1 | +| var_decls.swift:35:7:35:7 | projectedValue | getModule: | file://:0:0:0:0 | var_decls | getInterfaceType: | Bool | getName: | projectedValue | getType: | Bool | getIntroducerInt: | 1 | +| var_decls.swift:39:7:39:7 | wrappedValue | getModule: | file://:0:0:0:0 | var_decls | getInterfaceType: | Int | getName: | wrappedValue | getType: | Int | getIntroducerInt: | 1 | +| var_decls.swift:40:7:40:7 | projectedValue | getModule: | file://:0:0:0:0 | var_decls | getInterfaceType: | Bool | getName: | projectedValue | getType: | Bool | getIntroducerInt: | 1 | +| var_decls.swift:54:10:54:10 | _w1 | getModule: | file://:0:0:0:0 | var_decls | getInterfaceType: | X | getName: | _w1 | getType: | X | getIntroducerInt: | 1 | +| var_decls.swift:54:10:54:10 | w1 | getModule: | file://:0:0:0:0 | var_decls | getInterfaceType: | Int | getName: | w1 | getType: | Int | getIntroducerInt: | 1 | +| var_decls.swift:55:24:55:24 | _w2 | getModule: | file://:0:0:0:0 | var_decls | getInterfaceType: | WrapperWithInit | getName: | _w2 | getType: | WrapperWithInit | getIntroducerInt: | 1 | +| var_decls.swift:55:24:55:24 | w2 | getModule: | file://:0:0:0:0 | var_decls | getInterfaceType: | Int | getName: | w2 | getType: | Int | getIntroducerInt: | 1 | +| var_decls.swift:56:29:56:29 | $w3 | getModule: | file://:0:0:0:0 | var_decls | getInterfaceType: | Bool | getName: | $w3 | getType: | Bool | getIntroducerInt: | 1 | +| var_decls.swift:56:29:56:29 | _w3 | getModule: | file://:0:0:0:0 | var_decls | getInterfaceType: | WrapperWithProjected | getName: | _w3 | getType: | WrapperWithProjected | getIntroducerInt: | 1 | +| var_decls.swift:56:29:56:29 | w3 | getModule: | file://:0:0:0:0 | var_decls | getInterfaceType: | Int | getName: | w3 | getType: | Int | getIntroducerInt: | 1 | +| var_decls.swift:57:36:57:36 | $w4 | getModule: | file://:0:0:0:0 | var_decls | getInterfaceType: | Bool | getName: | $w4 | getType: | Bool | getIntroducerInt: | 1 | +| var_decls.swift:57:36:57:36 | _w4 | getModule: | file://:0:0:0:0 | var_decls | getInterfaceType: | WrapperWithProjectedAndInit | getName: | _w4 | getType: | WrapperWithProjectedAndInit | getIntroducerInt: | 1 | +| var_decls.swift:57:36:57:36 | w4 | getModule: | file://:0:0:0:0 | var_decls | getInterfaceType: | Int | getName: | w4 | getType: | Int | getIntroducerInt: | 1 | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAccessorDecl.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAccessorDecl.expected index cfbce6fe5ef..08aff3c9809 100644 --- a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAccessorDecl.expected +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAccessorDecl.expected @@ -11,3 +11,30 @@ | var_decls.swift:24:15:24:15 | wrapped | 0 | var_decls.swift:24:15:24:15 | get | | var_decls.swift:24:15:24:15 | wrapped | 1 | var_decls.swift:24:15:24:15 | set | | var_decls.swift:24:15:24:15 | wrapped | 2 | var_decls.swift:24:15:24:15 | (unnamed function decl) | +| var_decls.swift:28:7:28:7 | wrappedValue | 0 | var_decls.swift:28:7:28:7 | get | +| var_decls.swift:28:7:28:7 | wrappedValue | 1 | var_decls.swift:28:7:28:7 | set | +| var_decls.swift:28:7:28:7 | wrappedValue | 2 | var_decls.swift:28:7:28:7 | (unnamed function decl) | +| var_decls.swift:34:7:34:7 | wrappedValue | 0 | var_decls.swift:34:7:34:7 | get | +| var_decls.swift:34:7:34:7 | wrappedValue | 1 | var_decls.swift:34:7:34:7 | set | +| var_decls.swift:34:7:34:7 | wrappedValue | 2 | var_decls.swift:34:7:34:7 | (unnamed function decl) | +| var_decls.swift:35:7:35:7 | projectedValue | 0 | var_decls.swift:35:7:35:7 | get | +| var_decls.swift:35:7:35:7 | projectedValue | 1 | var_decls.swift:35:7:35:7 | set | +| var_decls.swift:35:7:35:7 | projectedValue | 2 | var_decls.swift:35:7:35:7 | (unnamed function decl) | +| var_decls.swift:39:7:39:7 | wrappedValue | 0 | var_decls.swift:39:7:39:7 | get | +| var_decls.swift:39:7:39:7 | wrappedValue | 1 | var_decls.swift:39:7:39:7 | set | +| var_decls.swift:39:7:39:7 | wrappedValue | 2 | var_decls.swift:39:7:39:7 | (unnamed function decl) | +| var_decls.swift:40:7:40:7 | projectedValue | 0 | var_decls.swift:40:7:40:7 | get | +| var_decls.swift:40:7:40:7 | projectedValue | 1 | var_decls.swift:40:7:40:7 | set | +| var_decls.swift:40:7:40:7 | projectedValue | 2 | var_decls.swift:40:7:40:7 | (unnamed function decl) | +| var_decls.swift:54:10:54:10 | w1 | 0 | var_decls.swift:54:10:54:10 | get | +| var_decls.swift:54:10:54:10 | w1 | 1 | var_decls.swift:54:10:54:10 | set | +| var_decls.swift:55:24:55:24 | w2 | 0 | var_decls.swift:55:24:55:24 | get | +| var_decls.swift:55:24:55:24 | w2 | 1 | var_decls.swift:55:24:55:24 | set | +| var_decls.swift:56:29:56:29 | $w3 | 0 | var_decls.swift:56:29:56:29 | get | +| var_decls.swift:56:29:56:29 | $w3 | 1 | var_decls.swift:56:29:56:29 | set | +| var_decls.swift:56:29:56:29 | w3 | 0 | var_decls.swift:56:29:56:29 | get | +| var_decls.swift:56:29:56:29 | w3 | 1 | var_decls.swift:56:29:56:29 | set | +| var_decls.swift:57:36:57:36 | $w4 | 0 | var_decls.swift:57:36:57:36 | get | +| var_decls.swift:57:36:57:36 | $w4 | 1 | var_decls.swift:57:36:57:36 | set | +| var_decls.swift:57:36:57:36 | w4 | 0 | var_decls.swift:57:36:57:36 | get | +| var_decls.swift:57:36:57:36 | w4 | 1 | var_decls.swift:57:36:57:36 | set | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAttachedPropertyWrapperType.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAttachedPropertyWrapperType.expected index 67bff5661b0..0212cc38f14 100644 --- a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAttachedPropertyWrapperType.expected +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAttachedPropertyWrapperType.expected @@ -1 +1,5 @@ | var_decls.swift:24:15:24:15 | wrapped | X | +| var_decls.swift:54:10:54:10 | w1 | X | +| var_decls.swift:55:24:55:24 | w2 | WrapperWithInit | +| var_decls.swift:56:29:56:29 | w3 | WrapperWithProjected | +| var_decls.swift:57:36:57:36 | w4 | WrapperWithProjectedAndInit | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentInitializer.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentInitializer.expected index 786b16fcab1..3f07cd825e9 100644 --- a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentInitializer.expected +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentInitializer.expected @@ -1,3 +1,13 @@ | var_decls.swift:4:7:4:7 | i | var_decls.swift:4:11:4:11 | 0 | | var_decls.swift:7:5:7:5 | numbers | var_decls.swift:7:15:7:18 | [...] | | var_decls.swift:10:12:10:12 | numbers | var_decls.swift:10:22:10:35 | [...] | +| var_decls.swift:34:7:34:7 | wrappedValue | var_decls.swift:34:28:34:28 | 42 | +| var_decls.swift:35:7:35:7 | projectedValue | var_decls.swift:35:31:35:31 | false | +| var_decls.swift:54:10:54:10 | _w1 | var_decls.swift:54:4:54:15 | call to init(wrappedValue:) | +| var_decls.swift:54:10:54:10 | w1 | var_decls.swift:54:4:54:15 | call to init(wrappedValue:) | +| var_decls.swift:55:24:55:24 | _w2 | var_decls.swift:55:4:55:29 | call to init(wrappedValue:) | +| var_decls.swift:55:24:55:24 | w2 | var_decls.swift:55:4:55:29 | call to init(wrappedValue:) | +| var_decls.swift:56:29:56:29 | _w3 | var_decls.swift:56:4:56:34 | call to init(wrappedValue:projectedValue:) | +| var_decls.swift:56:29:56:29 | w3 | var_decls.swift:56:4:56:34 | call to init(wrappedValue:projectedValue:) | +| var_decls.swift:57:36:57:36 | _w4 | var_decls.swift:57:4:57:41 | call to init(wrappedValue:) | +| var_decls.swift:57:36:57:36 | w4 | var_decls.swift:57:4:57:41 | call to init(wrappedValue:) | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentPattern.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentPattern.expected index 8e15539394a..67df53f0cce 100644 --- a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentPattern.expected +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentPattern.expected @@ -5,3 +5,18 @@ | var_decls.swift:20:7:20:7 | wrappedValue | var_decls.swift:20:7:20:21 | ... as ... | | var_decls.swift:24:15:24:15 | _wrapped | var_decls.swift:24:15:24:15 | ... as ... | | var_decls.swift:24:15:24:15 | wrapped | var_decls.swift:24:15:24:25 | ... as ... | +| var_decls.swift:28:7:28:7 | wrappedValue | var_decls.swift:28:7:28:22 | ... as ... | +| var_decls.swift:34:7:34:7 | wrappedValue | var_decls.swift:34:7:34:22 | ... as ... | +| var_decls.swift:35:7:35:7 | projectedValue | var_decls.swift:35:7:35:24 | ... as ... | +| var_decls.swift:39:7:39:7 | wrappedValue | var_decls.swift:39:7:39:22 | ... as ... | +| var_decls.swift:40:7:40:7 | projectedValue | var_decls.swift:40:7:40:24 | ... as ... | +| var_decls.swift:54:10:54:10 | _w1 | var_decls.swift:54:10:54:10 | ... as ... | +| var_decls.swift:54:10:54:10 | w1 | var_decls.swift:54:10:54:10 | w1 | +| var_decls.swift:55:24:55:24 | _w2 | var_decls.swift:55:24:55:24 | ... as ... | +| var_decls.swift:55:24:55:24 | w2 | var_decls.swift:55:24:55:24 | w2 | +| var_decls.swift:56:29:56:29 | $w3 | var_decls.swift:56:29:56:29 | ... as ... | +| var_decls.swift:56:29:56:29 | _w3 | var_decls.swift:56:29:56:29 | ... as ... | +| var_decls.swift:56:29:56:29 | w3 | var_decls.swift:56:29:56:29 | w3 | +| var_decls.swift:57:36:57:36 | $w4 | var_decls.swift:57:36:57:36 | ... as ... | +| var_decls.swift:57:36:57:36 | _w4 | var_decls.swift:57:36:57:36 | ... as ... | +| var_decls.swift:57:36:57:36 | w4 | var_decls.swift:57:36:57:36 | w4 | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperBackingVar.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperBackingVar.expected new file mode 100644 index 00000000000..b32567e0cbb --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperBackingVar.expected @@ -0,0 +1,5 @@ +| var_decls.swift:24:15:24:15 | wrapped | var_decls.swift:24:15:24:15 | _wrapped | +| var_decls.swift:54:10:54:10 | w1 | var_decls.swift:54:10:54:10 | _w1 | +| var_decls.swift:55:24:55:24 | w2 | var_decls.swift:55:24:55:24 | _w2 | +| var_decls.swift:56:29:56:29 | w3 | var_decls.swift:56:29:56:29 | _w3 | +| var_decls.swift:57:36:57:36 | w4 | var_decls.swift:57:36:57:36 | _w4 | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperBackingVarBinding.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperBackingVarBinding.expected new file mode 100644 index 00000000000..d6069ccf602 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperBackingVarBinding.expected @@ -0,0 +1,5 @@ +| var_decls.swift:24:15:24:15 | wrapped | file://:0:0:0:0 | var ... = ... | +| var_decls.swift:54:10:54:10 | w1 | file://:0:0:0:0 | var ... = ... | +| var_decls.swift:55:24:55:24 | w2 | file://:0:0:0:0 | var ... = ... | +| var_decls.swift:56:29:56:29 | w3 | file://:0:0:0:0 | var ... = ... | +| var_decls.swift:57:36:57:36 | w4 | file://:0:0:0:0 | var ... = ... | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperProjectionVar.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperProjectionVar.expected new file mode 100644 index 00000000000..720938bab9b --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperProjectionVar.expected @@ -0,0 +1,2 @@ +| var_decls.swift:56:29:56:29 | w3 | var_decls.swift:56:29:56:29 | $w3 | +| var_decls.swift:57:36:57:36 | w4 | var_decls.swift:57:36:57:36 | $w4 | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperProjectionVarBinding.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperProjectionVarBinding.expected new file mode 100644 index 00000000000..cb47a922746 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getPropertyWrapperProjectionVarBinding.expected @@ -0,0 +1,2 @@ +| var_decls.swift:56:29:56:29 | w3 | file://:0:0:0:0 | var ... = ... | +| var_decls.swift:57:36:57:36 | w4 | file://:0:0:0:0 | var ... = ... | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/var_decls.swift b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/var_decls.swift index 37c145a5a42..0bb5dc93edd 100644 --- a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/var_decls.swift +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/var_decls.swift @@ -23,3 +23,36 @@ struct Y { struct Wrapped { @X @Y var wrapped : Int } + +@propertyWrapper struct WrapperWithInit { + var wrappedValue : Int + + init(wrappedValue: Int) { self.wrappedValue = wrappedValue } +} + +@propertyWrapper struct WrapperWithProjected { + var wrappedValue : Int = 42 + var projectedValue : Bool = false +} + +@propertyWrapper struct WrapperWithProjectedAndInit { + var wrappedValue : Int + var projectedValue : Bool + + init(wrappedValue: Int) { + self.wrappedValue = wrappedValue + self.projectedValue = false + } + + init(projectedValue: Bool) { + self.wrappedValue = 0 + self.projectedValue = projectedValue + } +} + +func f3() { + @X var w1 = 1 + @WrapperWithInit var w2 = 2 + @WrapperWithProjected var w3 = 3 + @WrapperWithProjectedAndInit var w4 = 4 +} diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl.expected b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl.expected index ec27bd5592c..d59229fc961 100644 --- a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl.expected +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl.expected @@ -1,3 +1,5 @@ +| file://:0:0:0:0 | x | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Int | getName: | x | getType: | Int | isInout: | no | +| file://:0:0:0:0 | y | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Int | getName: | y | getType: | Int | isInout: | no | | param_decls.swift:1:10:1:13 | _ | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Int | getName: | _ | getType: | Int | isInout: | no | | param_decls.swift:1:18:1:29 | y | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Double | getName: | y | getType: | Double | isInout: | yes | | param_decls.swift:2:10:2:13 | _ | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Int | getName: | _ | getType: | Int | isInout: | no | @@ -16,3 +18,46 @@ | param_decls.swift:12:13:12:22 | s | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | String | getName: | s | getType: | String | isInout: | yes | | param_decls.swift:13:13:13:22 | s | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | String | getName: | s | getType: | String | isInout: | yes | | param_decls.swift:14:26:14:26 | $0 | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Int | getName: | $0 | getType: | Int | isInout: | no | +| param_decls.swift:17:25:17:25 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Wrapper | getName: | self | getType: | Wrapper | isInout: | yes | +| param_decls.swift:17:25:17:25 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Wrapper | getName: | self | getType: | Wrapper | isInout: | yes | +| param_decls.swift:17:25:17:25 | wrappedValue | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Int | getName: | wrappedValue | getType: | Int | isInout: | no | +| param_decls.swift:18:9:18:9 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Wrapper | getName: | self | getType: | Wrapper | isInout: | no | +| param_decls.swift:18:9:18:9 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Wrapper | getName: | self | getType: | Wrapper | isInout: | yes | +| param_decls.swift:18:9:18:9 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Wrapper | getName: | self | getType: | Wrapper | isInout: | yes | +| param_decls.swift:18:9:18:9 | value | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Int | getName: | value | getType: | Int | isInout: | no | +| param_decls.swift:22:9:22:9 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithInit | getName: | self | getType: | WrapperWithInit | isInout: | no | +| param_decls.swift:22:9:22:9 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithInit | getName: | self | getType: | WrapperWithInit | isInout: | yes | +| param_decls.swift:22:9:22:9 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithInit | getName: | self | getType: | WrapperWithInit | isInout: | yes | +| param_decls.swift:22:9:22:9 | value | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Int | getName: | value | getType: | Int | isInout: | no | +| param_decls.swift:24:5:24:5 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithInit | getName: | self | getType: | WrapperWithInit | isInout: | yes | +| param_decls.swift:24:10:24:24 | wrappedValue | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Int | getName: | wrappedValue | getType: | Int | isInout: | no | +| param_decls.swift:27:25:27:25 | projectedValue | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Bool | getName: | projectedValue | getType: | Bool | isInout: | no | +| param_decls.swift:27:25:27:25 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithProjected | getName: | self | getType: | WrapperWithProjected | isInout: | yes | +| param_decls.swift:27:25:27:25 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithProjected | getName: | self | getType: | WrapperWithProjected | isInout: | yes | +| param_decls.swift:27:25:27:25 | wrappedValue | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Int | getName: | wrappedValue | getType: | Int | isInout: | no | +| param_decls.swift:28:9:28:9 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithProjected | getName: | self | getType: | WrapperWithProjected | isInout: | no | +| param_decls.swift:28:9:28:9 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithProjected | getName: | self | getType: | WrapperWithProjected | isInout: | yes | +| param_decls.swift:28:9:28:9 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithProjected | getName: | self | getType: | WrapperWithProjected | isInout: | yes | +| param_decls.swift:28:9:28:9 | value | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Int | getName: | value | getType: | Int | isInout: | no | +| param_decls.swift:29:9:29:9 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithProjected | getName: | self | getType: | WrapperWithProjected | isInout: | no | +| param_decls.swift:29:9:29:9 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithProjected | getName: | self | getType: | WrapperWithProjected | isInout: | yes | +| param_decls.swift:29:9:29:9 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithProjected | getName: | self | getType: | WrapperWithProjected | isInout: | yes | +| param_decls.swift:29:9:29:9 | value | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Bool | getName: | value | getType: | Bool | isInout: | no | +| param_decls.swift:33:9:33:9 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithProjectedAndInit | getName: | self | getType: | WrapperWithProjectedAndInit | isInout: | no | +| param_decls.swift:33:9:33:9 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithProjectedAndInit | getName: | self | getType: | WrapperWithProjectedAndInit | isInout: | yes | +| param_decls.swift:33:9:33:9 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithProjectedAndInit | getName: | self | getType: | WrapperWithProjectedAndInit | isInout: | yes | +| param_decls.swift:33:9:33:9 | value | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Int | getName: | value | getType: | Int | isInout: | no | +| param_decls.swift:34:9:34:9 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithProjectedAndInit | getName: | self | getType: | WrapperWithProjectedAndInit | isInout: | no | +| param_decls.swift:34:9:34:9 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithProjectedAndInit | getName: | self | getType: | WrapperWithProjectedAndInit | isInout: | yes | +| param_decls.swift:34:9:34:9 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithProjectedAndInit | getName: | self | getType: | WrapperWithProjectedAndInit | isInout: | yes | +| param_decls.swift:34:9:34:9 | value | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Bool | getName: | value | getType: | Bool | isInout: | no | +| param_decls.swift:36:5:36:5 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithProjectedAndInit | getName: | self | getType: | WrapperWithProjectedAndInit | isInout: | yes | +| param_decls.swift:36:10:36:24 | wrappedValue | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Int | getName: | wrappedValue | getType: | Int | isInout: | no | +| param_decls.swift:41:5:41:5 | self | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | WrapperWithProjectedAndInit | getName: | self | getType: | WrapperWithProjectedAndInit | isInout: | yes | +| param_decls.swift:41:10:41:26 | projectedValue | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Bool | getName: | projectedValue | getType: | Bool | isInout: | no | +| param_decls.swift:48:18:48:22 | p1 | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Int | getName: | p1 | getType: | Int | isInout: | no | +| param_decls.swift:49:26:49:30 | p2 | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Int | getName: | p2 | getType: | Int | isInout: | no | +| param_decls.swift:50:31:50:31 | value | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Bool | getName: | value | getType: | Bool | isInout: | no | +| param_decls.swift:50:31:50:35 | p3 | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Int | getName: | p3 | getType: | Int | isInout: | no | +| param_decls.swift:51:38:51:38 | value | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Bool | getName: | value | getType: | Bool | isInout: | no | +| param_decls.swift:51:38:51:42 | p4 | getModule: | file://:0:0:0:0 | param_decls | getInterfaceType: | Int | getName: | p4 | getType: | Int | isInout: | no | diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAttachedPropertyWrapperType.expected b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAttachedPropertyWrapperType.expected index e69de29bb2d..0e5bd101964 100644 --- a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAttachedPropertyWrapperType.expected +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAttachedPropertyWrapperType.expected @@ -0,0 +1,4 @@ +| param_decls.swift:48:18:48:22 | p1 | Wrapper | +| param_decls.swift:49:26:49:30 | p2 | WrapperWithInit | +| param_decls.swift:50:31:50:35 | p3 | WrapperWithProjected | +| param_decls.swift:51:38:51:42 | p4 | WrapperWithProjectedAndInit | diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperBackingVar.expected b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperBackingVar.expected new file mode 100644 index 00000000000..6595e3ae3ba --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperBackingVar.expected @@ -0,0 +1,4 @@ +| param_decls.swift:48:18:48:22 | p1 | param_decls.swift:48:18:48:18 | _p1 | +| param_decls.swift:49:26:49:30 | p2 | param_decls.swift:49:26:49:26 | _p2 | +| param_decls.swift:50:31:50:35 | p3 | file://:0:0:0:0 | _p3 | +| param_decls.swift:51:38:51:42 | p4 | file://:0:0:0:0 | _p4 | diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperBackingVarBinding.expected b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperBackingVarBinding.expected new file mode 100644 index 00000000000..b3dc664e1db --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperBackingVarBinding.expected @@ -0,0 +1,2 @@ +| param_decls.swift:48:18:48:22 | p1 | file://:0:0:0:0 | var ... = ... | +| param_decls.swift:49:26:49:30 | p2 | file://:0:0:0:0 | var ... = ... | diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperLocalWrappedVar.expected b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperLocalWrappedVar.expected new file mode 100644 index 00000000000..b58494d1f8d --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperLocalWrappedVar.expected @@ -0,0 +1,4 @@ +| param_decls.swift:48:18:48:22 | p1 | param_decls.swift:48:18:48:18 | p1 | +| param_decls.swift:49:26:49:30 | p2 | param_decls.swift:49:26:49:26 | p2 | +| param_decls.swift:50:31:50:35 | p3 | param_decls.swift:50:31:50:31 | p3 | +| param_decls.swift:51:38:51:42 | p4 | param_decls.swift:51:38:51:38 | p4 | diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperLocalWrappedVarBinding.expected b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperLocalWrappedVarBinding.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperProjectionVar.expected b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperProjectionVar.expected new file mode 100644 index 00000000000..99679a1c64b --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperProjectionVar.expected @@ -0,0 +1,2 @@ +| param_decls.swift:50:31:50:35 | p3 | param_decls.swift:50:31:50:31 | $p3 | +| param_decls.swift:51:38:51:42 | p4 | param_decls.swift:51:38:51:38 | $p4 | diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperProjectionVarBinding.expected b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperProjectionVarBinding.expected new file mode 100644 index 00000000000..0ebae7b813a --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getPropertyWrapperProjectionVarBinding.expected @@ -0,0 +1,2 @@ +| param_decls.swift:50:31:50:35 | p3 | file://:0:0:0:0 | var ... = ... | +| param_decls.swift:51:38:51:42 | p4 | file://:0:0:0:0 | var ... = ... | diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/param_decls.swift b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/param_decls.swift index 72b47ed2c60..aa001dfeca7 100644 --- a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/param_decls.swift +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/param_decls.swift @@ -13,3 +13,40 @@ func closures() { let y = {(s: inout String) -> String in ""} let z : (Int) -> Int = { $0 + 1 } } + +@propertyWrapper struct Wrapper { + var wrappedValue : Int = 42 +} + +@propertyWrapper struct WrapperWithInit { + var wrappedValue : Int + + init(wrappedValue: Int) { self.wrappedValue = wrappedValue } +} + +@propertyWrapper struct WrapperWithProjected { + var wrappedValue : Int = 42 + var projectedValue : Bool = false +} + +@propertyWrapper struct WrapperWithProjectedAndInit { + var wrappedValue : Int + var projectedValue : Bool + + init(wrappedValue: Int) { + self.wrappedValue = wrappedValue + self.projectedValue = false + } + + init(projectedValue: Bool) { + self.wrappedValue = 0 + self.projectedValue = projectedValue + } +} + +func f2( + @Wrapper p1: Int, + @WrapperWithInit p2: Int, + @WrapperWithProjected p3: Int, + @WrapperWithProjectedAndInit p4: Int +) {} From be60a871a316862adb5958a06683c4f623685af1 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Fri, 11 Nov 2022 12:01:23 +0000 Subject: [PATCH 0191/1420] Ruby: tweak comment --- ruby/ql/lib/codeql/ruby/Concepts.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/Concepts.qll b/ruby/ql/lib/codeql/ruby/Concepts.qll index a42fa354660..54eee4e3e0a 100644 --- a/ruby/ql/lib/codeql/ruby/Concepts.qll +++ b/ruby/ql/lib/codeql/ruby/Concepts.qll @@ -17,7 +17,7 @@ private import codeql.ruby.ApiGraphs * Often, it is worthy of an alert if a SQL statement is constructed such that * executing it would be a security risk. * - * If it is important that the SQL statement is indeed executed, use `SqlExecution`. + * If it is important that the SQL statement is executed, use `SqlExecution`. * * Extend this class to refine existing API models. If you want to model new APIs, * extend `SqlConstruction::Range` instead. @@ -35,7 +35,7 @@ module SqlConstruction { * Often, it is worthy of an alert if a SQL statement is constructed such that * executing it would be a security risk. * - * If it is important that the SQL statement is indeed executed, use `SqlExecution`. + * If it is important that the SQL statement is executed, use `SqlExecution`. * * Extend this class to model new APIs. If you want to refine existing API models, * extend `SqlConstruction` instead. From f659ee3e0be655385dcaf001fb219b86ea6624bf Mon Sep 17 00:00:00 2001 From: Gustav Date: Fri, 11 Nov 2022 13:07:30 +0100 Subject: [PATCH 0192/1420] Go: Optimize trap.Writer by buffering gzip writes The TRAP writer already buffers writes before emitting to file, but running gzip compression is also fairly costly (especially if you only do it a couple of bytes at a time). Thus, this injects another buffer that collects the emitted tuples in string form, and only triggers gzip compression once the buffer is full. In my local testing, this buffering was actually more beneficial than the one between gzip and file (likely because the gzip writer already emits data in chunks), but that one is still beneficial. --- go/extractor/trap/trapwriter.go | 34 ++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/go/extractor/trap/trapwriter.go b/go/extractor/trap/trapwriter.go index 4c0911e24d7..2a3944705bf 100644 --- a/go/extractor/trap/trapwriter.go +++ b/go/extractor/trap/trapwriter.go @@ -19,7 +19,8 @@ import ( // A Writer provides methods for writing data to a TRAP file type Writer struct { zip *gzip.Writer - w *bufio.Writer + wzip *bufio.Writer + wfile *bufio.Writer file *os.File Labeler *Labeler path string @@ -54,11 +55,13 @@ func NewWriter(path string, pkg *packages.Package) (*Writer, error) { if err != nil { return nil, err } - bufioWriter := bufio.NewWriter(tmpFile) - zipWriter := gzip.NewWriter(bufioWriter) + bufioFileWriter := bufio.NewWriter(tmpFile) + zipWriter := gzip.NewWriter(bufioFileWriter) + bufioZipWriter := bufio.NewWriter(zipWriter) tw := &Writer{ zipWriter, - bufioWriter, + bufioZipWriter, + bufioFileWriter, tmpFile, nil, path, @@ -88,13 +91,18 @@ func trapFolder() (string, error) { // Close the underlying file writer func (tw *Writer) Close() error { - err := tw.zip.Close() + err := tw.wzip.Flush() + if err != nil { + // throw away file close error + tw.file.Close() + } + err = tw.zip.Close() if err != nil { // return zip-close error, but ignore file-close error tw.file.Close() return err } - err = tw.w.Flush() + err = tw.wfile.Flush() if err != nil { // throw away close error because write errors are likely to be more important tw.file.Close() @@ -145,24 +153,24 @@ func capStringLength(s string) string { // Emit writes out a tuple of values for the given `table` func (tw *Writer) Emit(table string, values []interface{}) error { - fmt.Fprintf(tw.zip, "%s(", table) + fmt.Fprintf(tw.wzip, "%s(", table) for i, value := range values { if i > 0 { - fmt.Fprint(tw.zip, ", ") + fmt.Fprint(tw.wzip, ", ") } switch value := value.(type) { case Label: - fmt.Fprint(tw.zip, value.id) + fmt.Fprint(tw.wzip, value.id) case string: - fmt.Fprintf(tw.zip, "\"%s\"", escapeString(capStringLength(value))) + fmt.Fprintf(tw.wzip, "\"%s\"", escapeString(capStringLength(value))) case int: - fmt.Fprintf(tw.zip, "%d", value) + fmt.Fprintf(tw.wzip, "%d", value) case float64: - fmt.Fprintf(tw.zip, "%e", value) + fmt.Fprintf(tw.wzip, "%e", value) default: return errors.New("Cannot emit value") } } - fmt.Fprintf(tw.zip, ")\n") + fmt.Fprintf(tw.wzip, ")\n") return nil } From 3e5e695325cbc6a4db89a8f789551c0c5dbb96cf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Nov 2022 12:36:19 +0000 Subject: [PATCH 0193/1420] JS: Bump patch version of ML-powered library and query packs --- .../ql/experimental/adaptivethreatmodeling/lib/qlpack.yml | 2 +- .../ql/experimental/adaptivethreatmodeling/src/qlpack.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml index dda5982f82e..e0571f38255 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-experimental-atm-lib -version: 0.4.0 +version: 0.4.1 extractor: javascript library: true groups: diff --git a/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml index a25ef767105..cab87ce0e33 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml +++ b/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml @@ -1,6 +1,6 @@ name: codeql/javascript-experimental-atm-queries language: javascript -version: 0.4.0 +version: 0.4.1 suites: codeql-suites defaultSuiteFile: codeql-suites/javascript-atm-code-scanning.qls groups: From b5b69e93575616630152eb67ac6c8c2741030d5e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Nov 2022 12:48:00 +0000 Subject: [PATCH 0194/1420] JS: Bump version of ML-powered library and query packs to 0.4.2 --- .../ql/experimental/adaptivethreatmodeling/lib/qlpack.yml | 2 +- .../ql/experimental/adaptivethreatmodeling/src/qlpack.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml index e0571f38255..fb53f54ded7 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-experimental-atm-lib -version: 0.4.1 +version: 0.4.2 extractor: javascript library: true groups: diff --git a/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml index cab87ce0e33..725beadcb0e 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml +++ b/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml @@ -1,6 +1,6 @@ name: codeql/javascript-experimental-atm-queries language: javascript -version: 0.4.1 +version: 0.4.2 suites: codeql-suites defaultSuiteFile: codeql-suites/javascript-atm-code-scanning.qls groups: From 131fc986b48d823cfd724fdecc196194395f4058 Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 11 Nov 2022 13:49:46 +0100 Subject: [PATCH 0195/1420] Python: Apply suggestions from code review Co-authored-by: Rasmus Wriedt Larsen Co-authored-by: yoff --- .../python/dataflow/new/internal/ImportResolution.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll index e1e4381519c..f1f3da84d74 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll @@ -35,10 +35,10 @@ private import semmle.python.dataflow.new.TypeTracker * 1. If `foo` is a module, and `bar` is an attribute of `foo`, then `from foo import bar` imports * the attribute `bar` into the current module (binding it to the name `bar`). * 2. If `foo` is a package, and `bar` is a submodule of `foo`, then `from foo import bar` first imports - * `foo.bar`, and then attempts to locate the `bar` attribute again. In most cases, that attribute + * `foo.bar`, and then reads the `bar` attribute on `foo`. In most cases, that attribute * will then point to the `bar` submodule. * - * Now, when in comes to how these imports are represented in the AST, things get a bit complicated. + * Now, when it comes to how these imports are represented in the AST, things get a bit complicated. * First of all, both of the above forms of imports get mapped to the same kind of AST node: * `Import`. An `Import` node has a sequence of names, each of which is an `Alias` node. This `Alias` * node represents the `x as y` bit of each imported module. @@ -230,7 +230,7 @@ module ImportResolution { module_reexport(p, attr_name, m) ) or - // Submodules that are implicitly defined when importing via `from ... import ...` statements. + // Submodules that are implicitly defined whith relative imports of the form `from .foo import ...`. // In practice, we create a definition for each module in a package, even if it is not imported. exists(string submodule, Module package | SsaSource::init_module_submodule_defn(result.asVar().getSourceVariable(), From 612624d241b5454d79286010fddf70149c503122 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 11 Nov 2022 13:53:49 +0100 Subject: [PATCH 0196/1420] C++: Recognize `basic_string::iterator` as an iterator --- .../semmle/code/cpp/models/implementations/StdString.qll | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll index cf3ecb5e892..9ac92597b1a 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll @@ -15,6 +15,15 @@ private class StdBasicString extends ClassTemplateInstantiation { StdBasicString() { this.hasQualifiedName(["std", "bsl"], "basic_string") } } +/** + * The `std::basic_string::iterator` declaration. + */ +private class StdBasicStringIterator extends Iterator, Type { + StdBasicStringIterator() { + this.getEnclosingElement() instanceof StdBasicString and this.hasName("iterator") + } +} + /** * A `std::string` function for which taint should be propagated. */ From 7f790432cc4a34d0ca1c4695a51e899458b133ef Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 11 Nov 2022 14:40:58 +0100 Subject: [PATCH 0197/1420] Python: More review suggestions I could have sworn I added all of them to the batch, but somehow these slipped through. Co-authored-by: yoff Co-authored-by: Rasmus Wriedt Larsen --- .../dataflow/new/internal/ImportResolution.qll | 2 +- .../import-resolution/importflow.ql | 2 +- .../experimental/import-resolution/main.py | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll index f1f3da84d74..823bb34b9e2 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll @@ -78,7 +78,7 @@ module ImportResolution { ) or exists(Alias a | - defn.asExpr() = [a.getValue(), a.getValue().(ImportMember).getModule()] and + defn.asExpr() = [a.getValue().(ImportExpr), a.getValue().(ImportMember).getModule()] and a.getAsname().(Name).getId() = name and defn.getScope() = m ) diff --git a/python/ql/test/experimental/import-resolution/importflow.ql b/python/ql/test/experimental/import-resolution/importflow.ql index 0e7d300d328..9439ffe0693 100644 --- a/python/ql/test/experimental/import-resolution/importflow.ql +++ b/python/ql/test/experimental/import-resolution/importflow.ql @@ -27,7 +27,7 @@ private class ImportConfiguration extends DataFlow::Configuration { class ResolutionTest extends InlineExpectationsTest { ResolutionTest() { this = "ResolutionTest" } - override string getARelevantTag() { result = "import" } + override string getARelevantTag() { result = "prints" } override predicate hasActualResult(Location location, string element, string tag, string value) { exists(DataFlow::PathNode source, DataFlow::PathNode sink, ImportConfiguration config | diff --git a/python/ql/test/experimental/import-resolution/main.py b/python/ql/test/experimental/import-resolution/main.py index 827bb78896b..801b081df0c 100644 --- a/python/ql/test/experimental/import-resolution/main.py +++ b/python/ql/test/experimental/import-resolution/main.py @@ -1,4 +1,22 @@ #! /usr/bin/env python3 +""" +A slightly complicated test setup. I wanted to both make sure I captured +the semantics of Python and also the fact that the kinds of global flow +we expect to see are indeed present. + +The code is executable, and prints out both when the execution reaches +certain files, and also what values are assigned to the various +attributes that are referenced throughout the program. These values are +validated in the test as well. + +My original version used introspection to avoid referencing attributes +directly (thus enabling better error diagnostics), but unfortunately +that made it so that the model couldn't follow what was going on. + +The current setup is a bit clunky (and Python's scoping rules makes it +especially so -- cf. the explicit calls to `globals` and `locals`), but +I think it does the job okay. +""" from __future__ import print_function import sys From b540eb094cb5ba7aea5f795ce4751f49106920ce Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 11 Nov 2022 13:36:55 +0000 Subject: [PATCH 0198/1420] Python: Various small fixes - Swaps `module_reference_in_scope` and `module_name_in_scope`. - uses `AttrRead::accesses` instead of `getObject`, etc. - Removes an errant `none()`. - Expands the QLDoc for some of the predicates. --- .../new/internal/ImportResolution.qll | 44 ++++++++++++------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll index 823bb34b9e2..3a9623dfb53 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll @@ -34,9 +34,11 @@ private import semmle.python.dataflow.new.TypeTracker * * 1. If `foo` is a module, and `bar` is an attribute of `foo`, then `from foo import bar` imports * the attribute `bar` into the current module (binding it to the name `bar`). - * 2. If `foo` is a package, and `bar` is a submodule of `foo`, then `from foo import bar` first imports - * `foo.bar`, and then reads the `bar` attribute on `foo`. In most cases, that attribute - * will then point to the `bar` submodule. + * 2. If `foo` is a package, and `bar` is already defined in `foo/__init__.py`, + * that value will be imported. If it is not defined, and `bar` is a submodule of `foo`, then + * `bar` is imported to `foo`, and the `bar` submodule imported. + * Note: We don't currently model if the attribute is already defined in `__init__.py` + * and always assume that the submodule will be used. * * Now, when it comes to how these imports are represented in the AST, things get a bit complicated. * First of all, both of the above forms of imports get mapped to the same kind of AST node: @@ -58,7 +60,7 @@ private import semmle.python.dataflow.new.TypeTracker * package, or the `bar` subpackage of the `foo` package. The practical difference here is the name of * the module that is imported, as the package `foo.bar` will have the "name" `foo.bar.__init__`, * corresponding to the fact that the code that is executed is in the `__init__.py` file of the - * `bar` package. + * `bar` subpackage. */ module ImportResolution { /** @@ -168,8 +170,17 @@ module ImportResolution { isPreferredModuleForName(result.getFile(), i.getImportedModuleName()) } - /** Gets a data-flow node that may be a reference to a module with the name `module_name`. */ - DataFlow::Node getReferenceToModuleName(string module_name) { + /** + * Gets a data-flow node that may be a reference to a module with the name `module_name`. + * + * This is a helper predicate for `getImmediateModuleReference`. It captures the fact that in an + * import such as `import foo`, + * - `foo` may simply be the name of a module, or + * - `foo` may be the name of a package (in which case its name is actually `foo.__init__`), or + * - `foo` may be a module name that has been added to `sys.modules` (in which case its actual name can + * be anything, for instance `os.path` is either `posixpath` or `ntpath`). + */ + private DataFlow::Node getReferenceToModuleName(string module_name) { // Regular import statements, e.g. // import foo # implicitly `import foo as foo` // import foo as foo_alias @@ -188,7 +199,6 @@ module ImportResolution { // from foo.bar import baz # imports foo.bar.baz as baz // from foo.bar import baz as baz_alias # imports foo.bar.baz as baz_alias exists(Import i, Alias a, ImportMember im | a = i.getAName() and im = a.getValue() | - i.isFromImport() and result.asExpr() = a.getAsname() and module_name = im.getModule().(ImportExpr).getImportedModuleName() + "." + im.getName() ) @@ -201,12 +211,15 @@ module ImportResolution { any(ImportMember i | i.getModule().(ImportExpr).getImportedModuleName() = module_name or - i.getModule().(ImportExpr).getImportedModuleName() + "." + i.getName() = module_name and - none() + i.getModule().(ImportExpr).getImportedModuleName() + "." + i.getName() = module_name ) } - /** Gets a dataflow node that is an immediate reference to the module `m`. */ + /** + * Gets a dataflow node that is an immediate reference to the module `m`. + * + * Because of attribute lookups, this is mutually recursive with `getModuleReference`. + */ DataFlow::Node getImmediateModuleReference(Module m) { exists(string module_name | result = getReferenceToModuleName(module_name) | // Depending on whether the referenced module is a package or not, we may need to add a @@ -219,9 +232,8 @@ module ImportResolution { or // Reading an attribute on a module may return a submodule (or subpackage). exists(DataFlow::AttrRead ar, Module p, string attr_name | - ar.getObject() = getModuleReference(p) and + ar.accesses(getModuleReference(p), attr_name) and attr_name = any(Module m0).getFile().getStem() and - ar.getAttributeName() = attr_name and result = ar | isPreferredModuleForName(m.getFile(), p.getPackageName() + "." + attr_name + ["", ".__init__"]) @@ -242,7 +254,7 @@ module ImportResolution { /** Join-order helper for `getModuleReference`. */ pragma[nomagic] - private predicate module_name_in_scope(DataFlow::Node node, Scope s, string name, Module m) { + private predicate module_reference_in_scope(DataFlow::Node node, Scope s, string name, Module m) { node.getScope() = s and node.asExpr().(Name).getId() = name and pragma[only_bind_into](node) = getImmediateModuleReference(pragma[only_bind_into](m)) @@ -250,7 +262,7 @@ module ImportResolution { /** Join-order helper for `getModuleReference`. */ pragma[nomagic] - private predicate module_reference_in_scope(DataFlow::Node node, Scope s, string name) { + private predicate module_name_in_scope(DataFlow::Node node, Scope s, string name) { node.getScope() = s and exists(Name n | n = node.asExpr() | n.getId() = name and @@ -277,9 +289,9 @@ module ImportResolution { or // A reference to a name that is bound to a module in an enclosing scope. exists(DataFlow::Node def, Scope def_scope, Scope use_scope, string name | - module_name_in_scope(pragma[only_bind_into](def), pragma[only_bind_into](def_scope), + module_reference_in_scope(pragma[only_bind_into](def), pragma[only_bind_into](def_scope), pragma[only_bind_into](name), pragma[only_bind_into](m)) and - module_reference_in_scope(result, use_scope, name) and + module_name_in_scope(result, use_scope, name) and use_scope.getEnclosingScope*() = def_scope ) } From 7d54b542b5fe304dc75ca35e5f1a46474637190b Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Fri, 11 Nov 2022 13:00:07 +0000 Subject: [PATCH 0199/1420] Kotlin: Put extractor name in a resource rather than generating code --- java/kotlin-extractor/build.py | 13 +++++++------ .../src/main/kotlin/KotlinExtractorExtension.kt | 1 + 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/java/kotlin-extractor/build.py b/java/kotlin-extractor/build.py index 497e6da049e..ebca028be63 100755 --- a/java/kotlin-extractor/build.py +++ b/java/kotlin-extractor/build.py @@ -103,7 +103,7 @@ def compile_to_dir(srcs, classpath, java_classpath, output): '-classpath', os.path.pathsep.join([output, classpath, java_classpath])] + [s for s in srcs if s.endswith(".java")]) -def compile_to_jar(build_dir, srcs, classpath, java_classpath, output): +def compile_to_jar(build_dir, tmp_src_dir, srcs, classpath, java_classpath, output): class_dir = build_dir + '/classes' if os.path.exists(class_dir): @@ -114,7 +114,7 @@ def compile_to_jar(build_dir, srcs, classpath, java_classpath, output): run_process(['jar', 'cf', output, '-C', class_dir, '.', - '-C', 'src/main/resources', 'META-INF']) + '-C', tmp_src_dir + '/main/resources', '.']) shutil.rmtree(class_dir) @@ -185,9 +185,10 @@ def compile(jars, java_jars, dependency_folder, transform_to_embeddable, output, include_version_folder = tmp_src_dir + '/main/kotlin/utils/versions/to_include' os.makedirs(include_version_folder) - with open(tmp_src_dir + '/main/kotlin/utils/ExtractorName.kt', 'w') as f: - f.write('package com.github.codeql\n') - f.write('val extractor_name: String = "' + output + '"\n') + resource_dir = tmp_src_dir + '/main/resources/com/github/codeql' + os.makedirs(resource_dir) + with open(resource_dir + '/extractor.name', 'w') as f: + f.write(output) parsed_current_version = kotlin_plugin_versions.version_string_to_tuple( current_version) @@ -215,7 +216,7 @@ def compile(jars, java_jars, dependency_folder, transform_to_embeddable, output, transform_to_embeddable(srcs) - compile_to_jar(build_dir, srcs, classpath, java_classpath, output) + compile_to_jar(build_dir, tmp_src_dir, srcs, classpath, java_classpath, output) shutil.rmtree(tmp_src_dir) diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt b/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt index 2ffef600476..71e1862a22e 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt @@ -132,6 +132,7 @@ class KotlinExtractorExtension( val compilation: Label = StringLabel("compilation") tw.writeCompilation_started(compilation) tw.writeCompilation_info(compilation, "Kotlin Compiler Version", KotlinCompilerVersion.getVersion() ?: "") + val extractor_name = this::class.java.getResource("extractor.name")?.readText() ?: "" tw.writeCompilation_info(compilation, "Kotlin Extractor Name", extractor_name) if (compilationStartTime != null) { tw.writeCompilation_compiler_times(compilation, -1.0, (System.currentTimeMillis()-compilationStartTime)/1000.0) From ce1fb4c018d93231c877cfc7d0d1c18f585ed9e6 Mon Sep 17 00:00:00 2001 From: Alex Denisov Date: Fri, 11 Nov 2022 15:22:58 +0100 Subject: [PATCH 0200/1420] Swift: accept test changes --- swift/ql/test/extractor-tests/errors/Errors.expected | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/ql/test/extractor-tests/errors/Errors.expected b/swift/ql/test/extractor-tests/errors/Errors.expected index a527c7b28c1..4ecfbef31da 100644 --- a/swift/ql/test/extractor-tests/errors/Errors.expected +++ b/swift/ql/test/extractor-tests/errors/Errors.expected @@ -1,5 +1,5 @@ -| file://:0:0:0:0 | (no string representation) | ErrorType | | file://:0:0:0:0 | ... .combine(_:) | UnresolvedDotExpr | +| file://:0:0:0:0 | <> | ErrorType | | overloaded.swift:6:5:6:5 | OverloadedDeclRefExpr | OverloadedDeclRefExpr | | unresolved.swift:5:1:5:14 | UnresolvedSpecializeExpr | UnresolvedSpecializeExpr | | unspecified.swift:3:1:3:23 | missing extended_type_decl from ExtensionDecl | UnspecifiedElement | From a8a7a59ae83dda9bd5aec5a7978614ddff9fab75 Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 11 Nov 2022 14:47:35 +0000 Subject: [PATCH 0201/1420] Python: Add test for attribute name clash --- .../experimental/import-resolution/attr_clash/__init__.py | 6 ++++++ .../import-resolution/attr_clash/clashing_attr.py | 4 ++++ .../import-resolution/attr_clash/non_clashing_submodule.py | 4 ++++ python/ql/test/experimental/import-resolution/main.py | 7 ++++++- python/ql/test/experimental/import-resolution/trace.py | 3 +++ 5 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 python/ql/test/experimental/import-resolution/attr_clash/__init__.py create mode 100644 python/ql/test/experimental/import-resolution/attr_clash/clashing_attr.py create mode 100644 python/ql/test/experimental/import-resolution/attr_clash/non_clashing_submodule.py diff --git a/python/ql/test/experimental/import-resolution/attr_clash/__init__.py b/python/ql/test/experimental/import-resolution/attr_clash/__init__.py new file mode 100644 index 00000000000..d210fbd703b --- /dev/null +++ b/python/ql/test/experimental/import-resolution/attr_clash/__init__.py @@ -0,0 +1,6 @@ +from trace import * +enter(__file__) + +clashing_attr = "clashing_attr" + +exit(__file__) diff --git a/python/ql/test/experimental/import-resolution/attr_clash/clashing_attr.py b/python/ql/test/experimental/import-resolution/attr_clash/clashing_attr.py new file mode 100644 index 00000000000..7544594893b --- /dev/null +++ b/python/ql/test/experimental/import-resolution/attr_clash/clashing_attr.py @@ -0,0 +1,4 @@ +from trace import * +enter(__file__) + +exit(__file__) diff --git a/python/ql/test/experimental/import-resolution/attr_clash/non_clashing_submodule.py b/python/ql/test/experimental/import-resolution/attr_clash/non_clashing_submodule.py new file mode 100644 index 00000000000..7544594893b --- /dev/null +++ b/python/ql/test/experimental/import-resolution/attr_clash/non_clashing_submodule.py @@ -0,0 +1,4 @@ +from trace import * +enter(__file__) + +exit(__file__) diff --git a/python/ql/test/experimental/import-resolution/main.py b/python/ql/test/experimental/import-resolution/main.py index 801b081df0c..d2ad1b9e337 100644 --- a/python/ql/test/experimental/import-resolution/main.py +++ b/python/ql/test/experimental/import-resolution/main.py @@ -35,7 +35,7 @@ import foo as foo_alias #$ imports=foo as=foo_alias check("foo_alias.foo_attr", foo_alias.foo_attr, "foo_attr", globals()) #$ prints=foo_attr # A reference to a reexported module -check("foo.bar_reexported.bar_attr", foo.bar_reexported.bar_attr, "bar_attr", globals()) #$ prints=bar_attr +check("foo.bar_reexported.bar_attr", foo.bar_reexported.bar_attr, "bar_attr", globals()) #$ MISSING: prints=bar_attr # A simple "import from" statement. from bar import bar_attr @@ -73,6 +73,11 @@ if sys.version_info[0] >= 3: from namespace_package.namespace_module import namespace_module_attr check("namespace_module_attr", namespace_module_attr, "namespace_module_attr", globals()) #$ prints=namespace_module_attr + +from attr_clash import clashing_attr, non_clashing_submodule #$ imports=attr_clash.clashing_attr as=clashing_attr imports=attr_clash.non_clashing_submodule as=non_clashing_submodule +check("clashing_attr", clashing_attr, "clashing_attr", globals()) #$ prints=clashing_attr +check("non_clashing_submodule", non_clashing_submodule, "", globals()) + exit(__file__) print() diff --git a/python/ql/test/experimental/import-resolution/trace.py b/python/ql/test/experimental/import-resolution/trace.py index 04e847304e9..90d8efba3fb 100644 --- a/python/ql/test/experimental/import-resolution/trace.py +++ b/python/ql/test/experimental/import-resolution/trace.py @@ -24,6 +24,7 @@ def status(): return _status def check(attr_path, actual_value, expected_value, bindings): + global _status parts = attr_path.split(".") base, parts = parts[0], parts[1:] if base not in bindings: @@ -40,6 +41,8 @@ def check(attr_path, actual_value, expected_value, bindings): if val != actual_value: print("Error: Value at path {} and actual value are out of sync! {} != {}".format(attr_path, val, actual_value)) _status = 1 + if str(val).startswith("" if val != expected_value: print("Error: Expected {} to be {}, got {}".format(attr_path, expected_value, val)) _status = 1 From a08253b6d0e2f8c53332dd270250feaa29bf0741 Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 11 Nov 2022 14:50:04 +0000 Subject: [PATCH 0202/1420] Python: Fix typo --- .../semmle/python/dataflow/new/internal/ImportResolution.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll index 3a9623dfb53..dd92cbb7a45 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll @@ -242,7 +242,7 @@ module ImportResolution { module_reexport(p, attr_name, m) ) or - // Submodules that are implicitly defined whith relative imports of the form `from .foo import ...`. + // Submodules that are implicitly defined with relative imports of the form `from .foo import ...`. // In practice, we create a definition for each module in a package, even if it is not imported. exists(string submodule, Module package | SsaSource::init_module_submodule_defn(result.asVar().getSourceVariable(), From d49015a7e648c0182d425037511b1f028669512e Mon Sep 17 00:00:00 2001 From: Alex Denisov Date: Thu, 10 Nov 2022 14:41:09 +0100 Subject: [PATCH 0203/1420] Swift: infrastructure for upgrade/downgrade scripts --- .github/workflows/swift.yml | 17 +- .../database-upgrade-scripts/action.yml | 23 + swift/downgrades/initial/swift.dbscheme | 2493 +++++++++++++++++ swift/downgrades/qlpack.yml | 4 + swift/ql/lib/upgrades/initial/swift.dbscheme | 2493 +++++++++++++++++ 5 files changed, 5029 insertions(+), 1 deletion(-) create mode 100644 swift/actions/database-upgrade-scripts/action.yml create mode 100644 swift/downgrades/initial/swift.dbscheme create mode 100644 swift/downgrades/qlpack.yml create mode 100644 swift/ql/lib/upgrades/initial/swift.dbscheme diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 5ec691befb1..330df323de9 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -111,4 +111,19 @@ jobs: - uses: actions/upload-artifact@v3 with: name: swift-generated-cpp-files - path: swift/generated-cpp-files/** \ No newline at end of file + path: swift/generated-cpp-files/** + qlformat: + runs-on: ubuntu-latest + needs: changes + if: ${{ needs.changes.outputs.ql == 'true' }} + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/fetch-codeql + - name: Check QL formatting + run: find swift/ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only + database-upgrade-scripts: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/fetch-codeql + - uses: ./swift/actions/database-upgrade-scripts diff --git a/swift/actions/database-upgrade-scripts/action.yml b/swift/actions/database-upgrade-scripts/action.yml new file mode 100644 index 00000000000..26f95d44b8a --- /dev/null +++ b/swift/actions/database-upgrade-scripts/action.yml @@ -0,0 +1,23 @@ +name: Check Swift database upgrade/downgrade scripts +runs: + using: composite + steps: + - name: Check upgrade scripts + shell: bash + working-directory: swift + run: | + echo > empty.trap + codeql dataset import -S ql/lib/upgrades/initial/swift.dbscheme testdb empty.trap + codeql dataset upgrade testdb --additional-packs ql/lib + diff -q testdb/swift.dbscheme ql/lib/swift.dbscheme + - name: Check downgrade scripts + shell: bash + working-directory: swift + run: | + echo > empty.trap + rm -rf testdb + codeql dataset import -S ql/lib/swift.dbscheme testdb empty.trap + codeql resolve upgrades --format=lines --allow-downgrades --additional-packs downgrades \ + --dbscheme=ql/lib/swift.dbscheme --target-dbscheme=downgrades/initial/swift.dbscheme | + xargs -r codeql execute upgrades testdb + diff -q testdb/swift.dbscheme downgrades/initial/swift.dbscheme diff --git a/swift/downgrades/initial/swift.dbscheme b/swift/downgrades/initial/swift.dbscheme new file mode 100644 index 00000000000..ceca289a0ff --- /dev/null +++ b/swift/downgrades/initial/swift.dbscheme @@ -0,0 +1,2493 @@ +// generated by codegen/codegen.py + +// from prefix.dbscheme +/** + * The source location of the snapshot. + */ +sourceLocationPrefix( + string prefix: string ref +); + + +// from schema.py + +@element = + @callable +| @file +| @generic_context +| @iterable_decl_context +| @locatable +| @location +| @type +; + +#keyset[id] +element_is_unknown( + int id: @element ref +); + +@callable = + @abstract_closure_expr +| @abstract_function_decl +; + +#keyset[id] +callable_self_params( + int id: @callable ref, + int self_param: @param_decl_or_none ref +); + +#keyset[id, index] +callable_params( + int id: @callable ref, + int index: int ref, + int param: @param_decl_or_none ref +); + +#keyset[id] +callable_bodies( + int id: @callable ref, + int body: @brace_stmt_or_none ref +); + +@file = + @db_file +| @unknown_file +; + +#keyset[id] +files( + int id: @file ref, + string name: string ref +); + +@locatable = + @argument +| @ast_node +| @comment +| @diagnostics +| @error_element +; + +#keyset[id] +locatable_locations( + int id: @locatable ref, + int location: @location_or_none ref +); + +@location = + @db_location +| @unknown_location +; + +#keyset[id] +locations( + int id: @location ref, + int file: @file_or_none ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +@ast_node = + @case_label_item +| @condition_element +| @decl +| @expr +| @pattern +| @stmt +| @stmt_condition +| @type_repr +; + +comments( + unique int id: @comment, + string text: string ref +); + +db_files( + unique int id: @db_file +); + +db_locations( + unique int id: @db_location +); + +diagnostics( + unique int id: @diagnostics, + string text: string ref, + int kind: int ref +); + +@error_element = + @error_expr +| @error_type +| @overloaded_decl_ref_expr +| @unresolved_decl_ref_expr +| @unresolved_dot_expr +| @unresolved_member_chain_result_expr +| @unresolved_member_expr +| @unresolved_pattern_expr +| @unresolved_specialize_expr +| @unresolved_type +| @unresolved_type_conversion_expr +| @unspecified_element +; + +unknown_files( + unique int id: @unknown_file +); + +unknown_locations( + unique int id: @unknown_location +); + +unspecified_elements( + unique int id: @unspecified_element, + string property: string ref, + string error: string ref +); + +#keyset[id] +unspecified_element_parents( + int id: @unspecified_element ref, + int parent: @element ref +); + +#keyset[id] +unspecified_element_indices( + int id: @unspecified_element ref, + int index: int ref +); + +@decl = + @enum_case_decl +| @extension_decl +| @if_config_decl +| @import_decl +| @missing_member_decl +| @operator_decl +| @pattern_binding_decl +| @pound_diagnostic_decl +| @precedence_group_decl +| @top_level_code_decl +| @value_decl +; + +#keyset[id] +decls( //dir=decl + int id: @decl ref, + int module: @module_decl_or_none ref +); + +@generic_context = + @abstract_function_decl +| @extension_decl +| @generic_type_decl +| @subscript_decl +; + +#keyset[id, index] +generic_context_generic_type_params( //dir=decl + int id: @generic_context ref, + int index: int ref, + int generic_type_param: @generic_type_param_decl_or_none ref +); + +@iterable_decl_context = + @extension_decl +| @nominal_type_decl +; + +#keyset[id, index] +iterable_decl_context_members( //dir=decl + int id: @iterable_decl_context ref, + int index: int ref, + int member: @decl_or_none ref +); + +enum_case_decls( //dir=decl + unique int id: @enum_case_decl +); + +#keyset[id, index] +enum_case_decl_elements( //dir=decl + int id: @enum_case_decl ref, + int index: int ref, + int element: @enum_element_decl_or_none ref +); + +extension_decls( //dir=decl + unique int id: @extension_decl, + int extended_type_decl: @nominal_type_decl_or_none ref +); + +if_config_decls( //dir=decl + unique int id: @if_config_decl +); + +#keyset[id, index] +if_config_decl_active_elements( //dir=decl + int id: @if_config_decl ref, + int index: int ref, + int active_element: @ast_node_or_none ref +); + +import_decls( //dir=decl + unique int id: @import_decl +); + +#keyset[id] +import_decl_is_exported( //dir=decl + int id: @import_decl ref +); + +#keyset[id] +import_decl_imported_modules( //dir=decl + int id: @import_decl ref, + int imported_module: @module_decl_or_none ref +); + +#keyset[id, index] +import_decl_declarations( //dir=decl + int id: @import_decl ref, + int index: int ref, + int declaration: @value_decl_or_none ref +); + +missing_member_decls( //dir=decl + unique int id: @missing_member_decl, + string name: string ref +); + +@operator_decl = + @infix_operator_decl +| @postfix_operator_decl +| @prefix_operator_decl +; + +#keyset[id] +operator_decls( //dir=decl + int id: @operator_decl ref, + string name: string ref +); + +pattern_binding_decls( //dir=decl + unique int id: @pattern_binding_decl +); + +#keyset[id, index] +pattern_binding_decl_inits( //dir=decl + int id: @pattern_binding_decl ref, + int index: int ref, + int init: @expr_or_none ref +); + +#keyset[id, index] +pattern_binding_decl_patterns( //dir=decl + int id: @pattern_binding_decl ref, + int index: int ref, + int pattern: @pattern_or_none ref +); + +pound_diagnostic_decls( //dir=decl + unique int id: @pound_diagnostic_decl, + int kind: int ref, + int message: @string_literal_expr_or_none ref +); + +precedence_group_decls( //dir=decl + unique int id: @precedence_group_decl +); + +top_level_code_decls( //dir=decl + unique int id: @top_level_code_decl, + int body: @brace_stmt_or_none ref +); + +@value_decl = + @abstract_function_decl +| @abstract_storage_decl +| @enum_element_decl +| @type_decl +; + +#keyset[id] +value_decls( //dir=decl + int id: @value_decl ref, + int interface_type: @type_or_none ref +); + +@abstract_function_decl = + @constructor_decl +| @destructor_decl +| @func_decl +; + +#keyset[id] +abstract_function_decls( //dir=decl + int id: @abstract_function_decl ref, + string name: string ref +); + +@abstract_storage_decl = + @subscript_decl +| @var_decl +; + +#keyset[id, index] +abstract_storage_decl_accessor_decls( //dir=decl + int id: @abstract_storage_decl ref, + int index: int ref, + int accessor_decl: @accessor_decl_or_none ref +); + +enum_element_decls( //dir=decl + unique int id: @enum_element_decl, + string name: string ref +); + +#keyset[id, index] +enum_element_decl_params( //dir=decl + int id: @enum_element_decl ref, + int index: int ref, + int param: @param_decl_or_none ref +); + +infix_operator_decls( //dir=decl + unique int id: @infix_operator_decl +); + +#keyset[id] +infix_operator_decl_precedence_groups( //dir=decl + int id: @infix_operator_decl ref, + int precedence_group: @precedence_group_decl_or_none ref +); + +postfix_operator_decls( //dir=decl + unique int id: @postfix_operator_decl +); + +prefix_operator_decls( //dir=decl + unique int id: @prefix_operator_decl +); + +@type_decl = + @abstract_type_param_decl +| @generic_type_decl +| @module_decl +; + +#keyset[id] +type_decls( //dir=decl + int id: @type_decl ref, + string name: string ref +); + +#keyset[id, index] +type_decl_base_types( //dir=decl + int id: @type_decl ref, + int index: int ref, + int base_type: @type_or_none ref +); + +@abstract_type_param_decl = + @associated_type_decl +| @generic_type_param_decl +; + +constructor_decls( //dir=decl + unique int id: @constructor_decl +); + +destructor_decls( //dir=decl + unique int id: @destructor_decl +); + +@func_decl = + @accessor_decl +| @concrete_func_decl +; + +@generic_type_decl = + @nominal_type_decl +| @opaque_type_decl +| @type_alias_decl +; + +module_decls( //dir=decl + unique int id: @module_decl +); + +#keyset[id] +module_decl_is_builtin_module( //dir=decl + int id: @module_decl ref +); + +#keyset[id] +module_decl_is_system_module( //dir=decl + int id: @module_decl ref +); + +#keyset[id, index] +module_decl_imported_modules( //dir=decl + int id: @module_decl ref, + int index: int ref, + int imported_module: @module_decl_or_none ref +); + +#keyset[id, index] +module_decl_exported_modules( //dir=decl + int id: @module_decl ref, + int index: int ref, + int exported_module: @module_decl_or_none ref +); + +subscript_decls( //dir=decl + unique int id: @subscript_decl, + int element_type: @type_or_none ref +); + +#keyset[id, index] +subscript_decl_params( //dir=decl + int id: @subscript_decl ref, + int index: int ref, + int param: @param_decl_or_none ref +); + +@var_decl = + @concrete_var_decl +| @param_decl +; + +#keyset[id] +var_decls( //dir=decl + int id: @var_decl ref, + string name: string ref, + int type_: @type_or_none ref +); + +#keyset[id] +var_decl_attached_property_wrapper_types( //dir=decl + int id: @var_decl ref, + int attached_property_wrapper_type: @type_or_none ref +); + +#keyset[id] +var_decl_parent_patterns( //dir=decl + int id: @var_decl ref, + int parent_pattern: @pattern_or_none ref +); + +#keyset[id] +var_decl_parent_initializers( //dir=decl + int id: @var_decl ref, + int parent_initializer: @expr_or_none ref +); + +#keyset[id] +var_decl_property_wrapper_backing_var_bindings( //dir=decl + int id: @var_decl ref, + int property_wrapper_backing_var_binding: @pattern_binding_decl_or_none ref +); + +#keyset[id] +var_decl_property_wrapper_backing_vars( //dir=decl + int id: @var_decl ref, + int property_wrapper_backing_var: @var_decl_or_none ref +); + +#keyset[id] +var_decl_property_wrapper_projection_var_bindings( //dir=decl + int id: @var_decl ref, + int property_wrapper_projection_var_binding: @pattern_binding_decl_or_none ref +); + +#keyset[id] +var_decl_property_wrapper_projection_vars( //dir=decl + int id: @var_decl ref, + int property_wrapper_projection_var: @var_decl_or_none ref +); + +accessor_decls( //dir=decl + unique int id: @accessor_decl +); + +#keyset[id] +accessor_decl_is_getter( //dir=decl + int id: @accessor_decl ref +); + +#keyset[id] +accessor_decl_is_setter( //dir=decl + int id: @accessor_decl ref +); + +#keyset[id] +accessor_decl_is_will_set( //dir=decl + int id: @accessor_decl ref +); + +#keyset[id] +accessor_decl_is_did_set( //dir=decl + int id: @accessor_decl ref +); + +associated_type_decls( //dir=decl + unique int id: @associated_type_decl +); + +concrete_func_decls( //dir=decl + unique int id: @concrete_func_decl +); + +concrete_var_decls( //dir=decl + unique int id: @concrete_var_decl, + int introducer_int: int ref +); + +generic_type_param_decls( //dir=decl + unique int id: @generic_type_param_decl +); + +@nominal_type_decl = + @class_decl +| @enum_decl +| @protocol_decl +| @struct_decl +; + +#keyset[id] +nominal_type_decls( //dir=decl + int id: @nominal_type_decl ref, + int type_: @type_or_none ref +); + +opaque_type_decls( //dir=decl + unique int id: @opaque_type_decl, + int naming_declaration: @value_decl_or_none ref +); + +#keyset[id, index] +opaque_type_decl_opaque_generic_params( //dir=decl + int id: @opaque_type_decl ref, + int index: int ref, + int opaque_generic_param: @generic_type_param_type_or_none ref +); + +param_decls( //dir=decl + unique int id: @param_decl +); + +#keyset[id] +param_decl_is_inout( //dir=decl + int id: @param_decl ref +); + +#keyset[id] +param_decl_property_wrapper_local_wrapped_var_bindings( //dir=decl + int id: @param_decl ref, + int property_wrapper_local_wrapped_var_binding: @pattern_binding_decl_or_none ref +); + +#keyset[id] +param_decl_property_wrapper_local_wrapped_vars( //dir=decl + int id: @param_decl ref, + int property_wrapper_local_wrapped_var: @var_decl_or_none ref +); + +type_alias_decls( //dir=decl + unique int id: @type_alias_decl +); + +class_decls( //dir=decl + unique int id: @class_decl +); + +enum_decls( //dir=decl + unique int id: @enum_decl +); + +protocol_decls( //dir=decl + unique int id: @protocol_decl +); + +struct_decls( //dir=decl + unique int id: @struct_decl +); + +arguments( //dir=expr + unique int id: @argument, + string label: string ref, + int expr: @expr_or_none ref +); + +@expr = + @abstract_closure_expr +| @any_try_expr +| @applied_property_wrapper_expr +| @apply_expr +| @assign_expr +| @bind_optional_expr +| @capture_list_expr +| @collection_expr +| @decl_ref_expr +| @default_argument_expr +| @discard_assignment_expr +| @dot_syntax_base_ignored_expr +| @dynamic_type_expr +| @enum_is_case_expr +| @error_expr +| @explicit_cast_expr +| @force_value_expr +| @identity_expr +| @if_expr +| @implicit_conversion_expr +| @in_out_expr +| @key_path_application_expr +| @key_path_dot_expr +| @key_path_expr +| @lazy_initializer_expr +| @literal_expr +| @lookup_expr +| @make_temporarily_escapable_expr +| @obj_c_selector_expr +| @one_way_expr +| @opaque_value_expr +| @open_existential_expr +| @optional_evaluation_expr +| @other_constructor_decl_ref_expr +| @overloaded_decl_ref_expr +| @property_wrapper_value_placeholder_expr +| @rebind_self_in_constructor_expr +| @sequence_expr +| @super_ref_expr +| @tap_expr +| @tuple_element_expr +| @tuple_expr +| @type_expr +| @unresolved_decl_ref_expr +| @unresolved_dot_expr +| @unresolved_member_expr +| @unresolved_pattern_expr +| @unresolved_specialize_expr +| @vararg_expansion_expr +; + +#keyset[id] +expr_types( //dir=expr + int id: @expr ref, + int type_: @type_or_none ref +); + +@abstract_closure_expr = + @auto_closure_expr +| @closure_expr +; + +@any_try_expr = + @force_try_expr +| @optional_try_expr +| @try_expr +; + +#keyset[id] +any_try_exprs( //dir=expr + int id: @any_try_expr ref, + int sub_expr: @expr_or_none ref +); + +applied_property_wrapper_exprs( //dir=expr + unique int id: @applied_property_wrapper_expr, + int kind: int ref, + int value: @expr_or_none ref, + int param: @param_decl_or_none ref +); + +@apply_expr = + @binary_expr +| @call_expr +| @postfix_unary_expr +| @prefix_unary_expr +| @self_apply_expr +; + +#keyset[id] +apply_exprs( //dir=expr + int id: @apply_expr ref, + int function: @expr_or_none ref +); + +#keyset[id, index] +apply_expr_arguments( //dir=expr + int id: @apply_expr ref, + int index: int ref, + int argument: @argument_or_none ref +); + +assign_exprs( //dir=expr + unique int id: @assign_expr, + int dest: @expr_or_none ref, + int source: @expr_or_none ref +); + +bind_optional_exprs( //dir=expr + unique int id: @bind_optional_expr, + int sub_expr: @expr_or_none ref +); + +capture_list_exprs( //dir=expr + unique int id: @capture_list_expr, + int closure_body: @closure_expr_or_none ref +); + +#keyset[id, index] +capture_list_expr_binding_decls( //dir=expr + int id: @capture_list_expr ref, + int index: int ref, + int binding_decl: @pattern_binding_decl_or_none ref +); + +@collection_expr = + @array_expr +| @dictionary_expr +; + +decl_ref_exprs( //dir=expr + unique int id: @decl_ref_expr, + int decl: @decl_or_none ref +); + +#keyset[id, index] +decl_ref_expr_replacement_types( //dir=expr + int id: @decl_ref_expr ref, + int index: int ref, + int replacement_type: @type_or_none ref +); + +#keyset[id] +decl_ref_expr_has_direct_to_storage_semantics( //dir=expr + int id: @decl_ref_expr ref +); + +#keyset[id] +decl_ref_expr_has_direct_to_implementation_semantics( //dir=expr + int id: @decl_ref_expr ref +); + +#keyset[id] +decl_ref_expr_has_ordinary_semantics( //dir=expr + int id: @decl_ref_expr ref +); + +default_argument_exprs( //dir=expr + unique int id: @default_argument_expr, + int param_decl: @param_decl_or_none ref, + int param_index: int ref +); + +#keyset[id] +default_argument_expr_caller_side_defaults( //dir=expr + int id: @default_argument_expr ref, + int caller_side_default: @expr_or_none ref +); + +discard_assignment_exprs( //dir=expr + unique int id: @discard_assignment_expr +); + +dot_syntax_base_ignored_exprs( //dir=expr + unique int id: @dot_syntax_base_ignored_expr, + int qualifier: @expr_or_none ref, + int sub_expr: @expr_or_none ref +); + +dynamic_type_exprs( //dir=expr + unique int id: @dynamic_type_expr, + int base: @expr_or_none ref +); + +enum_is_case_exprs( //dir=expr + unique int id: @enum_is_case_expr, + int sub_expr: @expr_or_none ref, + int element: @enum_element_decl_or_none ref +); + +error_exprs( //dir=expr + unique int id: @error_expr +); + +@explicit_cast_expr = + @checked_cast_expr +| @coerce_expr +; + +#keyset[id] +explicit_cast_exprs( //dir=expr + int id: @explicit_cast_expr ref, + int sub_expr: @expr_or_none ref +); + +force_value_exprs( //dir=expr + unique int id: @force_value_expr, + int sub_expr: @expr_or_none ref +); + +@identity_expr = + @await_expr +| @dot_self_expr +| @paren_expr +| @unresolved_member_chain_result_expr +; + +#keyset[id] +identity_exprs( //dir=expr + int id: @identity_expr ref, + int sub_expr: @expr_or_none ref +); + +if_exprs( //dir=expr + unique int id: @if_expr, + int condition: @expr_or_none ref, + int then_expr: @expr_or_none ref, + int else_expr: @expr_or_none ref +); + +@implicit_conversion_expr = + @any_hashable_erasure_expr +| @archetype_to_super_expr +| @array_to_pointer_expr +| @bridge_from_obj_c_expr +| @bridge_to_obj_c_expr +| @class_metatype_to_object_expr +| @collection_upcast_conversion_expr +| @conditional_bridge_from_obj_c_expr +| @covariant_function_conversion_expr +| @covariant_return_conversion_expr +| @derived_to_base_expr +| @destructure_tuple_expr +| @differentiable_function_expr +| @differentiable_function_extract_original_expr +| @erasure_expr +| @existential_metatype_to_object_expr +| @foreign_object_conversion_expr +| @function_conversion_expr +| @in_out_to_pointer_expr +| @inject_into_optional_expr +| @linear_function_expr +| @linear_function_extract_original_expr +| @linear_to_differentiable_function_expr +| @load_expr +| @metatype_conversion_expr +| @pointer_to_pointer_expr +| @protocol_metatype_to_object_expr +| @string_to_pointer_expr +| @underlying_to_opaque_expr +| @unevaluated_instance_expr +| @unresolved_type_conversion_expr +; + +#keyset[id] +implicit_conversion_exprs( //dir=expr + int id: @implicit_conversion_expr ref, + int sub_expr: @expr_or_none ref +); + +in_out_exprs( //dir=expr + unique int id: @in_out_expr, + int sub_expr: @expr_or_none ref +); + +key_path_application_exprs( //dir=expr + unique int id: @key_path_application_expr, + int base: @expr_or_none ref, + int key_path: @expr_or_none ref +); + +key_path_dot_exprs( //dir=expr + unique int id: @key_path_dot_expr +); + +key_path_exprs( //dir=expr + unique int id: @key_path_expr +); + +#keyset[id] +key_path_expr_roots( //dir=expr + int id: @key_path_expr ref, + int root: @type_repr_or_none ref +); + +#keyset[id] +key_path_expr_parsed_paths( //dir=expr + int id: @key_path_expr ref, + int parsed_path: @expr_or_none ref +); + +lazy_initializer_exprs( //dir=expr + unique int id: @lazy_initializer_expr, + int sub_expr: @expr_or_none ref +); + +@literal_expr = + @builtin_literal_expr +| @interpolated_string_literal_expr +| @nil_literal_expr +| @object_literal_expr +| @regex_literal_expr +; + +@lookup_expr = + @dynamic_lookup_expr +| @member_ref_expr +| @method_ref_expr +| @subscript_expr +; + +#keyset[id] +lookup_exprs( //dir=expr + int id: @lookup_expr ref, + int base: @expr_or_none ref +); + +#keyset[id] +lookup_expr_members( //dir=expr + int id: @lookup_expr ref, + int member: @decl_or_none ref +); + +make_temporarily_escapable_exprs( //dir=expr + unique int id: @make_temporarily_escapable_expr, + int escaping_closure: @opaque_value_expr_or_none ref, + int nonescaping_closure: @expr_or_none ref, + int sub_expr: @expr_or_none ref +); + +obj_c_selector_exprs( //dir=expr + unique int id: @obj_c_selector_expr, + int sub_expr: @expr_or_none ref, + int method: @abstract_function_decl_or_none ref +); + +one_way_exprs( //dir=expr + unique int id: @one_way_expr, + int sub_expr: @expr_or_none ref +); + +opaque_value_exprs( //dir=expr + unique int id: @opaque_value_expr +); + +open_existential_exprs( //dir=expr + unique int id: @open_existential_expr, + int sub_expr: @expr_or_none ref, + int existential: @expr_or_none ref, + int opaque_expr: @opaque_value_expr_or_none ref +); + +optional_evaluation_exprs( //dir=expr + unique int id: @optional_evaluation_expr, + int sub_expr: @expr_or_none ref +); + +other_constructor_decl_ref_exprs( //dir=expr + unique int id: @other_constructor_decl_ref_expr, + int constructor_decl: @constructor_decl_or_none ref +); + +overloaded_decl_ref_exprs( //dir=expr + unique int id: @overloaded_decl_ref_expr +); + +#keyset[id, index] +overloaded_decl_ref_expr_possible_declarations( //dir=expr + int id: @overloaded_decl_ref_expr ref, + int index: int ref, + int possible_declaration: @value_decl_or_none ref +); + +property_wrapper_value_placeholder_exprs( //dir=expr + unique int id: @property_wrapper_value_placeholder_expr, + int placeholder: @opaque_value_expr_or_none ref +); + +#keyset[id] +property_wrapper_value_placeholder_expr_wrapped_values( //dir=expr + int id: @property_wrapper_value_placeholder_expr ref, + int wrapped_value: @expr_or_none ref +); + +rebind_self_in_constructor_exprs( //dir=expr + unique int id: @rebind_self_in_constructor_expr, + int sub_expr: @expr_or_none ref, + int self: @var_decl_or_none ref +); + +sequence_exprs( //dir=expr + unique int id: @sequence_expr +); + +#keyset[id, index] +sequence_expr_elements( //dir=expr + int id: @sequence_expr ref, + int index: int ref, + int element: @expr_or_none ref +); + +super_ref_exprs( //dir=expr + unique int id: @super_ref_expr, + int self: @var_decl_or_none ref +); + +tap_exprs( //dir=expr + unique int id: @tap_expr, + int body: @brace_stmt_or_none ref, + int var: @var_decl_or_none ref +); + +#keyset[id] +tap_expr_sub_exprs( //dir=expr + int id: @tap_expr ref, + int sub_expr: @expr_or_none ref +); + +tuple_element_exprs( //dir=expr + unique int id: @tuple_element_expr, + int sub_expr: @expr_or_none ref, + int index: int ref +); + +tuple_exprs( //dir=expr + unique int id: @tuple_expr +); + +#keyset[id, index] +tuple_expr_elements( //dir=expr + int id: @tuple_expr ref, + int index: int ref, + int element: @expr_or_none ref +); + +type_exprs( //dir=expr + unique int id: @type_expr +); + +#keyset[id] +type_expr_type_reprs( //dir=expr + int id: @type_expr ref, + int type_repr: @type_repr_or_none ref +); + +unresolved_decl_ref_exprs( //dir=expr + unique int id: @unresolved_decl_ref_expr +); + +#keyset[id] +unresolved_decl_ref_expr_names( //dir=expr + int id: @unresolved_decl_ref_expr ref, + string name: string ref +); + +unresolved_dot_exprs( //dir=expr + unique int id: @unresolved_dot_expr, + int base: @expr_or_none ref, + string name: string ref +); + +unresolved_member_exprs( //dir=expr + unique int id: @unresolved_member_expr, + string name: string ref +); + +unresolved_pattern_exprs( //dir=expr + unique int id: @unresolved_pattern_expr, + int sub_pattern: @pattern_or_none ref +); + +unresolved_specialize_exprs( //dir=expr + unique int id: @unresolved_specialize_expr, + int sub_expr: @expr_or_none ref +); + +vararg_expansion_exprs( //dir=expr + unique int id: @vararg_expansion_expr, + int sub_expr: @expr_or_none ref +); + +any_hashable_erasure_exprs( //dir=expr + unique int id: @any_hashable_erasure_expr +); + +archetype_to_super_exprs( //dir=expr + unique int id: @archetype_to_super_expr +); + +array_exprs( //dir=expr + unique int id: @array_expr +); + +#keyset[id, index] +array_expr_elements( //dir=expr + int id: @array_expr ref, + int index: int ref, + int element: @expr_or_none ref +); + +array_to_pointer_exprs( //dir=expr + unique int id: @array_to_pointer_expr +); + +auto_closure_exprs( //dir=expr + unique int id: @auto_closure_expr +); + +await_exprs( //dir=expr + unique int id: @await_expr +); + +binary_exprs( //dir=expr + unique int id: @binary_expr +); + +bridge_from_obj_c_exprs( //dir=expr + unique int id: @bridge_from_obj_c_expr +); + +bridge_to_obj_c_exprs( //dir=expr + unique int id: @bridge_to_obj_c_expr +); + +@builtin_literal_expr = + @boolean_literal_expr +| @magic_identifier_literal_expr +| @number_literal_expr +| @string_literal_expr +; + +call_exprs( //dir=expr + unique int id: @call_expr +); + +@checked_cast_expr = + @conditional_checked_cast_expr +| @forced_checked_cast_expr +| @is_expr +; + +class_metatype_to_object_exprs( //dir=expr + unique int id: @class_metatype_to_object_expr +); + +closure_exprs( //dir=expr + unique int id: @closure_expr +); + +coerce_exprs( //dir=expr + unique int id: @coerce_expr +); + +collection_upcast_conversion_exprs( //dir=expr + unique int id: @collection_upcast_conversion_expr +); + +conditional_bridge_from_obj_c_exprs( //dir=expr + unique int id: @conditional_bridge_from_obj_c_expr +); + +covariant_function_conversion_exprs( //dir=expr + unique int id: @covariant_function_conversion_expr +); + +covariant_return_conversion_exprs( //dir=expr + unique int id: @covariant_return_conversion_expr +); + +derived_to_base_exprs( //dir=expr + unique int id: @derived_to_base_expr +); + +destructure_tuple_exprs( //dir=expr + unique int id: @destructure_tuple_expr +); + +dictionary_exprs( //dir=expr + unique int id: @dictionary_expr +); + +#keyset[id, index] +dictionary_expr_elements( //dir=expr + int id: @dictionary_expr ref, + int index: int ref, + int element: @expr_or_none ref +); + +differentiable_function_exprs( //dir=expr + unique int id: @differentiable_function_expr +); + +differentiable_function_extract_original_exprs( //dir=expr + unique int id: @differentiable_function_extract_original_expr +); + +dot_self_exprs( //dir=expr + unique int id: @dot_self_expr +); + +@dynamic_lookup_expr = + @dynamic_member_ref_expr +| @dynamic_subscript_expr +; + +erasure_exprs( //dir=expr + unique int id: @erasure_expr +); + +existential_metatype_to_object_exprs( //dir=expr + unique int id: @existential_metatype_to_object_expr +); + +force_try_exprs( //dir=expr + unique int id: @force_try_expr +); + +foreign_object_conversion_exprs( //dir=expr + unique int id: @foreign_object_conversion_expr +); + +function_conversion_exprs( //dir=expr + unique int id: @function_conversion_expr +); + +in_out_to_pointer_exprs( //dir=expr + unique int id: @in_out_to_pointer_expr +); + +inject_into_optional_exprs( //dir=expr + unique int id: @inject_into_optional_expr +); + +interpolated_string_literal_exprs( //dir=expr + unique int id: @interpolated_string_literal_expr +); + +#keyset[id] +interpolated_string_literal_expr_interpolation_exprs( //dir=expr + int id: @interpolated_string_literal_expr ref, + int interpolation_expr: @opaque_value_expr_or_none ref +); + +#keyset[id] +interpolated_string_literal_expr_interpolation_count_exprs( //dir=expr + int id: @interpolated_string_literal_expr ref, + int interpolation_count_expr: @expr_or_none ref +); + +#keyset[id] +interpolated_string_literal_expr_literal_capacity_exprs( //dir=expr + int id: @interpolated_string_literal_expr ref, + int literal_capacity_expr: @expr_or_none ref +); + +#keyset[id] +interpolated_string_literal_expr_appending_exprs( //dir=expr + int id: @interpolated_string_literal_expr ref, + int appending_expr: @tap_expr_or_none ref +); + +linear_function_exprs( //dir=expr + unique int id: @linear_function_expr +); + +linear_function_extract_original_exprs( //dir=expr + unique int id: @linear_function_extract_original_expr +); + +linear_to_differentiable_function_exprs( //dir=expr + unique int id: @linear_to_differentiable_function_expr +); + +load_exprs( //dir=expr + unique int id: @load_expr +); + +member_ref_exprs( //dir=expr + unique int id: @member_ref_expr +); + +#keyset[id] +member_ref_expr_has_direct_to_storage_semantics( //dir=expr + int id: @member_ref_expr ref +); + +#keyset[id] +member_ref_expr_has_direct_to_implementation_semantics( //dir=expr + int id: @member_ref_expr ref +); + +#keyset[id] +member_ref_expr_has_ordinary_semantics( //dir=expr + int id: @member_ref_expr ref +); + +metatype_conversion_exprs( //dir=expr + unique int id: @metatype_conversion_expr +); + +method_ref_exprs( //dir=expr + unique int id: @method_ref_expr +); + +nil_literal_exprs( //dir=expr + unique int id: @nil_literal_expr +); + +object_literal_exprs( //dir=expr + unique int id: @object_literal_expr, + int kind: int ref +); + +#keyset[id, index] +object_literal_expr_arguments( //dir=expr + int id: @object_literal_expr ref, + int index: int ref, + int argument: @argument_or_none ref +); + +optional_try_exprs( //dir=expr + unique int id: @optional_try_expr +); + +paren_exprs( //dir=expr + unique int id: @paren_expr +); + +pointer_to_pointer_exprs( //dir=expr + unique int id: @pointer_to_pointer_expr +); + +postfix_unary_exprs( //dir=expr + unique int id: @postfix_unary_expr +); + +prefix_unary_exprs( //dir=expr + unique int id: @prefix_unary_expr +); + +protocol_metatype_to_object_exprs( //dir=expr + unique int id: @protocol_metatype_to_object_expr +); + +regex_literal_exprs( //dir=expr + unique int id: @regex_literal_expr +); + +@self_apply_expr = + @constructor_ref_call_expr +| @dot_syntax_call_expr +; + +#keyset[id] +self_apply_exprs( //dir=expr + int id: @self_apply_expr ref, + int base: @expr_or_none ref +); + +string_to_pointer_exprs( //dir=expr + unique int id: @string_to_pointer_expr +); + +subscript_exprs( //dir=expr + unique int id: @subscript_expr +); + +#keyset[id, index] +subscript_expr_arguments( //dir=expr + int id: @subscript_expr ref, + int index: int ref, + int argument: @argument_or_none ref +); + +#keyset[id] +subscript_expr_has_direct_to_storage_semantics( //dir=expr + int id: @subscript_expr ref +); + +#keyset[id] +subscript_expr_has_direct_to_implementation_semantics( //dir=expr + int id: @subscript_expr ref +); + +#keyset[id] +subscript_expr_has_ordinary_semantics( //dir=expr + int id: @subscript_expr ref +); + +try_exprs( //dir=expr + unique int id: @try_expr +); + +underlying_to_opaque_exprs( //dir=expr + unique int id: @underlying_to_opaque_expr +); + +unevaluated_instance_exprs( //dir=expr + unique int id: @unevaluated_instance_expr +); + +unresolved_member_chain_result_exprs( //dir=expr + unique int id: @unresolved_member_chain_result_expr +); + +unresolved_type_conversion_exprs( //dir=expr + unique int id: @unresolved_type_conversion_expr +); + +boolean_literal_exprs( //dir=expr + unique int id: @boolean_literal_expr, + boolean value: boolean ref +); + +conditional_checked_cast_exprs( //dir=expr + unique int id: @conditional_checked_cast_expr +); + +constructor_ref_call_exprs( //dir=expr + unique int id: @constructor_ref_call_expr +); + +dot_syntax_call_exprs( //dir=expr + unique int id: @dot_syntax_call_expr +); + +dynamic_member_ref_exprs( //dir=expr + unique int id: @dynamic_member_ref_expr +); + +dynamic_subscript_exprs( //dir=expr + unique int id: @dynamic_subscript_expr +); + +forced_checked_cast_exprs( //dir=expr + unique int id: @forced_checked_cast_expr +); + +is_exprs( //dir=expr + unique int id: @is_expr +); + +magic_identifier_literal_exprs( //dir=expr + unique int id: @magic_identifier_literal_expr, + string kind: string ref +); + +@number_literal_expr = + @float_literal_expr +| @integer_literal_expr +; + +string_literal_exprs( //dir=expr + unique int id: @string_literal_expr, + string value: string ref +); + +float_literal_exprs( //dir=expr + unique int id: @float_literal_expr, + string string_value: string ref +); + +integer_literal_exprs( //dir=expr + unique int id: @integer_literal_expr, + string string_value: string ref +); + +@pattern = + @any_pattern +| @binding_pattern +| @bool_pattern +| @enum_element_pattern +| @expr_pattern +| @is_pattern +| @named_pattern +| @optional_some_pattern +| @paren_pattern +| @tuple_pattern +| @typed_pattern +; + +any_patterns( //dir=pattern + unique int id: @any_pattern +); + +binding_patterns( //dir=pattern + unique int id: @binding_pattern, + int sub_pattern: @pattern_or_none ref +); + +bool_patterns( //dir=pattern + unique int id: @bool_pattern, + boolean value: boolean ref +); + +enum_element_patterns( //dir=pattern + unique int id: @enum_element_pattern, + int element: @enum_element_decl_or_none ref +); + +#keyset[id] +enum_element_pattern_sub_patterns( //dir=pattern + int id: @enum_element_pattern ref, + int sub_pattern: @pattern_or_none ref +); + +expr_patterns( //dir=pattern + unique int id: @expr_pattern, + int sub_expr: @expr_or_none ref +); + +is_patterns( //dir=pattern + unique int id: @is_pattern +); + +#keyset[id] +is_pattern_cast_type_reprs( //dir=pattern + int id: @is_pattern ref, + int cast_type_repr: @type_repr_or_none ref +); + +#keyset[id] +is_pattern_sub_patterns( //dir=pattern + int id: @is_pattern ref, + int sub_pattern: @pattern_or_none ref +); + +named_patterns( //dir=pattern + unique int id: @named_pattern, + string name: string ref +); + +optional_some_patterns( //dir=pattern + unique int id: @optional_some_pattern, + int sub_pattern: @pattern_or_none ref +); + +paren_patterns( //dir=pattern + unique int id: @paren_pattern, + int sub_pattern: @pattern_or_none ref +); + +tuple_patterns( //dir=pattern + unique int id: @tuple_pattern +); + +#keyset[id, index] +tuple_pattern_elements( //dir=pattern + int id: @tuple_pattern ref, + int index: int ref, + int element: @pattern_or_none ref +); + +typed_patterns( //dir=pattern + unique int id: @typed_pattern, + int sub_pattern: @pattern_or_none ref +); + +#keyset[id] +typed_pattern_type_reprs( //dir=pattern + int id: @typed_pattern ref, + int type_repr: @type_repr_or_none ref +); + +case_label_items( //dir=stmt + unique int id: @case_label_item, + int pattern: @pattern_or_none ref +); + +#keyset[id] +case_label_item_guards( //dir=stmt + int id: @case_label_item ref, + int guard: @expr_or_none ref +); + +condition_elements( //dir=stmt + unique int id: @condition_element +); + +#keyset[id] +condition_element_booleans( //dir=stmt + int id: @condition_element ref, + int boolean_: @expr_or_none ref +); + +#keyset[id] +condition_element_patterns( //dir=stmt + int id: @condition_element ref, + int pattern: @pattern_or_none ref +); + +#keyset[id] +condition_element_initializers( //dir=stmt + int id: @condition_element ref, + int initializer: @expr_or_none ref +); + +@stmt = + @brace_stmt +| @break_stmt +| @case_stmt +| @continue_stmt +| @defer_stmt +| @fail_stmt +| @fallthrough_stmt +| @labeled_stmt +| @pound_assert_stmt +| @return_stmt +| @throw_stmt +| @yield_stmt +; + +stmt_conditions( //dir=stmt + unique int id: @stmt_condition +); + +#keyset[id, index] +stmt_condition_elements( //dir=stmt + int id: @stmt_condition ref, + int index: int ref, + int element: @condition_element_or_none ref +); + +brace_stmts( //dir=stmt + unique int id: @brace_stmt +); + +#keyset[id, index] +brace_stmt_elements( //dir=stmt + int id: @brace_stmt ref, + int index: int ref, + int element: @ast_node_or_none ref +); + +break_stmts( //dir=stmt + unique int id: @break_stmt +); + +#keyset[id] +break_stmt_target_names( //dir=stmt + int id: @break_stmt ref, + string target_name: string ref +); + +#keyset[id] +break_stmt_targets( //dir=stmt + int id: @break_stmt ref, + int target: @stmt_or_none ref +); + +case_stmts( //dir=stmt + unique int id: @case_stmt, + int body: @stmt_or_none ref +); + +#keyset[id, index] +case_stmt_labels( //dir=stmt + int id: @case_stmt ref, + int index: int ref, + int label: @case_label_item_or_none ref +); + +#keyset[id, index] +case_stmt_variables( //dir=stmt + int id: @case_stmt ref, + int index: int ref, + int variable: @var_decl_or_none ref +); + +continue_stmts( //dir=stmt + unique int id: @continue_stmt +); + +#keyset[id] +continue_stmt_target_names( //dir=stmt + int id: @continue_stmt ref, + string target_name: string ref +); + +#keyset[id] +continue_stmt_targets( //dir=stmt + int id: @continue_stmt ref, + int target: @stmt_or_none ref +); + +defer_stmts( //dir=stmt + unique int id: @defer_stmt, + int body: @brace_stmt_or_none ref +); + +fail_stmts( //dir=stmt + unique int id: @fail_stmt +); + +fallthrough_stmts( //dir=stmt + unique int id: @fallthrough_stmt, + int fallthrough_source: @case_stmt_or_none ref, + int fallthrough_dest: @case_stmt_or_none ref +); + +@labeled_stmt = + @do_catch_stmt +| @do_stmt +| @for_each_stmt +| @labeled_conditional_stmt +| @repeat_while_stmt +| @switch_stmt +; + +#keyset[id] +labeled_stmt_labels( //dir=stmt + int id: @labeled_stmt ref, + string label: string ref +); + +pound_assert_stmts( //dir=stmt + unique int id: @pound_assert_stmt, + int condition: @expr_or_none ref, + string message: string ref +); + +return_stmts( //dir=stmt + unique int id: @return_stmt +); + +#keyset[id] +return_stmt_results( //dir=stmt + int id: @return_stmt ref, + int result: @expr_or_none ref +); + +throw_stmts( //dir=stmt + unique int id: @throw_stmt, + int sub_expr: @expr_or_none ref +); + +yield_stmts( //dir=stmt + unique int id: @yield_stmt +); + +#keyset[id, index] +yield_stmt_results( //dir=stmt + int id: @yield_stmt ref, + int index: int ref, + int result: @expr_or_none ref +); + +do_catch_stmts( //dir=stmt + unique int id: @do_catch_stmt, + int body: @stmt_or_none ref +); + +#keyset[id, index] +do_catch_stmt_catches( //dir=stmt + int id: @do_catch_stmt ref, + int index: int ref, + int catch: @case_stmt_or_none ref +); + +do_stmts( //dir=stmt + unique int id: @do_stmt, + int body: @brace_stmt_or_none ref +); + +for_each_stmts( //dir=stmt + unique int id: @for_each_stmt, + int pattern: @pattern_or_none ref, + int sequence: @expr_or_none ref, + int body: @brace_stmt_or_none ref +); + +#keyset[id] +for_each_stmt_wheres( //dir=stmt + int id: @for_each_stmt ref, + int where: @expr_or_none ref +); + +@labeled_conditional_stmt = + @guard_stmt +| @if_stmt +| @while_stmt +; + +#keyset[id] +labeled_conditional_stmts( //dir=stmt + int id: @labeled_conditional_stmt ref, + int condition: @stmt_condition_or_none ref +); + +repeat_while_stmts( //dir=stmt + unique int id: @repeat_while_stmt, + int condition: @expr_or_none ref, + int body: @stmt_or_none ref +); + +switch_stmts( //dir=stmt + unique int id: @switch_stmt, + int expr: @expr_or_none ref +); + +#keyset[id, index] +switch_stmt_cases( //dir=stmt + int id: @switch_stmt ref, + int index: int ref, + int case_: @case_stmt_or_none ref +); + +guard_stmts( //dir=stmt + unique int id: @guard_stmt, + int body: @brace_stmt_or_none ref +); + +if_stmts( //dir=stmt + unique int id: @if_stmt, + int then: @stmt_or_none ref +); + +#keyset[id] +if_stmt_elses( //dir=stmt + int id: @if_stmt ref, + int else: @stmt_or_none ref +); + +while_stmts( //dir=stmt + unique int id: @while_stmt, + int body: @stmt_or_none ref +); + +@type = + @any_function_type +| @any_generic_type +| @any_metatype_type +| @builtin_type +| @dependent_member_type +| @dynamic_self_type +| @error_type +| @existential_type +| @in_out_type +| @l_value_type +| @module_type +| @parameterized_protocol_type +| @protocol_composition_type +| @reference_storage_type +| @substitutable_type +| @sugar_type +| @tuple_type +| @unresolved_type +; + +#keyset[id] +types( //dir=type + int id: @type ref, + string name: string ref, + int canonical_type: @type_or_none ref +); + +type_reprs( //dir=type + unique int id: @type_repr, + int type_: @type_or_none ref +); + +@any_function_type = + @function_type +| @generic_function_type +; + +#keyset[id] +any_function_types( //dir=type + int id: @any_function_type ref, + int result: @type_or_none ref +); + +#keyset[id, index] +any_function_type_param_types( //dir=type + int id: @any_function_type ref, + int index: int ref, + int param_type: @type_or_none ref +); + +#keyset[id, index] +any_function_type_param_labels( //dir=type + int id: @any_function_type ref, + int index: int ref, + string param_label: string ref +); + +#keyset[id] +any_function_type_is_throwing( //dir=type + int id: @any_function_type ref +); + +#keyset[id] +any_function_type_is_async( //dir=type + int id: @any_function_type ref +); + +@any_generic_type = + @nominal_or_bound_generic_nominal_type +| @unbound_generic_type +; + +#keyset[id] +any_generic_types( //dir=type + int id: @any_generic_type ref, + int declaration: @decl_or_none ref +); + +#keyset[id] +any_generic_type_parents( //dir=type + int id: @any_generic_type ref, + int parent: @type_or_none ref +); + +@any_metatype_type = + @existential_metatype_type +| @metatype_type +; + +@builtin_type = + @any_builtin_integer_type +| @builtin_bridge_object_type +| @builtin_default_actor_storage_type +| @builtin_executor_type +| @builtin_float_type +| @builtin_job_type +| @builtin_native_object_type +| @builtin_raw_pointer_type +| @builtin_raw_unsafe_continuation_type +| @builtin_unsafe_value_buffer_type +| @builtin_vector_type +; + +dependent_member_types( //dir=type + unique int id: @dependent_member_type, + int base_type: @type_or_none ref, + int associated_type_decl: @associated_type_decl_or_none ref +); + +dynamic_self_types( //dir=type + unique int id: @dynamic_self_type, + int static_self_type: @type_or_none ref +); + +error_types( //dir=type + unique int id: @error_type +); + +existential_types( //dir=type + unique int id: @existential_type, + int constraint: @type_or_none ref +); + +in_out_types( //dir=type + unique int id: @in_out_type, + int object_type: @type_or_none ref +); + +l_value_types( //dir=type + unique int id: @l_value_type, + int object_type: @type_or_none ref +); + +module_types( //dir=type + unique int id: @module_type, + int module: @module_decl_or_none ref +); + +parameterized_protocol_types( //dir=type + unique int id: @parameterized_protocol_type, + int base: @protocol_type_or_none ref +); + +#keyset[id, index] +parameterized_protocol_type_args( //dir=type + int id: @parameterized_protocol_type ref, + int index: int ref, + int arg: @type_or_none ref +); + +protocol_composition_types( //dir=type + unique int id: @protocol_composition_type +); + +#keyset[id, index] +protocol_composition_type_members( //dir=type + int id: @protocol_composition_type ref, + int index: int ref, + int member: @type_or_none ref +); + +@reference_storage_type = + @unmanaged_storage_type +| @unowned_storage_type +| @weak_storage_type +; + +#keyset[id] +reference_storage_types( //dir=type + int id: @reference_storage_type ref, + int referent_type: @type_or_none ref +); + +@substitutable_type = + @archetype_type +| @generic_type_param_type +; + +@sugar_type = + @paren_type +| @syntax_sugar_type +| @type_alias_type +; + +tuple_types( //dir=type + unique int id: @tuple_type +); + +#keyset[id, index] +tuple_type_types( //dir=type + int id: @tuple_type ref, + int index: int ref, + int type_: @type_or_none ref +); + +#keyset[id, index] +tuple_type_names( //dir=type + int id: @tuple_type ref, + int index: int ref, + string name: string ref +); + +unresolved_types( //dir=type + unique int id: @unresolved_type +); + +@any_builtin_integer_type = + @builtin_integer_literal_type +| @builtin_integer_type +; + +@archetype_type = + @opaque_type_archetype_type +| @opened_archetype_type +| @primary_archetype_type +; + +#keyset[id] +archetype_types( //dir=type + int id: @archetype_type ref, + int interface_type: @type_or_none ref +); + +#keyset[id] +archetype_type_superclasses( //dir=type + int id: @archetype_type ref, + int superclass: @type_or_none ref +); + +#keyset[id, index] +archetype_type_protocols( //dir=type + int id: @archetype_type ref, + int index: int ref, + int protocol: @protocol_decl_or_none ref +); + +builtin_bridge_object_types( //dir=type + unique int id: @builtin_bridge_object_type +); + +builtin_default_actor_storage_types( //dir=type + unique int id: @builtin_default_actor_storage_type +); + +builtin_executor_types( //dir=type + unique int id: @builtin_executor_type +); + +builtin_float_types( //dir=type + unique int id: @builtin_float_type +); + +builtin_job_types( //dir=type + unique int id: @builtin_job_type +); + +builtin_native_object_types( //dir=type + unique int id: @builtin_native_object_type +); + +builtin_raw_pointer_types( //dir=type + unique int id: @builtin_raw_pointer_type +); + +builtin_raw_unsafe_continuation_types( //dir=type + unique int id: @builtin_raw_unsafe_continuation_type +); + +builtin_unsafe_value_buffer_types( //dir=type + unique int id: @builtin_unsafe_value_buffer_type +); + +builtin_vector_types( //dir=type + unique int id: @builtin_vector_type +); + +existential_metatype_types( //dir=type + unique int id: @existential_metatype_type +); + +function_types( //dir=type + unique int id: @function_type +); + +generic_function_types( //dir=type + unique int id: @generic_function_type +); + +#keyset[id, index] +generic_function_type_generic_params( //dir=type + int id: @generic_function_type ref, + int index: int ref, + int generic_param: @generic_type_param_type_or_none ref +); + +generic_type_param_types( //dir=type + unique int id: @generic_type_param_type +); + +metatype_types( //dir=type + unique int id: @metatype_type +); + +@nominal_or_bound_generic_nominal_type = + @bound_generic_type +| @nominal_type +; + +paren_types( //dir=type + unique int id: @paren_type, + int type_: @type_or_none ref +); + +@syntax_sugar_type = + @dictionary_type +| @unary_syntax_sugar_type +; + +type_alias_types( //dir=type + unique int id: @type_alias_type, + int decl: @type_alias_decl_or_none ref +); + +unbound_generic_types( //dir=type + unique int id: @unbound_generic_type +); + +unmanaged_storage_types( //dir=type + unique int id: @unmanaged_storage_type +); + +unowned_storage_types( //dir=type + unique int id: @unowned_storage_type +); + +weak_storage_types( //dir=type + unique int id: @weak_storage_type +); + +@bound_generic_type = + @bound_generic_class_type +| @bound_generic_enum_type +| @bound_generic_struct_type +; + +#keyset[id, index] +bound_generic_type_arg_types( //dir=type + int id: @bound_generic_type ref, + int index: int ref, + int arg_type: @type_or_none ref +); + +builtin_integer_literal_types( //dir=type + unique int id: @builtin_integer_literal_type +); + +builtin_integer_types( //dir=type + unique int id: @builtin_integer_type +); + +#keyset[id] +builtin_integer_type_widths( //dir=type + int id: @builtin_integer_type ref, + int width: int ref +); + +dictionary_types( //dir=type + unique int id: @dictionary_type, + int key_type: @type_or_none ref, + int value_type: @type_or_none ref +); + +@nominal_type = + @class_type +| @enum_type +| @protocol_type +| @struct_type +; + +opaque_type_archetype_types( //dir=type + unique int id: @opaque_type_archetype_type, + int declaration: @opaque_type_decl_or_none ref +); + +opened_archetype_types( //dir=type + unique int id: @opened_archetype_type +); + +primary_archetype_types( //dir=type + unique int id: @primary_archetype_type +); + +@unary_syntax_sugar_type = + @array_slice_type +| @optional_type +| @variadic_sequence_type +; + +#keyset[id] +unary_syntax_sugar_types( //dir=type + int id: @unary_syntax_sugar_type ref, + int base_type: @type_or_none ref +); + +array_slice_types( //dir=type + unique int id: @array_slice_type +); + +bound_generic_class_types( //dir=type + unique int id: @bound_generic_class_type +); + +bound_generic_enum_types( //dir=type + unique int id: @bound_generic_enum_type +); + +bound_generic_struct_types( //dir=type + unique int id: @bound_generic_struct_type +); + +class_types( //dir=type + unique int id: @class_type +); + +enum_types( //dir=type + unique int id: @enum_type +); + +optional_types( //dir=type + unique int id: @optional_type +); + +protocol_types( //dir=type + unique int id: @protocol_type +); + +struct_types( //dir=type + unique int id: @struct_type +); + +variadic_sequence_types( //dir=type + unique int id: @variadic_sequence_type +); + +@abstract_function_decl_or_none = + @abstract_function_decl +| @unspecified_element +; + +@accessor_decl_or_none = + @accessor_decl +| @unspecified_element +; + +@argument_or_none = + @argument +| @unspecified_element +; + +@associated_type_decl_or_none = + @associated_type_decl +| @unspecified_element +; + +@ast_node_or_none = + @ast_node +| @unspecified_element +; + +@brace_stmt_or_none = + @brace_stmt +| @unspecified_element +; + +@case_label_item_or_none = + @case_label_item +| @unspecified_element +; + +@case_stmt_or_none = + @case_stmt +| @unspecified_element +; + +@closure_expr_or_none = + @closure_expr +| @unspecified_element +; + +@condition_element_or_none = + @condition_element +| @unspecified_element +; + +@constructor_decl_or_none = + @constructor_decl +| @unspecified_element +; + +@decl_or_none = + @decl +| @unspecified_element +; + +@enum_element_decl_or_none = + @enum_element_decl +| @unspecified_element +; + +@expr_or_none = + @expr +| @unspecified_element +; + +@file_or_none = + @file +| @unspecified_element +; + +@generic_type_param_decl_or_none = + @generic_type_param_decl +| @unspecified_element +; + +@generic_type_param_type_or_none = + @generic_type_param_type +| @unspecified_element +; + +@location_or_none = + @location +| @unspecified_element +; + +@module_decl_or_none = + @module_decl +| @unspecified_element +; + +@nominal_type_decl_or_none = + @nominal_type_decl +| @unspecified_element +; + +@opaque_type_decl_or_none = + @opaque_type_decl +| @unspecified_element +; + +@opaque_value_expr_or_none = + @opaque_value_expr +| @unspecified_element +; + +@param_decl_or_none = + @param_decl +| @unspecified_element +; + +@pattern_or_none = + @pattern +| @unspecified_element +; + +@pattern_binding_decl_or_none = + @pattern_binding_decl +| @unspecified_element +; + +@precedence_group_decl_or_none = + @precedence_group_decl +| @unspecified_element +; + +@protocol_decl_or_none = + @protocol_decl +| @unspecified_element +; + +@protocol_type_or_none = + @protocol_type +| @unspecified_element +; + +@stmt_or_none = + @stmt +| @unspecified_element +; + +@stmt_condition_or_none = + @stmt_condition +| @unspecified_element +; + +@string_literal_expr_or_none = + @string_literal_expr +| @unspecified_element +; + +@tap_expr_or_none = + @tap_expr +| @unspecified_element +; + +@type_or_none = + @type +| @unspecified_element +; + +@type_alias_decl_or_none = + @type_alias_decl +| @unspecified_element +; + +@type_repr_or_none = + @type_repr +| @unspecified_element +; + +@value_decl_or_none = + @unspecified_element +| @value_decl +; + +@var_decl_or_none = + @unspecified_element +| @var_decl +; diff --git a/swift/downgrades/qlpack.yml b/swift/downgrades/qlpack.yml new file mode 100644 index 00000000000..3fc919124df --- /dev/null +++ b/swift/downgrades/qlpack.yml @@ -0,0 +1,4 @@ +name: codeql/swift-downgrades +groups: swift +downgrades: . +library: true diff --git a/swift/ql/lib/upgrades/initial/swift.dbscheme b/swift/ql/lib/upgrades/initial/swift.dbscheme new file mode 100644 index 00000000000..ceca289a0ff --- /dev/null +++ b/swift/ql/lib/upgrades/initial/swift.dbscheme @@ -0,0 +1,2493 @@ +// generated by codegen/codegen.py + +// from prefix.dbscheme +/** + * The source location of the snapshot. + */ +sourceLocationPrefix( + string prefix: string ref +); + + +// from schema.py + +@element = + @callable +| @file +| @generic_context +| @iterable_decl_context +| @locatable +| @location +| @type +; + +#keyset[id] +element_is_unknown( + int id: @element ref +); + +@callable = + @abstract_closure_expr +| @abstract_function_decl +; + +#keyset[id] +callable_self_params( + int id: @callable ref, + int self_param: @param_decl_or_none ref +); + +#keyset[id, index] +callable_params( + int id: @callable ref, + int index: int ref, + int param: @param_decl_or_none ref +); + +#keyset[id] +callable_bodies( + int id: @callable ref, + int body: @brace_stmt_or_none ref +); + +@file = + @db_file +| @unknown_file +; + +#keyset[id] +files( + int id: @file ref, + string name: string ref +); + +@locatable = + @argument +| @ast_node +| @comment +| @diagnostics +| @error_element +; + +#keyset[id] +locatable_locations( + int id: @locatable ref, + int location: @location_or_none ref +); + +@location = + @db_location +| @unknown_location +; + +#keyset[id] +locations( + int id: @location ref, + int file: @file_or_none ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +@ast_node = + @case_label_item +| @condition_element +| @decl +| @expr +| @pattern +| @stmt +| @stmt_condition +| @type_repr +; + +comments( + unique int id: @comment, + string text: string ref +); + +db_files( + unique int id: @db_file +); + +db_locations( + unique int id: @db_location +); + +diagnostics( + unique int id: @diagnostics, + string text: string ref, + int kind: int ref +); + +@error_element = + @error_expr +| @error_type +| @overloaded_decl_ref_expr +| @unresolved_decl_ref_expr +| @unresolved_dot_expr +| @unresolved_member_chain_result_expr +| @unresolved_member_expr +| @unresolved_pattern_expr +| @unresolved_specialize_expr +| @unresolved_type +| @unresolved_type_conversion_expr +| @unspecified_element +; + +unknown_files( + unique int id: @unknown_file +); + +unknown_locations( + unique int id: @unknown_location +); + +unspecified_elements( + unique int id: @unspecified_element, + string property: string ref, + string error: string ref +); + +#keyset[id] +unspecified_element_parents( + int id: @unspecified_element ref, + int parent: @element ref +); + +#keyset[id] +unspecified_element_indices( + int id: @unspecified_element ref, + int index: int ref +); + +@decl = + @enum_case_decl +| @extension_decl +| @if_config_decl +| @import_decl +| @missing_member_decl +| @operator_decl +| @pattern_binding_decl +| @pound_diagnostic_decl +| @precedence_group_decl +| @top_level_code_decl +| @value_decl +; + +#keyset[id] +decls( //dir=decl + int id: @decl ref, + int module: @module_decl_or_none ref +); + +@generic_context = + @abstract_function_decl +| @extension_decl +| @generic_type_decl +| @subscript_decl +; + +#keyset[id, index] +generic_context_generic_type_params( //dir=decl + int id: @generic_context ref, + int index: int ref, + int generic_type_param: @generic_type_param_decl_or_none ref +); + +@iterable_decl_context = + @extension_decl +| @nominal_type_decl +; + +#keyset[id, index] +iterable_decl_context_members( //dir=decl + int id: @iterable_decl_context ref, + int index: int ref, + int member: @decl_or_none ref +); + +enum_case_decls( //dir=decl + unique int id: @enum_case_decl +); + +#keyset[id, index] +enum_case_decl_elements( //dir=decl + int id: @enum_case_decl ref, + int index: int ref, + int element: @enum_element_decl_or_none ref +); + +extension_decls( //dir=decl + unique int id: @extension_decl, + int extended_type_decl: @nominal_type_decl_or_none ref +); + +if_config_decls( //dir=decl + unique int id: @if_config_decl +); + +#keyset[id, index] +if_config_decl_active_elements( //dir=decl + int id: @if_config_decl ref, + int index: int ref, + int active_element: @ast_node_or_none ref +); + +import_decls( //dir=decl + unique int id: @import_decl +); + +#keyset[id] +import_decl_is_exported( //dir=decl + int id: @import_decl ref +); + +#keyset[id] +import_decl_imported_modules( //dir=decl + int id: @import_decl ref, + int imported_module: @module_decl_or_none ref +); + +#keyset[id, index] +import_decl_declarations( //dir=decl + int id: @import_decl ref, + int index: int ref, + int declaration: @value_decl_or_none ref +); + +missing_member_decls( //dir=decl + unique int id: @missing_member_decl, + string name: string ref +); + +@operator_decl = + @infix_operator_decl +| @postfix_operator_decl +| @prefix_operator_decl +; + +#keyset[id] +operator_decls( //dir=decl + int id: @operator_decl ref, + string name: string ref +); + +pattern_binding_decls( //dir=decl + unique int id: @pattern_binding_decl +); + +#keyset[id, index] +pattern_binding_decl_inits( //dir=decl + int id: @pattern_binding_decl ref, + int index: int ref, + int init: @expr_or_none ref +); + +#keyset[id, index] +pattern_binding_decl_patterns( //dir=decl + int id: @pattern_binding_decl ref, + int index: int ref, + int pattern: @pattern_or_none ref +); + +pound_diagnostic_decls( //dir=decl + unique int id: @pound_diagnostic_decl, + int kind: int ref, + int message: @string_literal_expr_or_none ref +); + +precedence_group_decls( //dir=decl + unique int id: @precedence_group_decl +); + +top_level_code_decls( //dir=decl + unique int id: @top_level_code_decl, + int body: @brace_stmt_or_none ref +); + +@value_decl = + @abstract_function_decl +| @abstract_storage_decl +| @enum_element_decl +| @type_decl +; + +#keyset[id] +value_decls( //dir=decl + int id: @value_decl ref, + int interface_type: @type_or_none ref +); + +@abstract_function_decl = + @constructor_decl +| @destructor_decl +| @func_decl +; + +#keyset[id] +abstract_function_decls( //dir=decl + int id: @abstract_function_decl ref, + string name: string ref +); + +@abstract_storage_decl = + @subscript_decl +| @var_decl +; + +#keyset[id, index] +abstract_storage_decl_accessor_decls( //dir=decl + int id: @abstract_storage_decl ref, + int index: int ref, + int accessor_decl: @accessor_decl_or_none ref +); + +enum_element_decls( //dir=decl + unique int id: @enum_element_decl, + string name: string ref +); + +#keyset[id, index] +enum_element_decl_params( //dir=decl + int id: @enum_element_decl ref, + int index: int ref, + int param: @param_decl_or_none ref +); + +infix_operator_decls( //dir=decl + unique int id: @infix_operator_decl +); + +#keyset[id] +infix_operator_decl_precedence_groups( //dir=decl + int id: @infix_operator_decl ref, + int precedence_group: @precedence_group_decl_or_none ref +); + +postfix_operator_decls( //dir=decl + unique int id: @postfix_operator_decl +); + +prefix_operator_decls( //dir=decl + unique int id: @prefix_operator_decl +); + +@type_decl = + @abstract_type_param_decl +| @generic_type_decl +| @module_decl +; + +#keyset[id] +type_decls( //dir=decl + int id: @type_decl ref, + string name: string ref +); + +#keyset[id, index] +type_decl_base_types( //dir=decl + int id: @type_decl ref, + int index: int ref, + int base_type: @type_or_none ref +); + +@abstract_type_param_decl = + @associated_type_decl +| @generic_type_param_decl +; + +constructor_decls( //dir=decl + unique int id: @constructor_decl +); + +destructor_decls( //dir=decl + unique int id: @destructor_decl +); + +@func_decl = + @accessor_decl +| @concrete_func_decl +; + +@generic_type_decl = + @nominal_type_decl +| @opaque_type_decl +| @type_alias_decl +; + +module_decls( //dir=decl + unique int id: @module_decl +); + +#keyset[id] +module_decl_is_builtin_module( //dir=decl + int id: @module_decl ref +); + +#keyset[id] +module_decl_is_system_module( //dir=decl + int id: @module_decl ref +); + +#keyset[id, index] +module_decl_imported_modules( //dir=decl + int id: @module_decl ref, + int index: int ref, + int imported_module: @module_decl_or_none ref +); + +#keyset[id, index] +module_decl_exported_modules( //dir=decl + int id: @module_decl ref, + int index: int ref, + int exported_module: @module_decl_or_none ref +); + +subscript_decls( //dir=decl + unique int id: @subscript_decl, + int element_type: @type_or_none ref +); + +#keyset[id, index] +subscript_decl_params( //dir=decl + int id: @subscript_decl ref, + int index: int ref, + int param: @param_decl_or_none ref +); + +@var_decl = + @concrete_var_decl +| @param_decl +; + +#keyset[id] +var_decls( //dir=decl + int id: @var_decl ref, + string name: string ref, + int type_: @type_or_none ref +); + +#keyset[id] +var_decl_attached_property_wrapper_types( //dir=decl + int id: @var_decl ref, + int attached_property_wrapper_type: @type_or_none ref +); + +#keyset[id] +var_decl_parent_patterns( //dir=decl + int id: @var_decl ref, + int parent_pattern: @pattern_or_none ref +); + +#keyset[id] +var_decl_parent_initializers( //dir=decl + int id: @var_decl ref, + int parent_initializer: @expr_or_none ref +); + +#keyset[id] +var_decl_property_wrapper_backing_var_bindings( //dir=decl + int id: @var_decl ref, + int property_wrapper_backing_var_binding: @pattern_binding_decl_or_none ref +); + +#keyset[id] +var_decl_property_wrapper_backing_vars( //dir=decl + int id: @var_decl ref, + int property_wrapper_backing_var: @var_decl_or_none ref +); + +#keyset[id] +var_decl_property_wrapper_projection_var_bindings( //dir=decl + int id: @var_decl ref, + int property_wrapper_projection_var_binding: @pattern_binding_decl_or_none ref +); + +#keyset[id] +var_decl_property_wrapper_projection_vars( //dir=decl + int id: @var_decl ref, + int property_wrapper_projection_var: @var_decl_or_none ref +); + +accessor_decls( //dir=decl + unique int id: @accessor_decl +); + +#keyset[id] +accessor_decl_is_getter( //dir=decl + int id: @accessor_decl ref +); + +#keyset[id] +accessor_decl_is_setter( //dir=decl + int id: @accessor_decl ref +); + +#keyset[id] +accessor_decl_is_will_set( //dir=decl + int id: @accessor_decl ref +); + +#keyset[id] +accessor_decl_is_did_set( //dir=decl + int id: @accessor_decl ref +); + +associated_type_decls( //dir=decl + unique int id: @associated_type_decl +); + +concrete_func_decls( //dir=decl + unique int id: @concrete_func_decl +); + +concrete_var_decls( //dir=decl + unique int id: @concrete_var_decl, + int introducer_int: int ref +); + +generic_type_param_decls( //dir=decl + unique int id: @generic_type_param_decl +); + +@nominal_type_decl = + @class_decl +| @enum_decl +| @protocol_decl +| @struct_decl +; + +#keyset[id] +nominal_type_decls( //dir=decl + int id: @nominal_type_decl ref, + int type_: @type_or_none ref +); + +opaque_type_decls( //dir=decl + unique int id: @opaque_type_decl, + int naming_declaration: @value_decl_or_none ref +); + +#keyset[id, index] +opaque_type_decl_opaque_generic_params( //dir=decl + int id: @opaque_type_decl ref, + int index: int ref, + int opaque_generic_param: @generic_type_param_type_or_none ref +); + +param_decls( //dir=decl + unique int id: @param_decl +); + +#keyset[id] +param_decl_is_inout( //dir=decl + int id: @param_decl ref +); + +#keyset[id] +param_decl_property_wrapper_local_wrapped_var_bindings( //dir=decl + int id: @param_decl ref, + int property_wrapper_local_wrapped_var_binding: @pattern_binding_decl_or_none ref +); + +#keyset[id] +param_decl_property_wrapper_local_wrapped_vars( //dir=decl + int id: @param_decl ref, + int property_wrapper_local_wrapped_var: @var_decl_or_none ref +); + +type_alias_decls( //dir=decl + unique int id: @type_alias_decl +); + +class_decls( //dir=decl + unique int id: @class_decl +); + +enum_decls( //dir=decl + unique int id: @enum_decl +); + +protocol_decls( //dir=decl + unique int id: @protocol_decl +); + +struct_decls( //dir=decl + unique int id: @struct_decl +); + +arguments( //dir=expr + unique int id: @argument, + string label: string ref, + int expr: @expr_or_none ref +); + +@expr = + @abstract_closure_expr +| @any_try_expr +| @applied_property_wrapper_expr +| @apply_expr +| @assign_expr +| @bind_optional_expr +| @capture_list_expr +| @collection_expr +| @decl_ref_expr +| @default_argument_expr +| @discard_assignment_expr +| @dot_syntax_base_ignored_expr +| @dynamic_type_expr +| @enum_is_case_expr +| @error_expr +| @explicit_cast_expr +| @force_value_expr +| @identity_expr +| @if_expr +| @implicit_conversion_expr +| @in_out_expr +| @key_path_application_expr +| @key_path_dot_expr +| @key_path_expr +| @lazy_initializer_expr +| @literal_expr +| @lookup_expr +| @make_temporarily_escapable_expr +| @obj_c_selector_expr +| @one_way_expr +| @opaque_value_expr +| @open_existential_expr +| @optional_evaluation_expr +| @other_constructor_decl_ref_expr +| @overloaded_decl_ref_expr +| @property_wrapper_value_placeholder_expr +| @rebind_self_in_constructor_expr +| @sequence_expr +| @super_ref_expr +| @tap_expr +| @tuple_element_expr +| @tuple_expr +| @type_expr +| @unresolved_decl_ref_expr +| @unresolved_dot_expr +| @unresolved_member_expr +| @unresolved_pattern_expr +| @unresolved_specialize_expr +| @vararg_expansion_expr +; + +#keyset[id] +expr_types( //dir=expr + int id: @expr ref, + int type_: @type_or_none ref +); + +@abstract_closure_expr = + @auto_closure_expr +| @closure_expr +; + +@any_try_expr = + @force_try_expr +| @optional_try_expr +| @try_expr +; + +#keyset[id] +any_try_exprs( //dir=expr + int id: @any_try_expr ref, + int sub_expr: @expr_or_none ref +); + +applied_property_wrapper_exprs( //dir=expr + unique int id: @applied_property_wrapper_expr, + int kind: int ref, + int value: @expr_or_none ref, + int param: @param_decl_or_none ref +); + +@apply_expr = + @binary_expr +| @call_expr +| @postfix_unary_expr +| @prefix_unary_expr +| @self_apply_expr +; + +#keyset[id] +apply_exprs( //dir=expr + int id: @apply_expr ref, + int function: @expr_or_none ref +); + +#keyset[id, index] +apply_expr_arguments( //dir=expr + int id: @apply_expr ref, + int index: int ref, + int argument: @argument_or_none ref +); + +assign_exprs( //dir=expr + unique int id: @assign_expr, + int dest: @expr_or_none ref, + int source: @expr_or_none ref +); + +bind_optional_exprs( //dir=expr + unique int id: @bind_optional_expr, + int sub_expr: @expr_or_none ref +); + +capture_list_exprs( //dir=expr + unique int id: @capture_list_expr, + int closure_body: @closure_expr_or_none ref +); + +#keyset[id, index] +capture_list_expr_binding_decls( //dir=expr + int id: @capture_list_expr ref, + int index: int ref, + int binding_decl: @pattern_binding_decl_or_none ref +); + +@collection_expr = + @array_expr +| @dictionary_expr +; + +decl_ref_exprs( //dir=expr + unique int id: @decl_ref_expr, + int decl: @decl_or_none ref +); + +#keyset[id, index] +decl_ref_expr_replacement_types( //dir=expr + int id: @decl_ref_expr ref, + int index: int ref, + int replacement_type: @type_or_none ref +); + +#keyset[id] +decl_ref_expr_has_direct_to_storage_semantics( //dir=expr + int id: @decl_ref_expr ref +); + +#keyset[id] +decl_ref_expr_has_direct_to_implementation_semantics( //dir=expr + int id: @decl_ref_expr ref +); + +#keyset[id] +decl_ref_expr_has_ordinary_semantics( //dir=expr + int id: @decl_ref_expr ref +); + +default_argument_exprs( //dir=expr + unique int id: @default_argument_expr, + int param_decl: @param_decl_or_none ref, + int param_index: int ref +); + +#keyset[id] +default_argument_expr_caller_side_defaults( //dir=expr + int id: @default_argument_expr ref, + int caller_side_default: @expr_or_none ref +); + +discard_assignment_exprs( //dir=expr + unique int id: @discard_assignment_expr +); + +dot_syntax_base_ignored_exprs( //dir=expr + unique int id: @dot_syntax_base_ignored_expr, + int qualifier: @expr_or_none ref, + int sub_expr: @expr_or_none ref +); + +dynamic_type_exprs( //dir=expr + unique int id: @dynamic_type_expr, + int base: @expr_or_none ref +); + +enum_is_case_exprs( //dir=expr + unique int id: @enum_is_case_expr, + int sub_expr: @expr_or_none ref, + int element: @enum_element_decl_or_none ref +); + +error_exprs( //dir=expr + unique int id: @error_expr +); + +@explicit_cast_expr = + @checked_cast_expr +| @coerce_expr +; + +#keyset[id] +explicit_cast_exprs( //dir=expr + int id: @explicit_cast_expr ref, + int sub_expr: @expr_or_none ref +); + +force_value_exprs( //dir=expr + unique int id: @force_value_expr, + int sub_expr: @expr_or_none ref +); + +@identity_expr = + @await_expr +| @dot_self_expr +| @paren_expr +| @unresolved_member_chain_result_expr +; + +#keyset[id] +identity_exprs( //dir=expr + int id: @identity_expr ref, + int sub_expr: @expr_or_none ref +); + +if_exprs( //dir=expr + unique int id: @if_expr, + int condition: @expr_or_none ref, + int then_expr: @expr_or_none ref, + int else_expr: @expr_or_none ref +); + +@implicit_conversion_expr = + @any_hashable_erasure_expr +| @archetype_to_super_expr +| @array_to_pointer_expr +| @bridge_from_obj_c_expr +| @bridge_to_obj_c_expr +| @class_metatype_to_object_expr +| @collection_upcast_conversion_expr +| @conditional_bridge_from_obj_c_expr +| @covariant_function_conversion_expr +| @covariant_return_conversion_expr +| @derived_to_base_expr +| @destructure_tuple_expr +| @differentiable_function_expr +| @differentiable_function_extract_original_expr +| @erasure_expr +| @existential_metatype_to_object_expr +| @foreign_object_conversion_expr +| @function_conversion_expr +| @in_out_to_pointer_expr +| @inject_into_optional_expr +| @linear_function_expr +| @linear_function_extract_original_expr +| @linear_to_differentiable_function_expr +| @load_expr +| @metatype_conversion_expr +| @pointer_to_pointer_expr +| @protocol_metatype_to_object_expr +| @string_to_pointer_expr +| @underlying_to_opaque_expr +| @unevaluated_instance_expr +| @unresolved_type_conversion_expr +; + +#keyset[id] +implicit_conversion_exprs( //dir=expr + int id: @implicit_conversion_expr ref, + int sub_expr: @expr_or_none ref +); + +in_out_exprs( //dir=expr + unique int id: @in_out_expr, + int sub_expr: @expr_or_none ref +); + +key_path_application_exprs( //dir=expr + unique int id: @key_path_application_expr, + int base: @expr_or_none ref, + int key_path: @expr_or_none ref +); + +key_path_dot_exprs( //dir=expr + unique int id: @key_path_dot_expr +); + +key_path_exprs( //dir=expr + unique int id: @key_path_expr +); + +#keyset[id] +key_path_expr_roots( //dir=expr + int id: @key_path_expr ref, + int root: @type_repr_or_none ref +); + +#keyset[id] +key_path_expr_parsed_paths( //dir=expr + int id: @key_path_expr ref, + int parsed_path: @expr_or_none ref +); + +lazy_initializer_exprs( //dir=expr + unique int id: @lazy_initializer_expr, + int sub_expr: @expr_or_none ref +); + +@literal_expr = + @builtin_literal_expr +| @interpolated_string_literal_expr +| @nil_literal_expr +| @object_literal_expr +| @regex_literal_expr +; + +@lookup_expr = + @dynamic_lookup_expr +| @member_ref_expr +| @method_ref_expr +| @subscript_expr +; + +#keyset[id] +lookup_exprs( //dir=expr + int id: @lookup_expr ref, + int base: @expr_or_none ref +); + +#keyset[id] +lookup_expr_members( //dir=expr + int id: @lookup_expr ref, + int member: @decl_or_none ref +); + +make_temporarily_escapable_exprs( //dir=expr + unique int id: @make_temporarily_escapable_expr, + int escaping_closure: @opaque_value_expr_or_none ref, + int nonescaping_closure: @expr_or_none ref, + int sub_expr: @expr_or_none ref +); + +obj_c_selector_exprs( //dir=expr + unique int id: @obj_c_selector_expr, + int sub_expr: @expr_or_none ref, + int method: @abstract_function_decl_or_none ref +); + +one_way_exprs( //dir=expr + unique int id: @one_way_expr, + int sub_expr: @expr_or_none ref +); + +opaque_value_exprs( //dir=expr + unique int id: @opaque_value_expr +); + +open_existential_exprs( //dir=expr + unique int id: @open_existential_expr, + int sub_expr: @expr_or_none ref, + int existential: @expr_or_none ref, + int opaque_expr: @opaque_value_expr_or_none ref +); + +optional_evaluation_exprs( //dir=expr + unique int id: @optional_evaluation_expr, + int sub_expr: @expr_or_none ref +); + +other_constructor_decl_ref_exprs( //dir=expr + unique int id: @other_constructor_decl_ref_expr, + int constructor_decl: @constructor_decl_or_none ref +); + +overloaded_decl_ref_exprs( //dir=expr + unique int id: @overloaded_decl_ref_expr +); + +#keyset[id, index] +overloaded_decl_ref_expr_possible_declarations( //dir=expr + int id: @overloaded_decl_ref_expr ref, + int index: int ref, + int possible_declaration: @value_decl_or_none ref +); + +property_wrapper_value_placeholder_exprs( //dir=expr + unique int id: @property_wrapper_value_placeholder_expr, + int placeholder: @opaque_value_expr_or_none ref +); + +#keyset[id] +property_wrapper_value_placeholder_expr_wrapped_values( //dir=expr + int id: @property_wrapper_value_placeholder_expr ref, + int wrapped_value: @expr_or_none ref +); + +rebind_self_in_constructor_exprs( //dir=expr + unique int id: @rebind_self_in_constructor_expr, + int sub_expr: @expr_or_none ref, + int self: @var_decl_or_none ref +); + +sequence_exprs( //dir=expr + unique int id: @sequence_expr +); + +#keyset[id, index] +sequence_expr_elements( //dir=expr + int id: @sequence_expr ref, + int index: int ref, + int element: @expr_or_none ref +); + +super_ref_exprs( //dir=expr + unique int id: @super_ref_expr, + int self: @var_decl_or_none ref +); + +tap_exprs( //dir=expr + unique int id: @tap_expr, + int body: @brace_stmt_or_none ref, + int var: @var_decl_or_none ref +); + +#keyset[id] +tap_expr_sub_exprs( //dir=expr + int id: @tap_expr ref, + int sub_expr: @expr_or_none ref +); + +tuple_element_exprs( //dir=expr + unique int id: @tuple_element_expr, + int sub_expr: @expr_or_none ref, + int index: int ref +); + +tuple_exprs( //dir=expr + unique int id: @tuple_expr +); + +#keyset[id, index] +tuple_expr_elements( //dir=expr + int id: @tuple_expr ref, + int index: int ref, + int element: @expr_or_none ref +); + +type_exprs( //dir=expr + unique int id: @type_expr +); + +#keyset[id] +type_expr_type_reprs( //dir=expr + int id: @type_expr ref, + int type_repr: @type_repr_or_none ref +); + +unresolved_decl_ref_exprs( //dir=expr + unique int id: @unresolved_decl_ref_expr +); + +#keyset[id] +unresolved_decl_ref_expr_names( //dir=expr + int id: @unresolved_decl_ref_expr ref, + string name: string ref +); + +unresolved_dot_exprs( //dir=expr + unique int id: @unresolved_dot_expr, + int base: @expr_or_none ref, + string name: string ref +); + +unresolved_member_exprs( //dir=expr + unique int id: @unresolved_member_expr, + string name: string ref +); + +unresolved_pattern_exprs( //dir=expr + unique int id: @unresolved_pattern_expr, + int sub_pattern: @pattern_or_none ref +); + +unresolved_specialize_exprs( //dir=expr + unique int id: @unresolved_specialize_expr, + int sub_expr: @expr_or_none ref +); + +vararg_expansion_exprs( //dir=expr + unique int id: @vararg_expansion_expr, + int sub_expr: @expr_or_none ref +); + +any_hashable_erasure_exprs( //dir=expr + unique int id: @any_hashable_erasure_expr +); + +archetype_to_super_exprs( //dir=expr + unique int id: @archetype_to_super_expr +); + +array_exprs( //dir=expr + unique int id: @array_expr +); + +#keyset[id, index] +array_expr_elements( //dir=expr + int id: @array_expr ref, + int index: int ref, + int element: @expr_or_none ref +); + +array_to_pointer_exprs( //dir=expr + unique int id: @array_to_pointer_expr +); + +auto_closure_exprs( //dir=expr + unique int id: @auto_closure_expr +); + +await_exprs( //dir=expr + unique int id: @await_expr +); + +binary_exprs( //dir=expr + unique int id: @binary_expr +); + +bridge_from_obj_c_exprs( //dir=expr + unique int id: @bridge_from_obj_c_expr +); + +bridge_to_obj_c_exprs( //dir=expr + unique int id: @bridge_to_obj_c_expr +); + +@builtin_literal_expr = + @boolean_literal_expr +| @magic_identifier_literal_expr +| @number_literal_expr +| @string_literal_expr +; + +call_exprs( //dir=expr + unique int id: @call_expr +); + +@checked_cast_expr = + @conditional_checked_cast_expr +| @forced_checked_cast_expr +| @is_expr +; + +class_metatype_to_object_exprs( //dir=expr + unique int id: @class_metatype_to_object_expr +); + +closure_exprs( //dir=expr + unique int id: @closure_expr +); + +coerce_exprs( //dir=expr + unique int id: @coerce_expr +); + +collection_upcast_conversion_exprs( //dir=expr + unique int id: @collection_upcast_conversion_expr +); + +conditional_bridge_from_obj_c_exprs( //dir=expr + unique int id: @conditional_bridge_from_obj_c_expr +); + +covariant_function_conversion_exprs( //dir=expr + unique int id: @covariant_function_conversion_expr +); + +covariant_return_conversion_exprs( //dir=expr + unique int id: @covariant_return_conversion_expr +); + +derived_to_base_exprs( //dir=expr + unique int id: @derived_to_base_expr +); + +destructure_tuple_exprs( //dir=expr + unique int id: @destructure_tuple_expr +); + +dictionary_exprs( //dir=expr + unique int id: @dictionary_expr +); + +#keyset[id, index] +dictionary_expr_elements( //dir=expr + int id: @dictionary_expr ref, + int index: int ref, + int element: @expr_or_none ref +); + +differentiable_function_exprs( //dir=expr + unique int id: @differentiable_function_expr +); + +differentiable_function_extract_original_exprs( //dir=expr + unique int id: @differentiable_function_extract_original_expr +); + +dot_self_exprs( //dir=expr + unique int id: @dot_self_expr +); + +@dynamic_lookup_expr = + @dynamic_member_ref_expr +| @dynamic_subscript_expr +; + +erasure_exprs( //dir=expr + unique int id: @erasure_expr +); + +existential_metatype_to_object_exprs( //dir=expr + unique int id: @existential_metatype_to_object_expr +); + +force_try_exprs( //dir=expr + unique int id: @force_try_expr +); + +foreign_object_conversion_exprs( //dir=expr + unique int id: @foreign_object_conversion_expr +); + +function_conversion_exprs( //dir=expr + unique int id: @function_conversion_expr +); + +in_out_to_pointer_exprs( //dir=expr + unique int id: @in_out_to_pointer_expr +); + +inject_into_optional_exprs( //dir=expr + unique int id: @inject_into_optional_expr +); + +interpolated_string_literal_exprs( //dir=expr + unique int id: @interpolated_string_literal_expr +); + +#keyset[id] +interpolated_string_literal_expr_interpolation_exprs( //dir=expr + int id: @interpolated_string_literal_expr ref, + int interpolation_expr: @opaque_value_expr_or_none ref +); + +#keyset[id] +interpolated_string_literal_expr_interpolation_count_exprs( //dir=expr + int id: @interpolated_string_literal_expr ref, + int interpolation_count_expr: @expr_or_none ref +); + +#keyset[id] +interpolated_string_literal_expr_literal_capacity_exprs( //dir=expr + int id: @interpolated_string_literal_expr ref, + int literal_capacity_expr: @expr_or_none ref +); + +#keyset[id] +interpolated_string_literal_expr_appending_exprs( //dir=expr + int id: @interpolated_string_literal_expr ref, + int appending_expr: @tap_expr_or_none ref +); + +linear_function_exprs( //dir=expr + unique int id: @linear_function_expr +); + +linear_function_extract_original_exprs( //dir=expr + unique int id: @linear_function_extract_original_expr +); + +linear_to_differentiable_function_exprs( //dir=expr + unique int id: @linear_to_differentiable_function_expr +); + +load_exprs( //dir=expr + unique int id: @load_expr +); + +member_ref_exprs( //dir=expr + unique int id: @member_ref_expr +); + +#keyset[id] +member_ref_expr_has_direct_to_storage_semantics( //dir=expr + int id: @member_ref_expr ref +); + +#keyset[id] +member_ref_expr_has_direct_to_implementation_semantics( //dir=expr + int id: @member_ref_expr ref +); + +#keyset[id] +member_ref_expr_has_ordinary_semantics( //dir=expr + int id: @member_ref_expr ref +); + +metatype_conversion_exprs( //dir=expr + unique int id: @metatype_conversion_expr +); + +method_ref_exprs( //dir=expr + unique int id: @method_ref_expr +); + +nil_literal_exprs( //dir=expr + unique int id: @nil_literal_expr +); + +object_literal_exprs( //dir=expr + unique int id: @object_literal_expr, + int kind: int ref +); + +#keyset[id, index] +object_literal_expr_arguments( //dir=expr + int id: @object_literal_expr ref, + int index: int ref, + int argument: @argument_or_none ref +); + +optional_try_exprs( //dir=expr + unique int id: @optional_try_expr +); + +paren_exprs( //dir=expr + unique int id: @paren_expr +); + +pointer_to_pointer_exprs( //dir=expr + unique int id: @pointer_to_pointer_expr +); + +postfix_unary_exprs( //dir=expr + unique int id: @postfix_unary_expr +); + +prefix_unary_exprs( //dir=expr + unique int id: @prefix_unary_expr +); + +protocol_metatype_to_object_exprs( //dir=expr + unique int id: @protocol_metatype_to_object_expr +); + +regex_literal_exprs( //dir=expr + unique int id: @regex_literal_expr +); + +@self_apply_expr = + @constructor_ref_call_expr +| @dot_syntax_call_expr +; + +#keyset[id] +self_apply_exprs( //dir=expr + int id: @self_apply_expr ref, + int base: @expr_or_none ref +); + +string_to_pointer_exprs( //dir=expr + unique int id: @string_to_pointer_expr +); + +subscript_exprs( //dir=expr + unique int id: @subscript_expr +); + +#keyset[id, index] +subscript_expr_arguments( //dir=expr + int id: @subscript_expr ref, + int index: int ref, + int argument: @argument_or_none ref +); + +#keyset[id] +subscript_expr_has_direct_to_storage_semantics( //dir=expr + int id: @subscript_expr ref +); + +#keyset[id] +subscript_expr_has_direct_to_implementation_semantics( //dir=expr + int id: @subscript_expr ref +); + +#keyset[id] +subscript_expr_has_ordinary_semantics( //dir=expr + int id: @subscript_expr ref +); + +try_exprs( //dir=expr + unique int id: @try_expr +); + +underlying_to_opaque_exprs( //dir=expr + unique int id: @underlying_to_opaque_expr +); + +unevaluated_instance_exprs( //dir=expr + unique int id: @unevaluated_instance_expr +); + +unresolved_member_chain_result_exprs( //dir=expr + unique int id: @unresolved_member_chain_result_expr +); + +unresolved_type_conversion_exprs( //dir=expr + unique int id: @unresolved_type_conversion_expr +); + +boolean_literal_exprs( //dir=expr + unique int id: @boolean_literal_expr, + boolean value: boolean ref +); + +conditional_checked_cast_exprs( //dir=expr + unique int id: @conditional_checked_cast_expr +); + +constructor_ref_call_exprs( //dir=expr + unique int id: @constructor_ref_call_expr +); + +dot_syntax_call_exprs( //dir=expr + unique int id: @dot_syntax_call_expr +); + +dynamic_member_ref_exprs( //dir=expr + unique int id: @dynamic_member_ref_expr +); + +dynamic_subscript_exprs( //dir=expr + unique int id: @dynamic_subscript_expr +); + +forced_checked_cast_exprs( //dir=expr + unique int id: @forced_checked_cast_expr +); + +is_exprs( //dir=expr + unique int id: @is_expr +); + +magic_identifier_literal_exprs( //dir=expr + unique int id: @magic_identifier_literal_expr, + string kind: string ref +); + +@number_literal_expr = + @float_literal_expr +| @integer_literal_expr +; + +string_literal_exprs( //dir=expr + unique int id: @string_literal_expr, + string value: string ref +); + +float_literal_exprs( //dir=expr + unique int id: @float_literal_expr, + string string_value: string ref +); + +integer_literal_exprs( //dir=expr + unique int id: @integer_literal_expr, + string string_value: string ref +); + +@pattern = + @any_pattern +| @binding_pattern +| @bool_pattern +| @enum_element_pattern +| @expr_pattern +| @is_pattern +| @named_pattern +| @optional_some_pattern +| @paren_pattern +| @tuple_pattern +| @typed_pattern +; + +any_patterns( //dir=pattern + unique int id: @any_pattern +); + +binding_patterns( //dir=pattern + unique int id: @binding_pattern, + int sub_pattern: @pattern_or_none ref +); + +bool_patterns( //dir=pattern + unique int id: @bool_pattern, + boolean value: boolean ref +); + +enum_element_patterns( //dir=pattern + unique int id: @enum_element_pattern, + int element: @enum_element_decl_or_none ref +); + +#keyset[id] +enum_element_pattern_sub_patterns( //dir=pattern + int id: @enum_element_pattern ref, + int sub_pattern: @pattern_or_none ref +); + +expr_patterns( //dir=pattern + unique int id: @expr_pattern, + int sub_expr: @expr_or_none ref +); + +is_patterns( //dir=pattern + unique int id: @is_pattern +); + +#keyset[id] +is_pattern_cast_type_reprs( //dir=pattern + int id: @is_pattern ref, + int cast_type_repr: @type_repr_or_none ref +); + +#keyset[id] +is_pattern_sub_patterns( //dir=pattern + int id: @is_pattern ref, + int sub_pattern: @pattern_or_none ref +); + +named_patterns( //dir=pattern + unique int id: @named_pattern, + string name: string ref +); + +optional_some_patterns( //dir=pattern + unique int id: @optional_some_pattern, + int sub_pattern: @pattern_or_none ref +); + +paren_patterns( //dir=pattern + unique int id: @paren_pattern, + int sub_pattern: @pattern_or_none ref +); + +tuple_patterns( //dir=pattern + unique int id: @tuple_pattern +); + +#keyset[id, index] +tuple_pattern_elements( //dir=pattern + int id: @tuple_pattern ref, + int index: int ref, + int element: @pattern_or_none ref +); + +typed_patterns( //dir=pattern + unique int id: @typed_pattern, + int sub_pattern: @pattern_or_none ref +); + +#keyset[id] +typed_pattern_type_reprs( //dir=pattern + int id: @typed_pattern ref, + int type_repr: @type_repr_or_none ref +); + +case_label_items( //dir=stmt + unique int id: @case_label_item, + int pattern: @pattern_or_none ref +); + +#keyset[id] +case_label_item_guards( //dir=stmt + int id: @case_label_item ref, + int guard: @expr_or_none ref +); + +condition_elements( //dir=stmt + unique int id: @condition_element +); + +#keyset[id] +condition_element_booleans( //dir=stmt + int id: @condition_element ref, + int boolean_: @expr_or_none ref +); + +#keyset[id] +condition_element_patterns( //dir=stmt + int id: @condition_element ref, + int pattern: @pattern_or_none ref +); + +#keyset[id] +condition_element_initializers( //dir=stmt + int id: @condition_element ref, + int initializer: @expr_or_none ref +); + +@stmt = + @brace_stmt +| @break_stmt +| @case_stmt +| @continue_stmt +| @defer_stmt +| @fail_stmt +| @fallthrough_stmt +| @labeled_stmt +| @pound_assert_stmt +| @return_stmt +| @throw_stmt +| @yield_stmt +; + +stmt_conditions( //dir=stmt + unique int id: @stmt_condition +); + +#keyset[id, index] +stmt_condition_elements( //dir=stmt + int id: @stmt_condition ref, + int index: int ref, + int element: @condition_element_or_none ref +); + +brace_stmts( //dir=stmt + unique int id: @brace_stmt +); + +#keyset[id, index] +brace_stmt_elements( //dir=stmt + int id: @brace_stmt ref, + int index: int ref, + int element: @ast_node_or_none ref +); + +break_stmts( //dir=stmt + unique int id: @break_stmt +); + +#keyset[id] +break_stmt_target_names( //dir=stmt + int id: @break_stmt ref, + string target_name: string ref +); + +#keyset[id] +break_stmt_targets( //dir=stmt + int id: @break_stmt ref, + int target: @stmt_or_none ref +); + +case_stmts( //dir=stmt + unique int id: @case_stmt, + int body: @stmt_or_none ref +); + +#keyset[id, index] +case_stmt_labels( //dir=stmt + int id: @case_stmt ref, + int index: int ref, + int label: @case_label_item_or_none ref +); + +#keyset[id, index] +case_stmt_variables( //dir=stmt + int id: @case_stmt ref, + int index: int ref, + int variable: @var_decl_or_none ref +); + +continue_stmts( //dir=stmt + unique int id: @continue_stmt +); + +#keyset[id] +continue_stmt_target_names( //dir=stmt + int id: @continue_stmt ref, + string target_name: string ref +); + +#keyset[id] +continue_stmt_targets( //dir=stmt + int id: @continue_stmt ref, + int target: @stmt_or_none ref +); + +defer_stmts( //dir=stmt + unique int id: @defer_stmt, + int body: @brace_stmt_or_none ref +); + +fail_stmts( //dir=stmt + unique int id: @fail_stmt +); + +fallthrough_stmts( //dir=stmt + unique int id: @fallthrough_stmt, + int fallthrough_source: @case_stmt_or_none ref, + int fallthrough_dest: @case_stmt_or_none ref +); + +@labeled_stmt = + @do_catch_stmt +| @do_stmt +| @for_each_stmt +| @labeled_conditional_stmt +| @repeat_while_stmt +| @switch_stmt +; + +#keyset[id] +labeled_stmt_labels( //dir=stmt + int id: @labeled_stmt ref, + string label: string ref +); + +pound_assert_stmts( //dir=stmt + unique int id: @pound_assert_stmt, + int condition: @expr_or_none ref, + string message: string ref +); + +return_stmts( //dir=stmt + unique int id: @return_stmt +); + +#keyset[id] +return_stmt_results( //dir=stmt + int id: @return_stmt ref, + int result: @expr_or_none ref +); + +throw_stmts( //dir=stmt + unique int id: @throw_stmt, + int sub_expr: @expr_or_none ref +); + +yield_stmts( //dir=stmt + unique int id: @yield_stmt +); + +#keyset[id, index] +yield_stmt_results( //dir=stmt + int id: @yield_stmt ref, + int index: int ref, + int result: @expr_or_none ref +); + +do_catch_stmts( //dir=stmt + unique int id: @do_catch_stmt, + int body: @stmt_or_none ref +); + +#keyset[id, index] +do_catch_stmt_catches( //dir=stmt + int id: @do_catch_stmt ref, + int index: int ref, + int catch: @case_stmt_or_none ref +); + +do_stmts( //dir=stmt + unique int id: @do_stmt, + int body: @brace_stmt_or_none ref +); + +for_each_stmts( //dir=stmt + unique int id: @for_each_stmt, + int pattern: @pattern_or_none ref, + int sequence: @expr_or_none ref, + int body: @brace_stmt_or_none ref +); + +#keyset[id] +for_each_stmt_wheres( //dir=stmt + int id: @for_each_stmt ref, + int where: @expr_or_none ref +); + +@labeled_conditional_stmt = + @guard_stmt +| @if_stmt +| @while_stmt +; + +#keyset[id] +labeled_conditional_stmts( //dir=stmt + int id: @labeled_conditional_stmt ref, + int condition: @stmt_condition_or_none ref +); + +repeat_while_stmts( //dir=stmt + unique int id: @repeat_while_stmt, + int condition: @expr_or_none ref, + int body: @stmt_or_none ref +); + +switch_stmts( //dir=stmt + unique int id: @switch_stmt, + int expr: @expr_or_none ref +); + +#keyset[id, index] +switch_stmt_cases( //dir=stmt + int id: @switch_stmt ref, + int index: int ref, + int case_: @case_stmt_or_none ref +); + +guard_stmts( //dir=stmt + unique int id: @guard_stmt, + int body: @brace_stmt_or_none ref +); + +if_stmts( //dir=stmt + unique int id: @if_stmt, + int then: @stmt_or_none ref +); + +#keyset[id] +if_stmt_elses( //dir=stmt + int id: @if_stmt ref, + int else: @stmt_or_none ref +); + +while_stmts( //dir=stmt + unique int id: @while_stmt, + int body: @stmt_or_none ref +); + +@type = + @any_function_type +| @any_generic_type +| @any_metatype_type +| @builtin_type +| @dependent_member_type +| @dynamic_self_type +| @error_type +| @existential_type +| @in_out_type +| @l_value_type +| @module_type +| @parameterized_protocol_type +| @protocol_composition_type +| @reference_storage_type +| @substitutable_type +| @sugar_type +| @tuple_type +| @unresolved_type +; + +#keyset[id] +types( //dir=type + int id: @type ref, + string name: string ref, + int canonical_type: @type_or_none ref +); + +type_reprs( //dir=type + unique int id: @type_repr, + int type_: @type_or_none ref +); + +@any_function_type = + @function_type +| @generic_function_type +; + +#keyset[id] +any_function_types( //dir=type + int id: @any_function_type ref, + int result: @type_or_none ref +); + +#keyset[id, index] +any_function_type_param_types( //dir=type + int id: @any_function_type ref, + int index: int ref, + int param_type: @type_or_none ref +); + +#keyset[id, index] +any_function_type_param_labels( //dir=type + int id: @any_function_type ref, + int index: int ref, + string param_label: string ref +); + +#keyset[id] +any_function_type_is_throwing( //dir=type + int id: @any_function_type ref +); + +#keyset[id] +any_function_type_is_async( //dir=type + int id: @any_function_type ref +); + +@any_generic_type = + @nominal_or_bound_generic_nominal_type +| @unbound_generic_type +; + +#keyset[id] +any_generic_types( //dir=type + int id: @any_generic_type ref, + int declaration: @decl_or_none ref +); + +#keyset[id] +any_generic_type_parents( //dir=type + int id: @any_generic_type ref, + int parent: @type_or_none ref +); + +@any_metatype_type = + @existential_metatype_type +| @metatype_type +; + +@builtin_type = + @any_builtin_integer_type +| @builtin_bridge_object_type +| @builtin_default_actor_storage_type +| @builtin_executor_type +| @builtin_float_type +| @builtin_job_type +| @builtin_native_object_type +| @builtin_raw_pointer_type +| @builtin_raw_unsafe_continuation_type +| @builtin_unsafe_value_buffer_type +| @builtin_vector_type +; + +dependent_member_types( //dir=type + unique int id: @dependent_member_type, + int base_type: @type_or_none ref, + int associated_type_decl: @associated_type_decl_or_none ref +); + +dynamic_self_types( //dir=type + unique int id: @dynamic_self_type, + int static_self_type: @type_or_none ref +); + +error_types( //dir=type + unique int id: @error_type +); + +existential_types( //dir=type + unique int id: @existential_type, + int constraint: @type_or_none ref +); + +in_out_types( //dir=type + unique int id: @in_out_type, + int object_type: @type_or_none ref +); + +l_value_types( //dir=type + unique int id: @l_value_type, + int object_type: @type_or_none ref +); + +module_types( //dir=type + unique int id: @module_type, + int module: @module_decl_or_none ref +); + +parameterized_protocol_types( //dir=type + unique int id: @parameterized_protocol_type, + int base: @protocol_type_or_none ref +); + +#keyset[id, index] +parameterized_protocol_type_args( //dir=type + int id: @parameterized_protocol_type ref, + int index: int ref, + int arg: @type_or_none ref +); + +protocol_composition_types( //dir=type + unique int id: @protocol_composition_type +); + +#keyset[id, index] +protocol_composition_type_members( //dir=type + int id: @protocol_composition_type ref, + int index: int ref, + int member: @type_or_none ref +); + +@reference_storage_type = + @unmanaged_storage_type +| @unowned_storage_type +| @weak_storage_type +; + +#keyset[id] +reference_storage_types( //dir=type + int id: @reference_storage_type ref, + int referent_type: @type_or_none ref +); + +@substitutable_type = + @archetype_type +| @generic_type_param_type +; + +@sugar_type = + @paren_type +| @syntax_sugar_type +| @type_alias_type +; + +tuple_types( //dir=type + unique int id: @tuple_type +); + +#keyset[id, index] +tuple_type_types( //dir=type + int id: @tuple_type ref, + int index: int ref, + int type_: @type_or_none ref +); + +#keyset[id, index] +tuple_type_names( //dir=type + int id: @tuple_type ref, + int index: int ref, + string name: string ref +); + +unresolved_types( //dir=type + unique int id: @unresolved_type +); + +@any_builtin_integer_type = + @builtin_integer_literal_type +| @builtin_integer_type +; + +@archetype_type = + @opaque_type_archetype_type +| @opened_archetype_type +| @primary_archetype_type +; + +#keyset[id] +archetype_types( //dir=type + int id: @archetype_type ref, + int interface_type: @type_or_none ref +); + +#keyset[id] +archetype_type_superclasses( //dir=type + int id: @archetype_type ref, + int superclass: @type_or_none ref +); + +#keyset[id, index] +archetype_type_protocols( //dir=type + int id: @archetype_type ref, + int index: int ref, + int protocol: @protocol_decl_or_none ref +); + +builtin_bridge_object_types( //dir=type + unique int id: @builtin_bridge_object_type +); + +builtin_default_actor_storage_types( //dir=type + unique int id: @builtin_default_actor_storage_type +); + +builtin_executor_types( //dir=type + unique int id: @builtin_executor_type +); + +builtin_float_types( //dir=type + unique int id: @builtin_float_type +); + +builtin_job_types( //dir=type + unique int id: @builtin_job_type +); + +builtin_native_object_types( //dir=type + unique int id: @builtin_native_object_type +); + +builtin_raw_pointer_types( //dir=type + unique int id: @builtin_raw_pointer_type +); + +builtin_raw_unsafe_continuation_types( //dir=type + unique int id: @builtin_raw_unsafe_continuation_type +); + +builtin_unsafe_value_buffer_types( //dir=type + unique int id: @builtin_unsafe_value_buffer_type +); + +builtin_vector_types( //dir=type + unique int id: @builtin_vector_type +); + +existential_metatype_types( //dir=type + unique int id: @existential_metatype_type +); + +function_types( //dir=type + unique int id: @function_type +); + +generic_function_types( //dir=type + unique int id: @generic_function_type +); + +#keyset[id, index] +generic_function_type_generic_params( //dir=type + int id: @generic_function_type ref, + int index: int ref, + int generic_param: @generic_type_param_type_or_none ref +); + +generic_type_param_types( //dir=type + unique int id: @generic_type_param_type +); + +metatype_types( //dir=type + unique int id: @metatype_type +); + +@nominal_or_bound_generic_nominal_type = + @bound_generic_type +| @nominal_type +; + +paren_types( //dir=type + unique int id: @paren_type, + int type_: @type_or_none ref +); + +@syntax_sugar_type = + @dictionary_type +| @unary_syntax_sugar_type +; + +type_alias_types( //dir=type + unique int id: @type_alias_type, + int decl: @type_alias_decl_or_none ref +); + +unbound_generic_types( //dir=type + unique int id: @unbound_generic_type +); + +unmanaged_storage_types( //dir=type + unique int id: @unmanaged_storage_type +); + +unowned_storage_types( //dir=type + unique int id: @unowned_storage_type +); + +weak_storage_types( //dir=type + unique int id: @weak_storage_type +); + +@bound_generic_type = + @bound_generic_class_type +| @bound_generic_enum_type +| @bound_generic_struct_type +; + +#keyset[id, index] +bound_generic_type_arg_types( //dir=type + int id: @bound_generic_type ref, + int index: int ref, + int arg_type: @type_or_none ref +); + +builtin_integer_literal_types( //dir=type + unique int id: @builtin_integer_literal_type +); + +builtin_integer_types( //dir=type + unique int id: @builtin_integer_type +); + +#keyset[id] +builtin_integer_type_widths( //dir=type + int id: @builtin_integer_type ref, + int width: int ref +); + +dictionary_types( //dir=type + unique int id: @dictionary_type, + int key_type: @type_or_none ref, + int value_type: @type_or_none ref +); + +@nominal_type = + @class_type +| @enum_type +| @protocol_type +| @struct_type +; + +opaque_type_archetype_types( //dir=type + unique int id: @opaque_type_archetype_type, + int declaration: @opaque_type_decl_or_none ref +); + +opened_archetype_types( //dir=type + unique int id: @opened_archetype_type +); + +primary_archetype_types( //dir=type + unique int id: @primary_archetype_type +); + +@unary_syntax_sugar_type = + @array_slice_type +| @optional_type +| @variadic_sequence_type +; + +#keyset[id] +unary_syntax_sugar_types( //dir=type + int id: @unary_syntax_sugar_type ref, + int base_type: @type_or_none ref +); + +array_slice_types( //dir=type + unique int id: @array_slice_type +); + +bound_generic_class_types( //dir=type + unique int id: @bound_generic_class_type +); + +bound_generic_enum_types( //dir=type + unique int id: @bound_generic_enum_type +); + +bound_generic_struct_types( //dir=type + unique int id: @bound_generic_struct_type +); + +class_types( //dir=type + unique int id: @class_type +); + +enum_types( //dir=type + unique int id: @enum_type +); + +optional_types( //dir=type + unique int id: @optional_type +); + +protocol_types( //dir=type + unique int id: @protocol_type +); + +struct_types( //dir=type + unique int id: @struct_type +); + +variadic_sequence_types( //dir=type + unique int id: @variadic_sequence_type +); + +@abstract_function_decl_or_none = + @abstract_function_decl +| @unspecified_element +; + +@accessor_decl_or_none = + @accessor_decl +| @unspecified_element +; + +@argument_or_none = + @argument +| @unspecified_element +; + +@associated_type_decl_or_none = + @associated_type_decl +| @unspecified_element +; + +@ast_node_or_none = + @ast_node +| @unspecified_element +; + +@brace_stmt_or_none = + @brace_stmt +| @unspecified_element +; + +@case_label_item_or_none = + @case_label_item +| @unspecified_element +; + +@case_stmt_or_none = + @case_stmt +| @unspecified_element +; + +@closure_expr_or_none = + @closure_expr +| @unspecified_element +; + +@condition_element_or_none = + @condition_element +| @unspecified_element +; + +@constructor_decl_or_none = + @constructor_decl +| @unspecified_element +; + +@decl_or_none = + @decl +| @unspecified_element +; + +@enum_element_decl_or_none = + @enum_element_decl +| @unspecified_element +; + +@expr_or_none = + @expr +| @unspecified_element +; + +@file_or_none = + @file +| @unspecified_element +; + +@generic_type_param_decl_or_none = + @generic_type_param_decl +| @unspecified_element +; + +@generic_type_param_type_or_none = + @generic_type_param_type +| @unspecified_element +; + +@location_or_none = + @location +| @unspecified_element +; + +@module_decl_or_none = + @module_decl +| @unspecified_element +; + +@nominal_type_decl_or_none = + @nominal_type_decl +| @unspecified_element +; + +@opaque_type_decl_or_none = + @opaque_type_decl +| @unspecified_element +; + +@opaque_value_expr_or_none = + @opaque_value_expr +| @unspecified_element +; + +@param_decl_or_none = + @param_decl +| @unspecified_element +; + +@pattern_or_none = + @pattern +| @unspecified_element +; + +@pattern_binding_decl_or_none = + @pattern_binding_decl +| @unspecified_element +; + +@precedence_group_decl_or_none = + @precedence_group_decl +| @unspecified_element +; + +@protocol_decl_or_none = + @protocol_decl +| @unspecified_element +; + +@protocol_type_or_none = + @protocol_type +| @unspecified_element +; + +@stmt_or_none = + @stmt +| @unspecified_element +; + +@stmt_condition_or_none = + @stmt_condition +| @unspecified_element +; + +@string_literal_expr_or_none = + @string_literal_expr +| @unspecified_element +; + +@tap_expr_or_none = + @tap_expr +| @unspecified_element +; + +@type_or_none = + @type +| @unspecified_element +; + +@type_alias_decl_or_none = + @type_alias_decl +| @unspecified_element +; + +@type_repr_or_none = + @type_repr +| @unspecified_element +; + +@value_decl_or_none = + @unspecified_element +| @value_decl +; + +@var_decl_or_none = + @unspecified_element +| @var_decl +; From b5c7d6bfcd5c2af418b8ec353fdd5685ad354ced Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Fri, 11 Nov 2022 14:57:36 +0000 Subject: [PATCH 0204/1420] Kotlin: Fix build on OS X --- java/kotlin-extractor/build.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/kotlin-extractor/build.py b/java/kotlin-extractor/build.py index ebca028be63..05cb1204d86 100755 --- a/java/kotlin-extractor/build.py +++ b/java/kotlin-extractor/build.py @@ -114,7 +114,8 @@ def compile_to_jar(build_dir, tmp_src_dir, srcs, classpath, java_classpath, outp run_process(['jar', 'cf', output, '-C', class_dir, '.', - '-C', tmp_src_dir + '/main/resources', '.']) + '-C', tmp_src_dir + '/main/resources', 'META-INF', + '-C', tmp_src_dir + '/main/resources', 'com/github/codeql/extractor.name']) shutil.rmtree(class_dir) From f92d836607a48948054e5ddd205b93b82ddd1bb8 Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 11 Nov 2022 16:03:14 +0000 Subject: [PATCH 0205/1420] Python: Fix test failure Casting to `ImportExpr` caused the `typetracking_imports` test to fail. --- .../semmle/python/dataflow/new/internal/ImportResolution.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll index dd92cbb7a45..9e04d3f9681 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll @@ -80,7 +80,7 @@ module ImportResolution { ) or exists(Alias a | - defn.asExpr() = [a.getValue().(ImportExpr), a.getValue().(ImportMember).getModule()] and + defn.asExpr() = [a.getValue(), a.getValue().(ImportMember).getModule()] and a.getAsname().(Name).getId() = name and defn.getScope() = m ) From fea4b816af4646904e4c4fe8e8bc98e9d52af32e Mon Sep 17 00:00:00 2001 From: Gustav Date: Fri, 11 Nov 2022 17:12:13 +0100 Subject: [PATCH 0206/1420] Fix double close Co-authored-by: Chris Smowton --- go/extractor/trap/trapwriter.go | 1 + 1 file changed, 1 insertion(+) diff --git a/go/extractor/trap/trapwriter.go b/go/extractor/trap/trapwriter.go index 2a3944705bf..0833d845830 100644 --- a/go/extractor/trap/trapwriter.go +++ b/go/extractor/trap/trapwriter.go @@ -95,6 +95,7 @@ func (tw *Writer) Close() error { if err != nil { // throw away file close error tw.file.Close() + return err } err = tw.zip.Close() if err != nil { From 3514694cdfc5c7ab49e0ecdec63a73244dabd526 Mon Sep 17 00:00:00 2001 From: Gustav Date: Fri, 11 Nov 2022 18:39:25 +0100 Subject: [PATCH 0207/1420] Fix direct access to trap.Writer from trap.Labeler --- go/extractor/trap/labels.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go/extractor/trap/labels.go b/go/extractor/trap/labels.go index 1e9bd298447..f1572fb874e 100644 --- a/go/extractor/trap/labels.go +++ b/go/extractor/trap/labels.go @@ -57,7 +57,7 @@ func (l *Labeler) GlobalID(key string) Label { label, exists := l.keyLabels[key] if !exists { id := l.nextID() - fmt.Fprintf(l.tw.zip, "%s=@\"%s\"\n", id, escapeString(key)) + fmt.Fprintf(l.tw.wzip, "%s=@\"%s\"\n", id, escapeString(key)) label = Label{id} l.keyLabels[key] = label } @@ -90,7 +90,7 @@ func (l *Labeler) LocalID(nd interface{}) Label { // FreshID creates a fresh label and returns it func (l *Labeler) FreshID() Label { id := l.nextID() - fmt.Fprintf(l.tw.zip, "%s=*\n", id) + fmt.Fprintf(l.tw.wzip, "%s=*\n", id) return Label{id} } From 4d3a8373100165b97fe58a909db62d8c5dbdd198 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Tue, 8 Nov 2022 19:20:43 -0500 Subject: [PATCH 0208/1420] Java: query for detecting enabling Javascript in Android WebSettings --- ...roidWebViewSettingsEnabledJavaScript.qhelp | 45 +++++++++++++++++++ ...AndroidWebViewSettingsEnabledJavaScript.ql | 21 +++++++++ .../CWE-079/WebSettingsDisableJavascript.java | 2 + .../CWE-079/WebSettingsEnableJavascript.java | 2 + 4 files changed, 70 insertions(+) create mode 100644 java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.qhelp create mode 100644 java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql create mode 100644 java/ql/src/Security/CWE/CWE-079/WebSettingsDisableJavascript.java create mode 100644 java/ql/src/Security/CWE/CWE-079/WebSettingsEnableJavascript.java diff --git a/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.qhelp b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.qhelp new file mode 100644 index 00000000000..c10856588c6 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.qhelp @@ -0,0 +1,45 @@ + + + +

    + Enabling JavaScript in an Android WebView allows for the running of JavaScript +code in the context of the running application. This opens the possibility for a +man-in-the-middle attack, where the attacker can inject arbitrary JavaScript. +

    + +

    + You can enable or disbale Javascript execution using + the setJavaScriptEnabled method of the settings of a webview. +

    +
    + + +

    If Javascript does not need to be enabled, call setJavaScriptEnabled(false) on the settings of the webview.

    + +

    If JavaScript is necessary, only load content from trusted servers using encrypted channels, such as https with certificate verification.

    +
    + + +

    In the following (bad) example, a webview has JavaScript enabled in its settings.

    + + + +

    In the following (good) example, a webview explicitly disallows JavaScript execution.

    + + + +
    + + +
  • + Oversecured Android Vulnerabilities Guide: Enabled JavaScript +
  • +
  • + Android documentation: setJavaScriptEnabled +
  • + + + + diff --git a/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql new file mode 100644 index 00000000000..4483f80ad38 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql @@ -0,0 +1,21 @@ +/** + * @name Android WebView JavaScript settings + * @kind problem + * @id java/android-websettings-javascript + * @problem.severity warning + * @security-severity 6.1 + * @precision high + * @tags security + * external/cwe/cwe-079 + */ + +import java +import semmle.code.java.frameworks.android.WebView + +from MethodAccess ma +where + ( + ma.getMethod() instanceof AllowJavaScriptMethod and + ma.getArgument(0).(CompileTimeConstantExpr).getBooleanValue() = true + ) +select ma, "JavaScript execution enabled in WebView." diff --git a/java/ql/src/Security/CWE/CWE-079/WebSettingsDisableJavascript.java b/java/ql/src/Security/CWE/CWE-079/WebSettingsDisableJavascript.java new file mode 100644 index 00000000000..43a8cd92c0e --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-079/WebSettingsDisableJavascript.java @@ -0,0 +1,2 @@ +WebSettings settings = webview.getSettings(); +settings.setJavaScriptEnabled(false); diff --git a/java/ql/src/Security/CWE/CWE-079/WebSettingsEnableJavascript.java b/java/ql/src/Security/CWE/CWE-079/WebSettingsEnableJavascript.java new file mode 100644 index 00000000000..adfac156009 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-079/WebSettingsEnableJavascript.java @@ -0,0 +1,2 @@ +WebSettings settings = webview.getSettings(); +settings.setJavaScriptEnabled(true); From b9c2ee75be3df35da13cb10c2ef47ec3fa985e52 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Tue, 8 Nov 2022 21:41:18 -0500 Subject: [PATCH 0209/1420] Java: Query for Android WebView File Access Query for Android WebView file access settings --- .../AndroidWebViewSettingsFileAccess.qhelp | 57 +++++++++++++++++++ .../AndroidWebViewSettingsFileAccess.ql | 18 ++++++ .../CWE/CWE-200/WebViewFileAccessSafe.java | 5 ++ .../CWE/CWE-200/WebViewFileAccessUnsafe.java | 5 ++ 4 files changed, 85 insertions(+) create mode 100644 java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.qhelp create mode 100644 java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql create mode 100644 java/ql/src/Security/CWE/CWE-200/WebViewFileAccessSafe.java create mode 100644 java/ql/src/Security/CWE/CWE-200/WebViewFileAccessUnsafe.java diff --git a/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.qhelp b/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.qhelp new file mode 100644 index 00000000000..cb80e92eab1 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.qhelp @@ -0,0 +1,57 @@ + + + +

    + File access in an Android WebView can expose the device's file system to + the JavaScript running in the WebView. If there are vulnerabilities in the + JavaScript, file access may allow an attacker to access or steal the + user's data. +

    +
    + + +

    When possible, you should disallow file access by setting the following settings to false:

    + +
      +
    • setAllowFileAccess
    • +
    • setAllowFileAccessFromFileURLs
    • +
    • setAllowUniversalAccessFromFileURLs
    • +
    +
    + + +

    In the following (bad) example, the WebView is configured with the settings + which would allow local file access.

    + + + +

    In the following (good) example, the WebView is configured to disallow file access.

    + + + +
    + + +
  • + Android documentation: WebSettings.setAllowFileAccess. +
  • +
  • + Android documentation: WebSettings.setAllowFileAccessFromFileURLs. +
  • +
  • + Android documentation: WebSettings.setAllowUniversalAccessFromFileURLs. +
  • +
  • + File access from URLs is enabled for WebView: File access for URLs is enabled for WebView. +
  • +
  • + File access is enabled for WebView: File access is enabled for WebView. +
  • +
  • + Universal file access from file URLs is enabled for WebView: Universal file access from file URLs is enabled for WebView. +
  • +
    + +
    diff --git a/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql b/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql new file mode 100644 index 00000000000..e34d502f3fa --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql @@ -0,0 +1,18 @@ +/** + * @name Android WebSettings file access + * @kind problem + * @id java/android-websettings-file-access + * @problem.severity warning + * @security-severity 6.5 + * @precision high + * @tags security + * external/cwe/cwe-200 + */ + +import java +import semmle.code.java.frameworks.android.WebView + +from MethodAccess ma +where ma.getMethod() instanceof CrossOriginAccessMethod +select ma, "WebView setting $@ may allow for unauthorized access of sensitive information.", ma, + ma.getMethod().getName() diff --git a/java/ql/src/Security/CWE/CWE-200/WebViewFileAccessSafe.java b/java/ql/src/Security/CWE/CWE-200/WebViewFileAccessSafe.java new file mode 100644 index 00000000000..6002888cba1 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-200/WebViewFileAccessSafe.java @@ -0,0 +1,5 @@ +WebSettings settings = view.getSettings(); + +settings.setAllowFileAccess(false); +settings.setAllowFileAccessFromURLs(false); +settings.setAllowUniversalAccessFromURLs(false); diff --git a/java/ql/src/Security/CWE/CWE-200/WebViewFileAccessUnsafe.java b/java/ql/src/Security/CWE/CWE-200/WebViewFileAccessUnsafe.java new file mode 100644 index 00000000000..6c17d66c3b0 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-200/WebViewFileAccessUnsafe.java @@ -0,0 +1,5 @@ +WebSettings settings = view.getSettings(); + +settings.setAllowFileAccess(true); +settings.setAllowFileAccessFromURLs(true); +settings.setAllowUniversalAccessFromURLs(true); From 7712ec2523c3178720aa648532f032b54ba4cf02 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Sat, 12 Nov 2022 09:54:26 -0500 Subject: [PATCH 0210/1420] Java: setJavascriptEnabled query change notes --- .../CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql | 2 +- .../2022-11-12-websettings-setjavascript-enabled.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 java/ql/src/change-notes/2022-11-12-websettings-setjavascript-enabled.md diff --git a/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql index 4483f80ad38..7b33f9313db 100644 --- a/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql +++ b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql @@ -1,7 +1,7 @@ /** * @name Android WebView JavaScript settings * @kind problem - * @id java/android-websettings-javascript + * @id java/android-websettings-javascript-enabled * @problem.severity warning * @security-severity 6.1 * @precision high diff --git a/java/ql/src/change-notes/2022-11-12-websettings-setjavascript-enabled.md b/java/ql/src/change-notes/2022-11-12-websettings-setjavascript-enabled.md new file mode 100644 index 00000000000..58579f006c4 --- /dev/null +++ b/java/ql/src/change-notes/2022-11-12-websettings-setjavascript-enabled.md @@ -0,0 +1,4 @@ +--- +category: newQuery +--- +* Added a new query, `java/android-websettings-javascript-enabled`, to detect if JavaScript execution is enabled in an Android WebView. From b4cd1ee34dc323aac07fcb7f2740fe72bd7d137f Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Sat, 12 Nov 2022 09:56:45 -0500 Subject: [PATCH 0211/1420] Java: Added description to query --- .../CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql | 1 + 1 file changed, 1 insertion(+) diff --git a/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql index 7b33f9313db..409ab786791 100644 --- a/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql +++ b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql @@ -1,5 +1,6 @@ /** * @name Android WebView JavaScript settings + * @description Enabling JavaScript execution in a WebView can result in man-in-the-middle attacks. * @kind problem * @id java/android-websettings-javascript-enabled * @problem.severity warning From ea358f069817b3c7fd1b0ce37aebd3e191e6dc7f Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Sat, 12 Nov 2022 10:01:30 -0500 Subject: [PATCH 0212/1420] Java: WebView file access query description --- .../src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql | 1 + 1 file changed, 1 insertion(+) diff --git a/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql b/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql index e34d502f3fa..c705ac52425 100644 --- a/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql +++ b/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql @@ -1,6 +1,7 @@ /** * @name Android WebSettings file access * @kind problem + * @description Enabling access to the file system in a WebView can enable access to sensitive information. * @id java/android-websettings-file-access * @problem.severity warning * @security-severity 6.5 From 631a08496e46f5bf19859ffb4b781d2b1e32eb58 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Sat, 12 Nov 2022 10:09:35 -0500 Subject: [PATCH 0213/1420] Java: WebView file access query change note --- .../change-notes/2022-11-12-android-webview-file-access.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 java/ql/src/change-notes/2022-11-12-android-webview-file-access.md diff --git a/java/ql/src/change-notes/2022-11-12-android-webview-file-access.md b/java/ql/src/change-notes/2022-11-12-android-webview-file-access.md new file mode 100644 index 00000000000..3e8777e60cd --- /dev/null +++ b/java/ql/src/change-notes/2022-11-12-android-webview-file-access.md @@ -0,0 +1,5 @@ +--- +category: newQuery +--- +* Added a new query `java/android-websettings-file-access` to detect configurations that enable file system access in Android WebViews. + From a8e67bdfc5782ff8ac38f3bd1fe23edbedb10e01 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Sat, 12 Nov 2022 10:15:15 -0500 Subject: [PATCH 0214/1420] Java: Removed typo in Android WebView file access documentation --- .../CWE/CWE-200/AndroidWebViewSettingsFileAccess.qhelp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.qhelp b/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.qhelp index cb80e92eab1..eba8aea88b2 100644 --- a/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.qhelp +++ b/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.qhelp @@ -35,13 +35,13 @@
  • - Android documentation: WebSettings.setAllowFileAccess. + Android documentation: WebSettings.setAllowFileAccess.
  • - Android documentation: WebSettings.setAllowFileAccessFromFileURLs. + Android documentation: WebSettings.setAllowFileAccessFromFileURLs.
  • - Android documentation: WebSettings.setAllowUniversalAccessFromFileURLs. + Android documentation: WebSettings.setAllowUniversalAccessFromFileURLs.
  • File access from URLs is enabled for WebView: File access for URLs is enabled for WebView. From b5400f6dc9ff5e1ef95db1df7ce6f405e4c5353a Mon Sep 17 00:00:00 2001 From: Alex Denisov Date: Mon, 14 Nov 2022 08:55:44 +0100 Subject: [PATCH 0215/1420] Swift: remove rebase artifact --- .github/workflows/swift.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 330df323de9..09e1c8a92f8 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -112,15 +112,6 @@ jobs: with: name: swift-generated-cpp-files path: swift/generated-cpp-files/** - qlformat: - runs-on: ubuntu-latest - needs: changes - if: ${{ needs.changes.outputs.ql == 'true' }} - steps: - - uses: actions/checkout@v3 - - uses: ./.github/actions/fetch-codeql - - name: Check QL formatting - run: find swift/ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only database-upgrade-scripts: runs-on: ubuntu-latest steps: From dd519cc9bf5254fff8a6de7b7c0d59262155aa34 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Mon, 10 Oct 2022 09:38:07 +0200 Subject: [PATCH 0216/1420] Ruby: also treat included/prepended modules as subclasses --- .../lib/codeql/ruby/ast/internal/Module.qll | 2 +- .../dataflow/internal/DataFlowDispatch.qll | 15 +- .../library-tests/modules/ancestors.expected | 6 + .../library-tests/modules/callgraph.expected | 7 + ruby/ql/test/library-tests/modules/calls.rb | 16 + .../library-tests/modules/methods.expected | 22 + .../library-tests/modules/modules.expected | 1216 +++++++++-------- .../modules/superclasses.expected | 5 + 8 files changed, 681 insertions(+), 608 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Module.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Module.qll index d0511247251..7b8ab7acba3 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/Module.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Module.qll @@ -165,7 +165,7 @@ private module Cached { */ cached Method lookupMethodInSubClasses(Module m, string name) { - exists(Module sub | sub.getSuperClass() = m | + exists(Module sub | sub.getAnImmediateAncestor() = m | TMethod(result) = lookupMethodOrConst0(sub, name) or result = lookupMethodInSubClasses(sub, name) ) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll index 4b97772d2c9..c5e2cce3988 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll @@ -190,13 +190,10 @@ private Block yieldCall(RelevantCall call) { } pragma[nomagic] -private predicate superCall(RelevantCall call, Module superClass, string method) { +private predicate superCall(RelevantCall call, Module cls, string method) { call.getExpr() instanceof SuperCall and - exists(Module tp | - tp = call.getExpr().getEnclosingModule().getModule() and - superClass = tp.getSuperClass() and - method = call.getExpr().getEnclosingMethod().getName() - ) + cls = call.getExpr().getEnclosingModule().getModule() and + method = call.getExpr().getEnclosingMethod().getName() } /** Holds if `self` belongs to module `m`. */ @@ -464,9 +461,9 @@ private module Cached { ) ) or - exists(Module superClass, string method | - superCall(call, superClass, method) and - result = lookupMethod(superClass, method) + exists(Module cls, string method | + superCall(call, cls, method) and + result = lookupMethod(cls.getAnImmediateAncestor(), method) ) or result = yieldCall(call) diff --git a/ruby/ql/test/library-tests/modules/ancestors.expected b/ruby/ql/test/library-tests/modules/ancestors.expected index 11abca965b6..50a5f7dc72e 100644 --- a/ruby/ql/test/library-tests/modules/ancestors.expected +++ b/ruby/ql/test/library-tests/modules/ancestors.expected @@ -136,6 +136,12 @@ calls.rb: # 605| SingletonC #-----| super -> SingletonA +# 618| Included + +# 626| IncludesIncluded +#-----| super -> Object +#-----| include -> Included + hello.rb: # 1| EnglishWords diff --git a/ruby/ql/test/library-tests/modules/callgraph.expected b/ruby/ql/test/library-tests/modules/callgraph.expected index afb8b6da172..93f262b3105 100644 --- a/ruby/ql/test/library-tests/modules/callgraph.expected +++ b/ruby/ql/test/library-tests/modules/callgraph.expected @@ -233,6 +233,10 @@ getTarget | calls.rb:614:1:614:31 | call to call_call_singleton1 | calls.rb:591:5:593:7 | call_call_singleton1 | | calls.rb:615:1:615:31 | call to call_call_singleton1 | calls.rb:591:5:593:7 | call_call_singleton1 | | calls.rb:616:1:616:31 | call to call_call_singleton1 | calls.rb:591:5:593:7 | call_call_singleton1 | +| calls.rb:620:9:620:16 | call to bar | calls.rb:622:5:623:7 | bar | +| calls.rb:620:9:620:16 | call to bar | calls.rb:628:5:630:7 | bar | +| calls.rb:627:5:627:20 | call to include | calls.rb:108:5:110:7 | include | +| calls.rb:629:9:629:13 | call to super | calls.rb:622:5:623:7 | bar | | hello.rb:12:5:12:24 | call to include | calls.rb:108:5:110:7 | include | | hello.rb:14:16:14:20 | call to hello | hello.rb:2:5:4:7 | hello | | hello.rb:20:16:20:20 | call to super | hello.rb:13:5:15:7 | message | @@ -476,6 +480,9 @@ publicMethod | calls.rb:600:5:602:7 | call_singleton1 | | calls.rb:606:5:607:7 | singleton1 | | calls.rb:609:5:611:7 | call_singleton1 | +| calls.rb:619:5:621:7 | foo | +| calls.rb:622:5:623:7 | bar | +| calls.rb:628:5:630:7 | bar | | hello.rb:2:5:4:7 | hello | | hello.rb:5:5:7:7 | world | | hello.rb:13:5:15:7 | message | diff --git a/ruby/ql/test/library-tests/modules/calls.rb b/ruby/ql/test/library-tests/modules/calls.rb index 031c85d67d8..c6cd849571c 100644 --- a/ruby/ql/test/library-tests/modules/calls.rb +++ b/ruby/ql/test/library-tests/modules/calls.rb @@ -614,3 +614,19 @@ end SingletonA.call_call_singleton1 SingletonB.call_call_singleton1 SingletonC.call_call_singleton1 + +module Included + def foo + self.bar + end + def bar + end +end + +class IncludesIncluded + include Included + def bar + super + end +end + diff --git a/ruby/ql/test/library-tests/modules/methods.expected b/ruby/ql/test/library-tests/modules/methods.expected index 5319fc55377..8bd0d32fde6 100644 --- a/ruby/ql/test/library-tests/modules/methods.expected +++ b/ruby/ql/test/library-tests/modules/methods.expected @@ -45,6 +45,9 @@ getMethod | calls.rb:531:1:544:3 | ProtectedMethods | bar | calls.rb:534:15:536:7 | bar | | calls.rb:531:1:544:3 | ProtectedMethods | baz | calls.rb:538:5:543:7 | baz | | calls.rb:550:1:555:3 | ProtectedMethodsSub | baz | calls.rb:551:5:554:7 | baz | +| calls.rb:618:1:624:3 | Included | bar | calls.rb:622:5:623:7 | bar | +| calls.rb:618:1:624:3 | Included | foo | calls.rb:619:5:621:7 | foo | +| calls.rb:626:1:631:3 | IncludesIncluded | bar | calls.rb:628:5:630:7 | bar | | hello.rb:1:1:8:3 | EnglishWords | hello | hello.rb:2:5:4:7 | hello | | hello.rb:1:1:8:3 | EnglishWords | world | hello.rb:5:5:7:7 | world | | hello.rb:11:1:16:3 | Greeting | message | hello.rb:13:5:15:7 | message | @@ -478,6 +481,22 @@ lookupMethod | calls.rb:605:1:612:3 | SingletonC | private_on_main | calls.rb:185:1:186:3 | private_on_main | | calls.rb:605:1:612:3 | SingletonC | puts | calls.rb:102:5:102:30 | puts | | calls.rb:605:1:612:3 | SingletonC | to_s | calls.rb:172:5:173:7 | to_s | +| calls.rb:618:1:624:3 | Included | bar | calls.rb:622:5:623:7 | bar | +| calls.rb:618:1:624:3 | Included | foo | calls.rb:619:5:621:7 | foo | +| calls.rb:626:1:631:3 | IncludesIncluded | add_singleton | calls.rb:367:1:371:3 | add_singleton | +| calls.rb:626:1:631:3 | IncludesIncluded | bar | calls.rb:628:5:630:7 | bar | +| calls.rb:626:1:631:3 | IncludesIncluded | call_block | calls.rb:81:1:83:3 | call_block | +| calls.rb:626:1:631:3 | IncludesIncluded | call_instance_m | calls.rb:39:1:41:3 | call_instance_m | +| calls.rb:626:1:631:3 | IncludesIncluded | create | calls.rb:278:1:286:3 | create | +| calls.rb:626:1:631:3 | IncludesIncluded | foo | calls.rb:619:5:621:7 | foo | +| calls.rb:626:1:631:3 | IncludesIncluded | funny | calls.rb:140:1:142:3 | funny | +| calls.rb:626:1:631:3 | IncludesIncluded | indirect | calls.rb:158:1:160:3 | indirect | +| calls.rb:626:1:631:3 | IncludesIncluded | new | calls.rb:117:5:117:16 | new | +| calls.rb:626:1:631:3 | IncludesIncluded | optional_arg | calls.rb:76:1:79:3 | optional_arg | +| calls.rb:626:1:631:3 | IncludesIncluded | pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch | +| calls.rb:626:1:631:3 | IncludesIncluded | private_on_main | calls.rb:185:1:186:3 | private_on_main | +| calls.rb:626:1:631:3 | IncludesIncluded | puts | calls.rb:102:5:102:30 | puts | +| calls.rb:626:1:631:3 | IncludesIncluded | to_s | calls.rb:172:5:173:7 | to_s | | file://:0:0:0:0 | Class | include | calls.rb:108:5:110:7 | include | | file://:0:0:0:0 | Class | module_eval | calls.rb:107:5:107:24 | module_eval | | file://:0:0:0:0 | Class | new | calls.rb:117:5:117:16 | new | @@ -965,6 +984,9 @@ enclosingMethod | calls.rb:601:9:601:18 | self | calls.rb:600:5:602:7 | call_singleton1 | | calls.rb:610:9:610:18 | call to singleton1 | calls.rb:609:5:611:7 | call_singleton1 | | calls.rb:610:9:610:18 | self | calls.rb:609:5:611:7 | call_singleton1 | +| calls.rb:620:9:620:12 | self | calls.rb:619:5:621:7 | foo | +| calls.rb:620:9:620:16 | call to bar | calls.rb:619:5:621:7 | foo | +| calls.rb:629:9:629:13 | call to super | calls.rb:628:5:630:7 | bar | | hello.rb:3:9:3:22 | return | hello.rb:2:5:4:7 | hello | | hello.rb:3:16:3:22 | "hello" | hello.rb:2:5:4:7 | hello | | hello.rb:3:17:3:21 | hello | hello.rb:2:5:4:7 | hello | diff --git a/ruby/ql/test/library-tests/modules/modules.expected b/ruby/ql/test/library-tests/modules/modules.expected index 01b81e01efe..57cebf1907c 100644 --- a/ruby/ql/test/library-tests/modules/modules.expected +++ b/ruby/ql/test/library-tests/modules/modules.expected @@ -32,6 +32,8 @@ getModule | calls.rb:583:1:594:3 | SingletonA | | calls.rb:596:1:603:3 | SingletonB | | calls.rb:605:1:612:3 | SingletonC | +| calls.rb:618:1:624:3 | Included | +| calls.rb:626:1:631:3 | IncludesIncluded | | file://:0:0:0:0 | BasicObject | | file://:0:0:0:0 | Class | | file://:0:0:0:0 | Complex | @@ -101,7 +103,7 @@ getADeclaration | calls.rb:96:1:98:3 | String | calls.rb:96:1:98:3 | String | | calls.rb:100:1:103:3 | Kernel | calls.rb:100:1:103:3 | Kernel | | calls.rb:105:1:113:3 | Module | calls.rb:105:1:113:3 | Module | -| calls.rb:115:1:118:3 | Object | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:115:1:118:3 | Object | calls.rb:1:1:632:1 | calls.rb | | calls.rb:115:1:118:3 | Object | calls.rb:115:1:118:3 | Object | | calls.rb:115:1:118:3 | Object | hello.rb:1:1:22:3 | hello.rb | | calls.rb:115:1:118:3 | Object | instance_fields.rb:1:1:29:4 | instance_fields.rb | @@ -139,6 +141,8 @@ getADeclaration | calls.rb:583:1:594:3 | SingletonA | calls.rb:583:1:594:3 | SingletonA | | calls.rb:596:1:603:3 | SingletonB | calls.rb:596:1:603:3 | SingletonB | | calls.rb:605:1:612:3 | SingletonC | calls.rb:605:1:612:3 | SingletonC | +| calls.rb:618:1:624:3 | Included | calls.rb:618:1:624:3 | Included | +| calls.rb:626:1:631:3 | IncludesIncluded | calls.rb:626:1:631:3 | IncludesIncluded | | hello.rb:1:1:8:3 | EnglishWords | hello.rb:1:1:8:3 | EnglishWords | | hello.rb:11:1:16:3 | Greeting | hello.rb:11:1:16:3 | Greeting | | hello.rb:18:1:22:3 | HelloWorld | hello.rb:18:1:22:3 | HelloWorld | @@ -218,6 +222,7 @@ getSuperClass | calls.rb:583:1:594:3 | SingletonA | calls.rb:115:1:118:3 | Object | | calls.rb:596:1:603:3 | SingletonB | calls.rb:583:1:594:3 | SingletonA | | calls.rb:605:1:612:3 | SingletonC | calls.rb:583:1:594:3 | SingletonA | +| calls.rb:626:1:631:3 | IncludesIncluded | calls.rb:115:1:118:3 | Object | | file://:0:0:0:0 | Class | calls.rb:105:1:113:3 | Module | | file://:0:0:0:0 | Complex | file://:0:0:0:0 | Numeric | | file://:0:0:0:0 | FalseClass | calls.rb:115:1:118:3 | Object | @@ -256,6 +261,7 @@ getAnIncludedModule | calls.rb:43:1:58:3 | C | calls.rb:21:1:34:3 | M | | calls.rb:115:1:118:3 | Object | calls.rb:100:1:103:3 | Kernel | | calls.rb:531:1:544:3 | ProtectedMethods | calls.rb:525:1:529:3 | ProtectedMethodInModule | +| calls.rb:626:1:631:3 | IncludesIncluded | calls.rb:618:1:624:3 | Included | | hello.rb:11:1:16:3 | Greeting | hello.rb:1:1:8:3 | EnglishWords | | modules.rb:88:1:93:3 | IncludeTest | modules.rb:63:1:81:3 | Test | | modules.rb:95:1:99:3 | IncludeTest2 | modules.rb:63:1:81:3 | Test | @@ -358,6 +364,7 @@ resolveConstantReadAccess | calls.rb:614:1:614:10 | SingletonA | SingletonA | | calls.rb:615:1:615:10 | SingletonB | SingletonB | | calls.rb:616:1:616:10 | SingletonC | SingletonC | +| calls.rb:627:13:627:20 | Included | Included | | hello.rb:12:13:12:24 | EnglishWords | EnglishWords | | hello.rb:18:20:18:27 | Greeting | Greeting | | instance_fields.rb:4:22:4:31 | A_target | A_target | @@ -438,6 +445,8 @@ resolveConstantWriteAccess | calls.rb:583:1:594:3 | SingletonA | SingletonA | | calls.rb:596:1:603:3 | SingletonB | SingletonB | | calls.rb:605:1:612:3 | SingletonC | SingletonC | +| calls.rb:618:1:624:3 | Included | Included | +| calls.rb:626:1:631:3 | IncludesIncluded | IncludesIncluded | | hello.rb:1:1:8:3 | EnglishWords | EnglishWords | | hello.rb:11:1:16:3 | Greeting | Greeting | | hello.rb:18:1:22:3 | HelloWorld | HelloWorld | @@ -504,32 +513,32 @@ resolveConstantWriteAccess | unresolved_subclass.rb:7:1:8:3 | Subclass2 | UnresolvedNamespace::Subclass2 | | unresolved_subclass.rb:11:1:12:3 | A | UnresolvedNamespace::A | enclosingModule -| calls.rb:1:1:3:3 | foo | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:2:5:2:14 | call to puts | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:2:5:2:14 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:2:10:2:14 | "foo" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:2:11:2:13 | foo | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:5:1:5:3 | call to foo | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:5:1:5:3 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:7:1:9:3 | bar | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:7:5:7:8 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:8:5:8:15 | call to puts | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:8:5:8:15 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:8:10:8:15 | "bar1" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:8:11:8:14 | bar1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:11:1:11:4 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:11:1:11:8 | call to bar | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:13:1:15:3 | bar | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:13:5:13:8 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:14:5:14:15 | call to puts | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:14:5:14:15 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:14:10:14:15 | "bar2" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:14:11:14:14 | bar2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:17:1:17:4 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:17:1:17:8 | call to bar | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:19:1:19:4 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:19:1:19:8 | call to foo | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:21:1:34:3 | M | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:1:1:3:3 | foo | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:2:5:2:14 | call to puts | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:2:5:2:14 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:2:10:2:14 | "foo" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:2:11:2:13 | foo | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:5:1:5:3 | call to foo | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:5:1:5:3 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:7:1:9:3 | bar | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:7:5:7:8 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:8:5:8:15 | call to puts | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:8:5:8:15 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:8:10:8:15 | "bar1" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:8:11:8:14 | bar1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:11:1:11:4 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:11:1:11:8 | call to bar | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:13:1:15:3 | bar | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:13:5:13:8 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:14:5:14:15 | call to puts | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:14:5:14:15 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:14:10:14:15 | "bar2" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:14:11:14:14 | bar2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:17:1:17:4 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:17:1:17:8 | call to bar | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:19:1:19:4 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:19:1:19:8 | call to foo | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:21:1:34:3 | M | calls.rb:1:1:632:1 | calls.rb | | calls.rb:22:5:24:7 | instance_m | calls.rb:21:1:34:3 | M | | calls.rb:23:9:23:19 | call to singleton_m | calls.rb:21:1:34:3 | M | | calls.rb:23:9:23:19 | self | calls.rb:21:1:34:3 | M | @@ -545,14 +554,14 @@ enclosingModule | calls.rb:32:5:32:15 | self | calls.rb:21:1:34:3 | M | | calls.rb:33:5:33:8 | self | calls.rb:21:1:34:3 | M | | calls.rb:33:5:33:20 | call to singleton_m | calls.rb:21:1:34:3 | M | -| calls.rb:36:1:36:1 | M | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:36:1:36:12 | call to instance_m | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:37:1:37:1 | M | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:37:1:37:13 | call to singleton_m | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:39:1:41:3 | call_instance_m | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:40:5:40:14 | call to instance_m | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:40:5:40:14 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:43:1:58:3 | C | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:36:1:36:1 | M | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:36:1:36:12 | call to instance_m | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:37:1:37:1 | M | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:37:1:37:13 | call to singleton_m | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:39:1:41:3 | call_instance_m | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:40:5:40:14 | call to instance_m | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:40:5:40:14 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:43:1:58:3 | C | calls.rb:1:1:632:1 | calls.rb | | calls.rb:44:5:44:13 | call to include | calls.rb:43:1:58:3 | C | | calls.rb:44:5:44:13 | self | calls.rb:43:1:58:3 | C | | calls.rb:44:13:44:13 | M | calls.rb:43:1:58:3 | C | @@ -573,66 +582,66 @@ enclosingModule | calls.rb:55:9:55:19 | self | calls.rb:43:1:58:3 | C | | calls.rb:56:9:56:12 | self | calls.rb:43:1:58:3 | C | | calls.rb:56:9:56:24 | call to singleton_m | calls.rb:43:1:58:3 | C | -| calls.rb:60:1:60:1 | c | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:60:1:60:9 | ... = ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:60:5:60:5 | C | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:60:5:60:9 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:61:1:61:1 | c | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:61:1:61:5 | call to baz | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:62:1:62:1 | c | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:62:1:62:13 | call to singleton_m | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:63:1:63:1 | c | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:63:1:63:12 | call to instance_m | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:65:1:69:3 | D | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:65:11:65:11 | C | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:60:1:60:1 | c | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:60:1:60:9 | ... = ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:60:5:60:5 | C | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:60:5:60:9 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:61:1:61:1 | c | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:61:1:61:5 | call to baz | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:62:1:62:1 | c | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:62:1:62:13 | call to singleton_m | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:63:1:63:1 | c | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:63:1:63:12 | call to instance_m | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:65:1:69:3 | D | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:65:11:65:11 | C | calls.rb:1:1:632:1 | calls.rb | | calls.rb:66:5:68:7 | baz | calls.rb:65:1:69:3 | D | | calls.rb:67:9:67:13 | call to super | calls.rb:65:1:69:3 | D | -| calls.rb:71:1:71:1 | d | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:71:1:71:9 | ... = ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:71:5:71:5 | D | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:71:5:71:9 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:72:1:72:1 | d | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:72:1:72:5 | call to baz | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:73:1:73:1 | d | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:73:1:73:13 | call to singleton_m | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:74:1:74:1 | d | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:74:1:74:12 | call to instance_m | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:76:1:79:3 | optional_arg | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:76:18:76:18 | a | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:76:18:76:18 | a | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:76:22:76:22 | 4 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:76:25:76:25 | b | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:76:25:76:25 | b | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:76:28:76:28 | 5 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:77:5:77:5 | a | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:77:5:77:16 | call to bit_length | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:78:5:78:5 | b | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:78:5:78:16 | call to bit_length | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:81:1:83:3 | call_block | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:82:5:82:11 | yield ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:82:11:82:11 | 1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:85:1:89:3 | foo | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:86:5:86:7 | var | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:86:5:86:18 | ... = ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:86:11:86:14 | Hash | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:86:11:86:18 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:87:5:87:7 | var | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:87:5:87:10 | ...[...] | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:87:9:87:9 | 1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:88:5:88:29 | call to call_block | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:88:5:88:29 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:88:16:88:29 | { ... } | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:88:19:88:19 | x | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:88:19:88:19 | x | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:88:22:88:24 | var | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:88:22:88:27 | ...[...] | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:88:26:88:26 | x | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:91:1:94:3 | Integer | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:71:1:71:1 | d | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:71:1:71:9 | ... = ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:71:5:71:5 | D | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:71:5:71:9 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:72:1:72:1 | d | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:72:1:72:5 | call to baz | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:73:1:73:1 | d | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:73:1:73:13 | call to singleton_m | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:74:1:74:1 | d | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:74:1:74:12 | call to instance_m | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:76:1:79:3 | optional_arg | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:76:18:76:18 | a | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:76:18:76:18 | a | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:76:22:76:22 | 4 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:76:25:76:25 | b | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:76:25:76:25 | b | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:76:28:76:28 | 5 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:77:5:77:5 | a | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:77:5:77:16 | call to bit_length | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:78:5:78:5 | b | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:78:5:78:16 | call to bit_length | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:81:1:83:3 | call_block | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:82:5:82:11 | yield ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:82:11:82:11 | 1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:85:1:89:3 | foo | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:86:5:86:7 | var | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:86:5:86:18 | ... = ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:86:11:86:14 | Hash | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:86:11:86:18 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:87:5:87:7 | var | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:87:5:87:10 | ...[...] | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:87:9:87:9 | 1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:88:5:88:29 | call to call_block | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:88:5:88:29 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:88:16:88:29 | { ... } | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:88:19:88:19 | x | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:88:19:88:19 | x | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:88:22:88:24 | var | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:88:22:88:27 | ...[...] | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:88:26:88:26 | x | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:91:1:94:3 | Integer | calls.rb:1:1:632:1 | calls.rb | | calls.rb:92:5:92:23 | bit_length | calls.rb:91:1:94:3 | Integer | | calls.rb:93:5:93:16 | abs | calls.rb:91:1:94:3 | Integer | -| calls.rb:96:1:98:3 | String | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:96:1:98:3 | String | calls.rb:1:1:632:1 | calls.rb | | calls.rb:97:5:97:23 | capitalize | calls.rb:96:1:98:3 | String | -| calls.rb:100:1:103:3 | Kernel | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:100:1:103:3 | Kernel | calls.rb:1:1:632:1 | calls.rb | | calls.rb:101:5:101:25 | alias ... | calls.rb:100:1:103:3 | Kernel | | calls.rb:101:11:101:19 | :old_puts | calls.rb:100:1:103:3 | Kernel | | calls.rb:101:11:101:19 | old_puts | calls.rb:100:1:103:3 | Kernel | @@ -644,7 +653,7 @@ enclosingModule | calls.rb:102:17:102:26 | call to old_puts | calls.rb:100:1:103:3 | Kernel | | calls.rb:102:17:102:26 | self | calls.rb:100:1:103:3 | Kernel | | calls.rb:102:26:102:26 | x | calls.rb:100:1:103:3 | Kernel | -| calls.rb:105:1:113:3 | Module | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:105:1:113:3 | Module | calls.rb:1:1:632:1 | calls.rb | | calls.rb:106:5:106:31 | alias ... | calls.rb:105:1:113:3 | Module | | calls.rb:106:11:106:22 | :old_include | calls.rb:105:1:113:3 | Module | | calls.rb:106:11:106:22 | old_include | calls.rb:105:1:113:3 | Module | @@ -659,13 +668,13 @@ enclosingModule | calls.rb:109:21:109:21 | x | calls.rb:105:1:113:3 | Module | | calls.rb:111:5:111:20 | prepend | calls.rb:105:1:113:3 | Module | | calls.rb:112:5:112:20 | private | calls.rb:105:1:113:3 | Module | -| calls.rb:115:1:118:3 | Object | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:115:16:115:21 | Module | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:115:1:118:3 | Object | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:115:16:115:21 | Module | calls.rb:1:1:632:1 | calls.rb | | calls.rb:116:5:116:18 | call to include | calls.rb:115:1:118:3 | Object | | calls.rb:116:5:116:18 | self | calls.rb:115:1:118:3 | Object | | calls.rb:116:13:116:18 | Kernel | calls.rb:115:1:118:3 | Object | | calls.rb:117:5:117:16 | new | calls.rb:115:1:118:3 | Object | -| calls.rb:120:1:123:3 | Hash | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:120:1:123:3 | Hash | calls.rb:1:1:632:1 | calls.rb | | calls.rb:121:5:121:25 | alias ... | calls.rb:120:1:123:3 | Hash | | calls.rb:121:11:121:21 | :old_lookup | calls.rb:120:1:123:3 | Hash | | calls.rb:121:11:121:21 | old_lookup | calls.rb:120:1:123:3 | Hash | @@ -677,7 +686,7 @@ enclosingModule | calls.rb:122:15:122:27 | call to old_lookup | calls.rb:120:1:123:3 | Hash | | calls.rb:122:15:122:27 | self | calls.rb:120:1:123:3 | Hash | | calls.rb:122:26:122:26 | x | calls.rb:120:1:123:3 | Hash | -| calls.rb:125:1:138:3 | Array | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:125:1:138:3 | Array | calls.rb:1:1:632:1 | calls.rb | | calls.rb:126:3:126:23 | alias ... | calls.rb:125:1:138:3 | Array | | calls.rb:126:9:126:19 | :old_lookup | calls.rb:125:1:138:3 | Array | | calls.rb:126:9:126:19 | old_lookup | calls.rb:125:1:138:3 | Array | @@ -713,130 +722,130 @@ enclosingModule | calls.rb:135:9:135:14 | ... = ... | calls.rb:125:1:138:3 | Array | | calls.rb:135:11:135:12 | ... + ... | calls.rb:125:1:138:3 | Array | | calls.rb:135:14:135:14 | 1 | calls.rb:125:1:138:3 | Array | -| calls.rb:140:1:142:3 | funny | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:141:5:141:20 | yield ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:141:11:141:20 | "prefix: " | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:141:12:141:19 | prefix: | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:144:1:144:30 | call to funny | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:144:1:144:30 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:144:7:144:30 | { ... } | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:144:10:144:10 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:144:10:144:10 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:144:13:144:29 | call to puts | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:144:13:144:29 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:144:18:144:18 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:144:18:144:29 | call to capitalize | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:146:1:146:3 | "a" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:146:1:146:14 | call to capitalize | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:146:2:146:2 | a | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:147:1:147:1 | 1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:147:1:147:12 | call to bit_length | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:148:1:148:1 | 1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:148:1:148:5 | call to abs | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:1:150:13 | Array | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:1:150:13 | [...] | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:1:150:13 | call to [] | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:1:150:62 | call to foreach | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:2:150:4 | "a" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:3:150:3 | a | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:6:150:8 | "b" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:7:150:7 | b | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:10:150:12 | "c" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:11:150:11 | c | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:23:150:62 | { ... } | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:26:150:26 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:26:150:26 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:29:150:29 | v | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:29:150:29 | v | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:32:150:61 | call to puts | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:32:150:61 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:37:150:61 | "#{...} -> #{...}" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:38:150:41 | #{...} | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:40:150:40 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:42:150:45 | -> | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:46:150:60 | #{...} | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:48:150:48 | v | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:150:48:150:59 | call to capitalize | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:152:1:152:7 | Array | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:152:1:152:7 | [...] | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:152:1:152:7 | call to [] | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:152:1:152:35 | call to foreach | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:152:2:152:2 | 1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:152:4:152:4 | 2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:152:6:152:6 | 3 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:152:17:152:35 | { ... } | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:152:20:152:20 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:152:20:152:20 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:152:23:152:23 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:152:23:152:34 | call to bit_length | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:154:1:154:7 | Array | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:154:1:154:7 | [...] | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:154:1:154:7 | call to [] | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:154:1:154:40 | call to foreach | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:154:2:154:2 | 1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:154:4:154:4 | 2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:154:6:154:6 | 3 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:154:17:154:40 | { ... } | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:154:20:154:20 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:154:20:154:20 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:154:23:154:39 | call to puts | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:154:23:154:39 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:154:28:154:28 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:154:28:154:39 | call to capitalize | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:156:1:156:8 | Array | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:156:1:156:8 | [...] | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:156:1:156:8 | call to [] | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:156:1:156:37 | call to foreach | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:156:2:156:2 | 1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:156:4:156:5 | - ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:156:5:156:5 | 2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:156:7:156:7 | 3 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:156:18:156:37 | { ... } | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:156:21:156:21 | _ | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:156:21:156:21 | _ | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:156:24:156:24 | v | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:156:24:156:24 | v | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:156:27:156:36 | call to puts | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:156:27:156:36 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:156:32:156:32 | v | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:156:32:156:36 | call to abs | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:158:1:160:3 | indirect | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:158:14:158:15 | &b | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:158:15:158:15 | b | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:159:5:159:17 | call to call_block | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:159:5:159:17 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:159:16:159:17 | &... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:159:17:159:17 | b | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:162:1:162:28 | call to indirect | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:162:1:162:28 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:162:10:162:28 | { ... } | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:162:13:162:13 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:162:13:162:13 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:162:16:162:16 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:162:16:162:27 | call to bit_length | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:165:1:169:3 | S | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:140:1:142:3 | funny | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:141:5:141:20 | yield ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:141:11:141:20 | "prefix: " | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:141:12:141:19 | prefix: | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:144:1:144:30 | call to funny | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:144:1:144:30 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:144:7:144:30 | { ... } | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:144:10:144:10 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:144:10:144:10 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:144:13:144:29 | call to puts | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:144:13:144:29 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:144:18:144:18 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:144:18:144:29 | call to capitalize | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:146:1:146:3 | "a" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:146:1:146:14 | call to capitalize | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:146:2:146:2 | a | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:147:1:147:1 | 1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:147:1:147:12 | call to bit_length | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:148:1:148:1 | 1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:148:1:148:5 | call to abs | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:1:150:13 | Array | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:1:150:13 | [...] | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:1:150:13 | call to [] | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:1:150:62 | call to foreach | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:2:150:4 | "a" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:3:150:3 | a | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:6:150:8 | "b" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:7:150:7 | b | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:10:150:12 | "c" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:11:150:11 | c | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:23:150:62 | { ... } | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:26:150:26 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:26:150:26 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:29:150:29 | v | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:29:150:29 | v | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:32:150:61 | call to puts | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:32:150:61 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:37:150:61 | "#{...} -> #{...}" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:38:150:41 | #{...} | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:40:150:40 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:42:150:45 | -> | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:46:150:60 | #{...} | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:48:150:48 | v | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:150:48:150:59 | call to capitalize | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:152:1:152:7 | Array | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:152:1:152:7 | [...] | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:152:1:152:7 | call to [] | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:152:1:152:35 | call to foreach | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:152:2:152:2 | 1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:152:4:152:4 | 2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:152:6:152:6 | 3 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:152:17:152:35 | { ... } | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:152:20:152:20 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:152:20:152:20 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:152:23:152:23 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:152:23:152:34 | call to bit_length | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:154:1:154:7 | Array | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:154:1:154:7 | [...] | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:154:1:154:7 | call to [] | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:154:1:154:40 | call to foreach | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:154:2:154:2 | 1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:154:4:154:4 | 2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:154:6:154:6 | 3 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:154:17:154:40 | { ... } | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:154:20:154:20 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:154:20:154:20 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:154:23:154:39 | call to puts | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:154:23:154:39 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:154:28:154:28 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:154:28:154:39 | call to capitalize | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:156:1:156:8 | Array | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:156:1:156:8 | [...] | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:156:1:156:8 | call to [] | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:156:1:156:37 | call to foreach | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:156:2:156:2 | 1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:156:4:156:5 | - ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:156:5:156:5 | 2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:156:7:156:7 | 3 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:156:18:156:37 | { ... } | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:156:21:156:21 | _ | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:156:21:156:21 | _ | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:156:24:156:24 | v | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:156:24:156:24 | v | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:156:27:156:36 | call to puts | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:156:27:156:36 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:156:32:156:32 | v | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:156:32:156:36 | call to abs | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:158:1:160:3 | indirect | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:158:14:158:15 | &b | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:158:15:158:15 | b | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:159:5:159:17 | call to call_block | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:159:5:159:17 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:159:16:159:17 | &... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:159:17:159:17 | b | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:162:1:162:28 | call to indirect | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:162:1:162:28 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:162:10:162:28 | { ... } | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:162:13:162:13 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:162:13:162:13 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:162:16:162:16 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:162:16:162:27 | call to bit_length | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:165:1:169:3 | S | calls.rb:1:1:632:1 | calls.rb | | calls.rb:166:5:168:7 | s_method | calls.rb:165:1:169:3 | S | | calls.rb:167:9:167:12 | self | calls.rb:165:1:169:3 | S | | calls.rb:167:9:167:17 | call to to_s | calls.rb:165:1:169:3 | S | -| calls.rb:171:1:174:3 | A | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:171:11:171:11 | S | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:171:1:174:3 | A | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:171:11:171:11 | S | calls.rb:1:1:632:1 | calls.rb | | calls.rb:172:5:173:7 | to_s | calls.rb:171:1:174:3 | A | -| calls.rb:176:1:179:3 | B | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:176:11:176:11 | S | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:176:1:179:3 | B | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:176:11:176:11 | S | calls.rb:1:1:632:1 | calls.rb | | calls.rb:177:5:178:7 | to_s | calls.rb:176:1:179:3 | B | -| calls.rb:181:1:181:1 | S | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:181:1:181:5 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:181:1:181:14 | call to s_method | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:182:1:182:1 | A | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:182:1:182:5 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:182:1:182:14 | call to s_method | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:183:1:183:1 | B | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:183:1:183:5 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:183:1:183:14 | call to s_method | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:185:1:186:3 | private_on_main | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:188:1:188:15 | call to private_on_main | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:188:1:188:15 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:190:1:226:3 | Singletons | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:181:1:181:1 | S | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:181:1:181:5 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:181:1:181:14 | call to s_method | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:182:1:182:1 | A | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:182:1:182:5 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:182:1:182:14 | call to s_method | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:183:1:183:1 | B | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:183:1:183:5 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:183:1:183:14 | call to s_method | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:185:1:186:3 | private_on_main | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:188:1:188:15 | call to private_on_main | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:188:1:188:15 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:190:1:226:3 | Singletons | calls.rb:1:1:632:1 | calls.rb | | calls.rb:191:5:194:7 | singleton_a | calls.rb:190:1:226:3 | Singletons | | calls.rb:191:9:191:12 | self | calls.rb:190:1:226:3 | Singletons | | calls.rb:192:9:192:26 | call to puts | calls.rb:190:1:226:3 | Singletons | @@ -886,126 +895,126 @@ enclosingModule | calls.rb:223:5:225:7 | call_singleton_g | calls.rb:190:1:226:3 | Singletons | | calls.rb:224:9:224:12 | self | calls.rb:190:1:226:3 | Singletons | | calls.rb:224:9:224:24 | call to singleton_g | calls.rb:190:1:226:3 | Singletons | -| calls.rb:228:1:228:10 | Singletons | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:228:1:228:22 | call to singleton_a | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:229:1:229:10 | Singletons | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:229:1:229:22 | call to singleton_f | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:231:1:231:2 | c1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:231:1:231:19 | ... = ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:231:6:231:15 | Singletons | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:231:6:231:19 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:233:1:233:2 | c1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:233:1:233:11 | call to instance | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:234:1:234:2 | c1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:234:1:234:14 | call to singleton_e | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:236:1:238:3 | singleton_g | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:236:5:236:6 | c1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:237:5:237:24 | call to puts | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:237:5:237:24 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:237:10:237:24 | "singleton_g_1" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:237:11:237:23 | singleton_g_1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:240:1:240:2 | c1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:240:1:240:14 | call to singleton_g | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:241:1:241:2 | c1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:241:1:241:19 | call to call_singleton_g | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:243:1:245:3 | singleton_g | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:243:5:243:6 | c1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:244:5:244:24 | call to puts | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:244:5:244:24 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:244:10:244:24 | "singleton_g_2" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:244:11:244:23 | singleton_g_2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:247:1:247:2 | c1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:247:1:247:14 | call to singleton_g | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:248:1:248:2 | c1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:248:1:248:19 | call to call_singleton_g | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:250:1:254:3 | class << ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:250:10:250:11 | c1 | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:228:1:228:10 | Singletons | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:228:1:228:22 | call to singleton_a | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:229:1:229:10 | Singletons | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:229:1:229:22 | call to singleton_f | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:231:1:231:2 | c1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:231:1:231:19 | ... = ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:231:6:231:15 | Singletons | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:231:6:231:19 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:233:1:233:2 | c1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:233:1:233:11 | call to instance | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:234:1:234:2 | c1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:234:1:234:14 | call to singleton_e | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:236:1:238:3 | singleton_g | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:236:5:236:6 | c1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:237:5:237:24 | call to puts | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:237:5:237:24 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:237:10:237:24 | "singleton_g_1" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:237:11:237:23 | singleton_g_1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:240:1:240:2 | c1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:240:1:240:14 | call to singleton_g | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:241:1:241:2 | c1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:241:1:241:19 | call to call_singleton_g | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:243:1:245:3 | singleton_g | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:243:5:243:6 | c1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:244:5:244:24 | call to puts | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:244:5:244:24 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:244:10:244:24 | "singleton_g_2" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:244:11:244:23 | singleton_g_2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:247:1:247:2 | c1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:247:1:247:14 | call to singleton_g | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:248:1:248:2 | c1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:248:1:248:19 | call to call_singleton_g | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:250:1:254:3 | class << ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:250:10:250:11 | c1 | calls.rb:1:1:632:1 | calls.rb | | calls.rb:251:5:253:7 | singleton_g | calls.rb:250:1:254:3 | class << ... | | calls.rb:252:9:252:28 | call to puts | calls.rb:250:1:254:3 | class << ... | | calls.rb:252:9:252:28 | self | calls.rb:250:1:254:3 | class << ... | | calls.rb:252:14:252:28 | "singleton_g_3" | calls.rb:250:1:254:3 | class << ... | | calls.rb:252:15:252:27 | singleton_g_3 | calls.rb:250:1:254:3 | class << ... | -| calls.rb:256:1:256:2 | c1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:256:1:256:14 | call to singleton_g | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:257:1:257:2 | c1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:257:1:257:19 | call to call_singleton_g | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:259:1:259:2 | c2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:259:1:259:19 | ... = ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:259:6:259:15 | Singletons | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:259:6:259:19 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:260:1:260:2 | c2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:260:1:260:14 | call to singleton_e | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:261:1:261:2 | c2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:261:1:261:14 | call to singleton_g | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:263:1:263:4 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:263:1:263:8 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:265:1:265:16 | call to puts | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:265:1:265:16 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:265:6:265:16 | "top-level" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:265:7:265:15 | top-level | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:267:1:269:3 | singleton_g | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:267:5:267:14 | Singletons | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:268:5:268:22 | call to puts | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:268:5:268:22 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:268:10:268:22 | "singleton_g" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:268:11:268:21 | singleton_g | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:271:1:271:10 | Singletons | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:271:1:271:22 | call to singleton_g | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:272:1:272:2 | c1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:272:1:272:14 | call to singleton_g | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:273:1:273:2 | c1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:273:1:273:19 | call to call_singleton_g | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:274:1:274:2 | c2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:274:1:274:14 | call to singleton_g | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:275:1:275:2 | c3 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:275:1:275:19 | ... = ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:275:6:275:15 | Singletons | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:275:6:275:19 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:276:1:276:2 | c3 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:276:1:276:14 | call to singleton_g | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:278:1:286:3 | create | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:278:12:278:15 | type | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:278:12:278:15 | type | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:279:5:279:8 | type | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:279:5:279:12 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:279:5:279:21 | call to instance | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:281:5:283:7 | singleton_h | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:281:9:281:12 | type | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:282:9:282:26 | call to puts | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:282:9:282:26 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:282:14:282:26 | "singleton_h" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:282:15:282:25 | singleton_h | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:285:5:285:8 | type | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:285:5:285:20 | call to singleton_h | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:288:1:288:17 | call to create | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:288:1:288:17 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:288:8:288:17 | Singletons | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:289:1:289:10 | Singletons | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:289:1:289:22 | call to singleton_h | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:291:1:291:1 | x | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:291:1:291:14 | ... = ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:291:5:291:14 | Singletons | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:293:1:297:3 | class << ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:293:10:293:10 | x | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:256:1:256:2 | c1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:256:1:256:14 | call to singleton_g | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:257:1:257:2 | c1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:257:1:257:19 | call to call_singleton_g | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:259:1:259:2 | c2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:259:1:259:19 | ... = ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:259:6:259:15 | Singletons | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:259:6:259:19 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:260:1:260:2 | c2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:260:1:260:14 | call to singleton_e | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:261:1:261:2 | c2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:261:1:261:14 | call to singleton_g | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:263:1:263:4 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:263:1:263:8 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:265:1:265:16 | call to puts | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:265:1:265:16 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:265:6:265:16 | "top-level" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:265:7:265:15 | top-level | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:267:1:269:3 | singleton_g | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:267:5:267:14 | Singletons | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:268:5:268:22 | call to puts | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:268:5:268:22 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:268:10:268:22 | "singleton_g" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:268:11:268:21 | singleton_g | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:271:1:271:10 | Singletons | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:271:1:271:22 | call to singleton_g | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:272:1:272:2 | c1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:272:1:272:14 | call to singleton_g | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:273:1:273:2 | c1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:273:1:273:19 | call to call_singleton_g | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:274:1:274:2 | c2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:274:1:274:14 | call to singleton_g | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:275:1:275:2 | c3 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:275:1:275:19 | ... = ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:275:6:275:15 | Singletons | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:275:6:275:19 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:276:1:276:2 | c3 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:276:1:276:14 | call to singleton_g | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:278:1:286:3 | create | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:278:12:278:15 | type | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:278:12:278:15 | type | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:279:5:279:8 | type | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:279:5:279:12 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:279:5:279:21 | call to instance | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:281:5:283:7 | singleton_h | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:281:9:281:12 | type | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:282:9:282:26 | call to puts | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:282:9:282:26 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:282:14:282:26 | "singleton_h" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:282:15:282:25 | singleton_h | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:285:5:285:8 | type | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:285:5:285:20 | call to singleton_h | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:288:1:288:17 | call to create | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:288:1:288:17 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:288:8:288:17 | Singletons | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:289:1:289:10 | Singletons | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:289:1:289:22 | call to singleton_h | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:291:1:291:1 | x | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:291:1:291:14 | ... = ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:291:5:291:14 | Singletons | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:293:1:297:3 | class << ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:293:10:293:10 | x | calls.rb:1:1:632:1 | calls.rb | | calls.rb:294:5:296:7 | singleton_i | calls.rb:293:1:297:3 | class << ... | | calls.rb:295:9:295:26 | call to puts | calls.rb:293:1:297:3 | class << ... | | calls.rb:295:9:295:26 | self | calls.rb:293:1:297:3 | class << ... | | calls.rb:295:14:295:26 | "singleton_i" | calls.rb:293:1:297:3 | class << ... | | calls.rb:295:15:295:25 | singleton_i | calls.rb:293:1:297:3 | class << ... | -| calls.rb:299:1:299:1 | x | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:299:1:299:13 | call to singleton_i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:300:1:300:10 | Singletons | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:300:1:300:22 | call to singleton_i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:302:1:306:3 | class << ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:302:10:302:19 | Singletons | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:299:1:299:1 | x | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:299:1:299:13 | call to singleton_i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:300:1:300:10 | Singletons | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:300:1:300:22 | call to singleton_i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:302:1:306:3 | class << ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:302:10:302:19 | Singletons | calls.rb:1:1:632:1 | calls.rb | | calls.rb:303:5:305:7 | singleton_j | calls.rb:302:1:306:3 | class << ... | | calls.rb:304:9:304:26 | call to puts | calls.rb:302:1:306:3 | class << ... | | calls.rb:304:9:304:26 | self | calls.rb:302:1:306:3 | class << ... | | calls.rb:304:14:304:26 | "singleton_j" | calls.rb:302:1:306:3 | class << ... | | calls.rb:304:15:304:25 | singleton_j | calls.rb:302:1:306:3 | class << ... | -| calls.rb:308:1:308:10 | Singletons | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:308:1:308:22 | call to singleton_j | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:310:1:321:3 | SelfNew | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:308:1:308:10 | Singletons | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:308:1:308:22 | call to singleton_j | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:310:1:321:3 | SelfNew | calls.rb:1:1:632:1 | calls.rb | | calls.rb:311:5:314:7 | instance | calls.rb:310:1:321:3 | SelfNew | | calls.rb:312:9:312:31 | call to puts | calls.rb:310:1:321:3 | SelfNew | | calls.rb:312:9:312:31 | self | calls.rb:310:1:321:3 | SelfNew | @@ -1022,110 +1031,110 @@ enclosingModule | calls.rb:320:5:320:7 | call to new | calls.rb:310:1:321:3 | SelfNew | | calls.rb:320:5:320:7 | self | calls.rb:310:1:321:3 | SelfNew | | calls.rb:320:5:320:16 | call to instance | calls.rb:310:1:321:3 | SelfNew | -| calls.rb:323:1:323:7 | SelfNew | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:323:1:323:17 | call to singleton | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:325:1:329:3 | C1 | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:323:1:323:7 | SelfNew | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:323:1:323:17 | call to singleton | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:325:1:329:3 | C1 | calls.rb:1:1:632:1 | calls.rb | | calls.rb:326:5:328:7 | instance | calls.rb:325:1:329:3 | C1 | | calls.rb:327:9:327:26 | call to puts | calls.rb:325:1:329:3 | C1 | | calls.rb:327:9:327:26 | self | calls.rb:325:1:329:3 | C1 | | calls.rb:327:14:327:26 | "C1#instance" | calls.rb:325:1:329:3 | C1 | | calls.rb:327:15:327:25 | C1#instance | calls.rb:325:1:329:3 | C1 | -| calls.rb:331:1:335:3 | C2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:331:12:331:13 | C1 | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:331:1:335:3 | C2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:331:12:331:13 | C1 | calls.rb:1:1:632:1 | calls.rb | | calls.rb:332:5:334:7 | instance | calls.rb:331:1:335:3 | C2 | | calls.rb:333:9:333:26 | call to puts | calls.rb:331:1:335:3 | C2 | | calls.rb:333:9:333:26 | self | calls.rb:331:1:335:3 | C2 | | calls.rb:333:14:333:26 | "C2#instance" | calls.rb:331:1:335:3 | C2 | | calls.rb:333:15:333:25 | C2#instance | calls.rb:331:1:335:3 | C2 | -| calls.rb:337:1:341:3 | C3 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:337:12:337:13 | C2 | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:337:1:341:3 | C3 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:337:12:337:13 | C2 | calls.rb:1:1:632:1 | calls.rb | | calls.rb:338:5:340:7 | instance | calls.rb:337:1:341:3 | C3 | | calls.rb:339:9:339:26 | call to puts | calls.rb:337:1:341:3 | C3 | | calls.rb:339:9:339:26 | self | calls.rb:337:1:341:3 | C3 | | calls.rb:339:14:339:26 | "C3#instance" | calls.rb:337:1:341:3 | C3 | | calls.rb:339:15:339:25 | C3#instance | calls.rb:337:1:341:3 | C3 | -| calls.rb:343:1:359:3 | pattern_dispatch | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:343:22:343:22 | x | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:343:22:343:22 | x | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:344:5:352:7 | case ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:344:10:344:10 | x | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:345:5:346:18 | when ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:345:10:345:11 | C3 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:345:12:346:18 | then ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:346:9:346:9 | x | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:346:9:346:18 | call to instance | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:347:5:348:18 | when ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:347:10:347:11 | C2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:347:12:348:18 | then ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:348:9:348:9 | x | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:348:9:348:18 | call to instance | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:349:5:350:18 | when ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:349:10:349:11 | C1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:349:12:350:18 | then ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:350:9:350:9 | x | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:350:9:350:18 | call to instance | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:351:5:351:8 | else ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:354:5:358:7 | case ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:354:10:354:10 | x | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:355:9:355:29 | in ... then ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:355:12:355:13 | C3 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:355:15:355:29 | then ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:355:20:355:20 | x | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:355:20:355:29 | call to instance | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:356:9:356:36 | in ... then ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:356:12:356:13 | C2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:356:12:356:19 | ... => ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:356:18:356:19 | c2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:356:21:356:36 | then ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:356:26:356:27 | c2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:356:26:356:36 | call to instance | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:357:9:357:36 | in ... then ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:357:12:357:13 | C1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:357:12:357:19 | ... => ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:357:18:357:19 | c1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:357:21:357:36 | then ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:357:26:357:27 | c1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:357:26:357:36 | call to instance | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:361:1:361:2 | c1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:361:1:361:11 | ... = ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:361:6:361:7 | C1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:361:6:361:11 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:362:1:362:2 | c1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:362:1:362:11 | call to instance | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:363:1:363:25 | call to pattern_dispatch | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:363:1:363:25 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:363:18:363:25 | ( ... ) | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:363:19:363:20 | C1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:363:19:363:24 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:364:1:364:25 | call to pattern_dispatch | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:364:1:364:25 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:364:18:364:25 | ( ... ) | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:364:19:364:20 | C2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:364:19:364:24 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:365:1:365:25 | call to pattern_dispatch | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:365:1:365:25 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:365:18:365:25 | ( ... ) | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:365:19:365:20 | C3 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:365:19:365:24 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:367:1:371:3 | add_singleton | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:367:19:367:19 | x | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:367:19:367:19 | x | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:368:5:370:7 | instance | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:368:9:368:9 | x | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:369:9:369:28 | call to puts | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:369:9:369:28 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:369:14:369:28 | "instance_on x" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:369:15:369:27 | instance_on x | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:373:1:373:2 | c3 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:373:1:373:11 | ... = ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:373:6:373:7 | C1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:373:6:373:11 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:374:1:374:16 | call to add_singleton | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:374:1:374:16 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:374:15:374:16 | c3 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:375:1:375:2 | c3 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:375:1:375:11 | call to instance | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:377:1:405:3 | SingletonOverride1 | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:343:1:359:3 | pattern_dispatch | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:343:22:343:22 | x | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:343:22:343:22 | x | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:344:5:352:7 | case ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:344:10:344:10 | x | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:345:5:346:18 | when ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:345:10:345:11 | C3 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:345:12:346:18 | then ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:346:9:346:9 | x | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:346:9:346:18 | call to instance | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:347:5:348:18 | when ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:347:10:347:11 | C2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:347:12:348:18 | then ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:348:9:348:9 | x | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:348:9:348:18 | call to instance | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:349:5:350:18 | when ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:349:10:349:11 | C1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:349:12:350:18 | then ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:350:9:350:9 | x | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:350:9:350:18 | call to instance | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:351:5:351:8 | else ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:354:5:358:7 | case ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:354:10:354:10 | x | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:355:9:355:29 | in ... then ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:355:12:355:13 | C3 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:355:15:355:29 | then ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:355:20:355:20 | x | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:355:20:355:29 | call to instance | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:356:9:356:36 | in ... then ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:356:12:356:13 | C2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:356:12:356:19 | ... => ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:356:18:356:19 | c2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:356:21:356:36 | then ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:356:26:356:27 | c2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:356:26:356:36 | call to instance | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:357:9:357:36 | in ... then ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:357:12:357:13 | C1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:357:12:357:19 | ... => ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:357:18:357:19 | c1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:357:21:357:36 | then ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:357:26:357:27 | c1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:357:26:357:36 | call to instance | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:361:1:361:2 | c1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:361:1:361:11 | ... = ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:361:6:361:7 | C1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:361:6:361:11 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:362:1:362:2 | c1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:362:1:362:11 | call to instance | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:363:1:363:25 | call to pattern_dispatch | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:363:1:363:25 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:363:18:363:25 | ( ... ) | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:363:19:363:20 | C1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:363:19:363:24 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:364:1:364:25 | call to pattern_dispatch | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:364:1:364:25 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:364:18:364:25 | ( ... ) | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:364:19:364:20 | C2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:364:19:364:24 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:365:1:365:25 | call to pattern_dispatch | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:365:1:365:25 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:365:18:365:25 | ( ... ) | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:365:19:365:20 | C3 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:365:19:365:24 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:367:1:371:3 | add_singleton | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:367:19:367:19 | x | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:367:19:367:19 | x | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:368:5:370:7 | instance | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:368:9:368:9 | x | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:369:9:369:28 | call to puts | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:369:9:369:28 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:369:14:369:28 | "instance_on x" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:369:15:369:27 | instance_on x | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:373:1:373:2 | c3 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:373:1:373:11 | ... = ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:373:6:373:7 | C1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:373:6:373:11 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:374:1:374:16 | call to add_singleton | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:374:1:374:16 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:374:15:374:16 | c3 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:375:1:375:2 | c3 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:375:1:375:11 | call to instance | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:377:1:405:3 | SingletonOverride1 | calls.rb:1:1:632:1 | calls.rb | | calls.rb:378:5:390:7 | class << ... | calls.rb:377:1:405:3 | SingletonOverride1 | | calls.rb:378:14:378:17 | self | calls.rb:377:1:405:3 | SingletonOverride1 | | calls.rb:379:9:381:11 | singleton1 | calls.rb:378:5:390:7 | class << ... | @@ -1157,16 +1166,16 @@ enclosingModule | calls.rb:403:9:403:43 | self | calls.rb:377:1:405:3 | SingletonOverride1 | | calls.rb:403:14:403:43 | "SingletonOverride1#instance1" | calls.rb:377:1:405:3 | SingletonOverride1 | | calls.rb:403:15:403:42 | SingletonOverride1#instance1 | calls.rb:377:1:405:3 | SingletonOverride1 | -| calls.rb:407:1:407:18 | SingletonOverride1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:407:1:407:29 | call to singleton1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:408:1:408:18 | SingletonOverride1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:408:1:408:29 | call to singleton2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:409:1:409:18 | SingletonOverride1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:409:1:409:34 | call to call_singleton1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:410:1:410:18 | SingletonOverride1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:410:1:410:34 | call to call_singleton2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:412:1:426:3 | SingletonOverride2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:412:28:412:45 | SingletonOverride1 | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:407:1:407:18 | SingletonOverride1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:407:1:407:29 | call to singleton1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:408:1:408:18 | SingletonOverride1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:408:1:408:29 | call to singleton2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:409:1:409:18 | SingletonOverride1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:409:1:409:34 | call to call_singleton1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:410:1:410:18 | SingletonOverride1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:410:1:410:34 | call to call_singleton2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:412:1:426:3 | SingletonOverride2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:412:28:412:45 | SingletonOverride1 | calls.rb:1:1:632:1 | calls.rb | | calls.rb:413:5:417:7 | class << ... | calls.rb:412:1:426:3 | SingletonOverride2 | | calls.rb:413:14:413:17 | self | calls.rb:412:1:426:3 | SingletonOverride2 | | calls.rb:414:9:416:11 | singleton1 | calls.rb:413:5:417:7 | class << ... | @@ -1185,15 +1194,15 @@ enclosingModule | calls.rb:424:9:424:43 | self | calls.rb:412:1:426:3 | SingletonOverride2 | | calls.rb:424:14:424:43 | "SingletonOverride2#instance1" | calls.rb:412:1:426:3 | SingletonOverride2 | | calls.rb:424:15:424:42 | SingletonOverride2#instance1 | calls.rb:412:1:426:3 | SingletonOverride2 | -| calls.rb:428:1:428:18 | SingletonOverride2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:428:1:428:29 | call to singleton1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:429:1:429:18 | SingletonOverride2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:429:1:429:29 | call to singleton2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:430:1:430:18 | SingletonOverride2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:430:1:430:34 | call to call_singleton1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:431:1:431:18 | SingletonOverride2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:431:1:431:34 | call to call_singleton2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:433:1:461:3 | ConditionalInstanceMethods | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:428:1:428:18 | SingletonOverride2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:428:1:428:29 | call to singleton1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:429:1:429:18 | SingletonOverride2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:429:1:429:29 | call to singleton2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:430:1:430:18 | SingletonOverride2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:430:1:430:34 | call to call_singleton1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:431:1:431:18 | SingletonOverride2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:431:1:431:34 | call to call_singleton2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:433:1:461:3 | ConditionalInstanceMethods | calls.rb:1:1:632:1 | calls.rb | | calls.rb:434:5:438:7 | if ... | calls.rb:433:1:461:3 | ConditionalInstanceMethods | | calls.rb:434:8:434:13 | call to rand | calls.rb:433:1:461:3 | ConditionalInstanceMethods | | calls.rb:434:8:434:13 | self | calls.rb:433:1:461:3 | ConditionalInstanceMethods | @@ -1238,91 +1247,91 @@ enclosingModule | calls.rb:457:17:457:40 | self | calls.rb:433:1:461:3 | ConditionalInstanceMethods | | calls.rb:457:22:457:40 | "AnonymousClass#m5" | calls.rb:433:1:461:3 | ConditionalInstanceMethods | | calls.rb:457:23:457:39 | AnonymousClass#m5 | calls.rb:433:1:461:3 | ConditionalInstanceMethods | -| calls.rb:463:1:463:26 | ConditionalInstanceMethods | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:463:1:463:30 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:463:1:463:33 | call to m1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:464:1:464:26 | ConditionalInstanceMethods | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:464:1:464:30 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:464:1:464:33 | call to m3 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:465:1:465:26 | ConditionalInstanceMethods | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:465:1:465:30 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:465:1:465:33 | call to m2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:466:1:466:26 | ConditionalInstanceMethods | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:466:1:466:30 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:466:1:466:33 | call to m3 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:467:1:467:26 | ConditionalInstanceMethods | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:467:1:467:30 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:467:1:467:33 | call to m4 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:468:1:468:26 | ConditionalInstanceMethods | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:468:1:468:30 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:468:1:468:33 | call to m5 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:470:1:470:23 | EsotericInstanceMethods | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:470:1:488:3 | ... = ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:470:27:470:31 | Class | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:470:27:488:3 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:470:37:488:3 | do ... end | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:471:5:471:11 | Array | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:471:5:471:11 | [...] | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:471:5:471:11 | call to [] | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:471:5:475:7 | call to each | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:471:6:471:6 | 0 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:471:8:471:8 | 1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:471:10:471:10 | 2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:471:18:475:7 | do ... end | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:472:9:474:11 | foo | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:473:13:473:22 | call to puts | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:473:13:473:22 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:473:18:473:22 | "foo" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:473:19:473:21 | foo | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:477:5:477:9 | Class | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:477:5:481:7 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:477:5:481:11 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:477:5:481:15 | call to bar | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:477:15:481:7 | do ... end | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:478:9:480:11 | bar | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:479:13:479:22 | call to puts | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:479:13:479:22 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:479:18:479:22 | "bar" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:479:19:479:21 | bar | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:483:5:483:11 | Array | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:483:5:483:11 | [...] | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:483:5:483:11 | call to [] | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:483:5:487:7 | call to each | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:483:6:483:6 | 0 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:483:8:483:8 | 1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:483:10:483:10 | 2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:483:18:487:7 | do ... end | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:483:22:483:22 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:483:22:483:22 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:484:9:486:11 | call to define_method | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:484:9:486:11 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:484:23:484:32 | "baz_#{...}" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:484:24:484:27 | baz_ | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:484:28:484:31 | #{...} | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:484:30:484:30 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:484:35:486:11 | do ... end | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:485:13:485:27 | call to puts | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:485:13:485:27 | self | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:485:18:485:27 | "baz_#{...}" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:485:19:485:22 | baz_ | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:485:23:485:26 | #{...} | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:485:25:485:25 | i | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:490:1:490:23 | EsotericInstanceMethods | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:490:1:490:27 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:490:1:490:31 | call to foo | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:491:1:491:23 | EsotericInstanceMethods | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:491:1:491:27 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:491:1:491:31 | call to bar | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:492:1:492:23 | EsotericInstanceMethods | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:492:1:492:27 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:492:1:492:33 | call to baz_0 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:493:1:493:23 | EsotericInstanceMethods | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:493:1:493:27 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:493:1:493:33 | call to baz_1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:494:1:494:23 | EsotericInstanceMethods | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:494:1:494:27 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:494:1:494:33 | call to baz_2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:496:1:502:3 | ExtendSingletonMethod | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:463:1:463:26 | ConditionalInstanceMethods | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:463:1:463:30 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:463:1:463:33 | call to m1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:464:1:464:26 | ConditionalInstanceMethods | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:464:1:464:30 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:464:1:464:33 | call to m3 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:465:1:465:26 | ConditionalInstanceMethods | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:465:1:465:30 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:465:1:465:33 | call to m2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:466:1:466:26 | ConditionalInstanceMethods | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:466:1:466:30 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:466:1:466:33 | call to m3 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:467:1:467:26 | ConditionalInstanceMethods | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:467:1:467:30 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:467:1:467:33 | call to m4 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:468:1:468:26 | ConditionalInstanceMethods | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:468:1:468:30 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:468:1:468:33 | call to m5 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:470:1:470:23 | EsotericInstanceMethods | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:470:1:488:3 | ... = ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:470:27:470:31 | Class | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:470:27:488:3 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:470:37:488:3 | do ... end | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:471:5:471:11 | Array | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:471:5:471:11 | [...] | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:471:5:471:11 | call to [] | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:471:5:475:7 | call to each | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:471:6:471:6 | 0 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:471:8:471:8 | 1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:471:10:471:10 | 2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:471:18:475:7 | do ... end | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:472:9:474:11 | foo | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:473:13:473:22 | call to puts | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:473:13:473:22 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:473:18:473:22 | "foo" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:473:19:473:21 | foo | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:477:5:477:9 | Class | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:477:5:481:7 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:477:5:481:11 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:477:5:481:15 | call to bar | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:477:15:481:7 | do ... end | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:478:9:480:11 | bar | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:479:13:479:22 | call to puts | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:479:13:479:22 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:479:18:479:22 | "bar" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:479:19:479:21 | bar | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:483:5:483:11 | Array | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:483:5:483:11 | [...] | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:483:5:483:11 | call to [] | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:483:5:487:7 | call to each | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:483:6:483:6 | 0 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:483:8:483:8 | 1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:483:10:483:10 | 2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:483:18:487:7 | do ... end | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:483:22:483:22 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:483:22:483:22 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:484:9:486:11 | call to define_method | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:484:9:486:11 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:484:23:484:32 | "baz_#{...}" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:484:24:484:27 | baz_ | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:484:28:484:31 | #{...} | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:484:30:484:30 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:484:35:486:11 | do ... end | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:485:13:485:27 | call to puts | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:485:13:485:27 | self | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:485:18:485:27 | "baz_#{...}" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:485:19:485:22 | baz_ | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:485:23:485:26 | #{...} | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:485:25:485:25 | i | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:490:1:490:23 | EsotericInstanceMethods | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:490:1:490:27 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:490:1:490:31 | call to foo | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:491:1:491:23 | EsotericInstanceMethods | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:491:1:491:27 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:491:1:491:31 | call to bar | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:492:1:492:23 | EsotericInstanceMethods | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:492:1:492:27 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:492:1:492:33 | call to baz_0 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:493:1:493:23 | EsotericInstanceMethods | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:493:1:493:27 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:493:1:493:33 | call to baz_1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:494:1:494:23 | EsotericInstanceMethods | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:494:1:494:27 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:494:1:494:33 | call to baz_2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:496:1:502:3 | ExtendSingletonMethod | calls.rb:1:1:632:1 | calls.rb | | calls.rb:497:5:499:7 | singleton | calls.rb:496:1:502:3 | ExtendSingletonMethod | | calls.rb:498:9:498:46 | call to puts | calls.rb:496:1:502:3 | ExtendSingletonMethod | | calls.rb:498:9:498:46 | self | calls.rb:496:1:502:3 | ExtendSingletonMethod | @@ -1331,32 +1340,32 @@ enclosingModule | calls.rb:501:5:501:15 | call to extend | calls.rb:496:1:502:3 | ExtendSingletonMethod | | calls.rb:501:5:501:15 | self | calls.rb:496:1:502:3 | ExtendSingletonMethod | | calls.rb:501:12:501:15 | self | calls.rb:496:1:502:3 | ExtendSingletonMethod | -| calls.rb:504:1:504:21 | ExtendSingletonMethod | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:504:1:504:31 | call to singleton | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:506:1:508:3 | ExtendSingletonMethod2 | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:504:1:504:21 | ExtendSingletonMethod | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:504:1:504:31 | call to singleton | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:506:1:508:3 | ExtendSingletonMethod2 | calls.rb:1:1:632:1 | calls.rb | | calls.rb:507:5:507:32 | call to extend | calls.rb:506:1:508:3 | ExtendSingletonMethod2 | | calls.rb:507:5:507:32 | self | calls.rb:506:1:508:3 | ExtendSingletonMethod2 | | calls.rb:507:12:507:32 | ExtendSingletonMethod | calls.rb:506:1:508:3 | ExtendSingletonMethod2 | -| calls.rb:510:1:510:22 | ExtendSingletonMethod2 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:510:1:510:32 | call to singleton | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:512:1:513:3 | ExtendSingletonMethod3 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:515:1:515:22 | ExtendSingletonMethod3 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:515:1:515:51 | call to extend | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:515:31:515:51 | ExtendSingletonMethod | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:517:1:517:22 | ExtendSingletonMethod3 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:517:1:517:32 | call to singleton | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:519:1:519:3 | foo | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:519:1:519:13 | ... = ... | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:519:7:519:13 | "hello" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:519:8:519:12 | hello | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:520:1:520:3 | foo | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:520:1:520:13 | call to singleton | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:521:1:521:3 | foo | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:521:1:521:32 | call to extend | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:521:12:521:32 | ExtendSingletonMethod | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:523:1:523:3 | foo | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:523:1:523:13 | call to singleton | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:525:1:529:3 | ProtectedMethodInModule | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:510:1:510:22 | ExtendSingletonMethod2 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:510:1:510:32 | call to singleton | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:512:1:513:3 | ExtendSingletonMethod3 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:515:1:515:22 | ExtendSingletonMethod3 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:515:1:515:51 | call to extend | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:515:31:515:51 | ExtendSingletonMethod | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:517:1:517:22 | ExtendSingletonMethod3 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:517:1:517:32 | call to singleton | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:519:1:519:3 | foo | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:519:1:519:13 | ... = ... | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:519:7:519:13 | "hello" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:519:8:519:12 | hello | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:520:1:520:3 | foo | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:520:1:520:13 | call to singleton | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:521:1:521:3 | foo | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:521:1:521:32 | call to extend | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:521:12:521:32 | ExtendSingletonMethod | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:523:1:523:3 | foo | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:523:1:523:13 | call to singleton | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:525:1:529:3 | ProtectedMethodInModule | calls.rb:1:1:632:1 | calls.rb | | calls.rb:526:5:528:7 | call to protected | calls.rb:525:1:529:3 | ProtectedMethodInModule | | calls.rb:526:5:528:7 | self | calls.rb:525:1:529:3 | ProtectedMethodInModule | | calls.rb:526:15:528:7 | foo | calls.rb:525:1:529:3 | ProtectedMethodInModule | @@ -1364,7 +1373,7 @@ enclosingModule | calls.rb:527:9:527:42 | self | calls.rb:525:1:529:3 | ProtectedMethodInModule | | calls.rb:527:14:527:42 | "ProtectedMethodInModule#foo" | calls.rb:525:1:529:3 | ProtectedMethodInModule | | calls.rb:527:15:527:41 | ProtectedMethodInModule#foo | calls.rb:525:1:529:3 | ProtectedMethodInModule | -| calls.rb:531:1:544:3 | ProtectedMethods | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:531:1:544:3 | ProtectedMethods | calls.rb:1:1:632:1 | calls.rb | | calls.rb:532:5:532:35 | call to include | calls.rb:531:1:544:3 | ProtectedMethods | | calls.rb:532:5:532:35 | self | calls.rb:531:1:544:3 | ProtectedMethods | | calls.rb:532:13:532:35 | ProtectedMethodInModule | calls.rb:531:1:544:3 | ProtectedMethods | @@ -1386,63 +1395,63 @@ enclosingModule | calls.rb:542:9:542:24 | ProtectedMethods | calls.rb:531:1:544:3 | ProtectedMethods | | calls.rb:542:9:542:28 | call to new | calls.rb:531:1:544:3 | ProtectedMethods | | calls.rb:542:9:542:32 | call to bar | calls.rb:531:1:544:3 | ProtectedMethods | -| calls.rb:546:1:546:16 | ProtectedMethods | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:546:1:546:20 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:546:1:546:24 | call to foo | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:547:1:547:16 | ProtectedMethods | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:547:1:547:20 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:547:1:547:24 | call to bar | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:548:1:548:16 | ProtectedMethods | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:548:1:548:20 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:548:1:548:24 | call to baz | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:550:1:555:3 | ProtectedMethodsSub | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:550:29:550:44 | ProtectedMethods | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:546:1:546:16 | ProtectedMethods | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:546:1:546:20 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:546:1:546:24 | call to foo | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:547:1:547:16 | ProtectedMethods | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:547:1:547:20 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:547:1:547:24 | call to bar | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:548:1:548:16 | ProtectedMethods | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:548:1:548:20 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:548:1:548:24 | call to baz | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:550:1:555:3 | ProtectedMethodsSub | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:550:29:550:44 | ProtectedMethods | calls.rb:1:1:632:1 | calls.rb | | calls.rb:551:5:554:7 | baz | calls.rb:550:1:555:3 | ProtectedMethodsSub | | calls.rb:552:9:552:11 | call to foo | calls.rb:550:1:555:3 | ProtectedMethodsSub | | calls.rb:552:9:552:11 | self | calls.rb:550:1:555:3 | ProtectedMethodsSub | | calls.rb:553:9:553:27 | ProtectedMethodsSub | calls.rb:550:1:555:3 | ProtectedMethodsSub | | calls.rb:553:9:553:31 | call to new | calls.rb:550:1:555:3 | ProtectedMethodsSub | | calls.rb:553:9:553:35 | call to foo | calls.rb:550:1:555:3 | ProtectedMethodsSub | -| calls.rb:557:1:557:19 | ProtectedMethodsSub | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:557:1:557:23 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:557:1:557:27 | call to foo | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:558:1:558:19 | ProtectedMethodsSub | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:558:1:558:23 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:558:1:558:27 | call to bar | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:559:1:559:19 | ProtectedMethodsSub | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:559:1:559:23 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:559:1:559:27 | call to baz | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:561:1:561:7 | Array | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:561:1:561:7 | [...] | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:561:1:561:7 | call to [] | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:561:1:561:26 | call to each | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:561:2:561:2 | C | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:561:2:561:6 | call to new | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:561:14:561:26 | { ... } | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:561:17:561:17 | c | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:561:17:561:17 | c | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:561:20:561:20 | c | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:561:20:561:24 | call to baz | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:562:1:562:13 | Array | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:562:1:562:13 | [...] | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:562:1:562:13 | call to [] | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:562:1:562:39 | call to each | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:562:2:562:4 | "a" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:562:3:562:3 | a | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:562:6:562:8 | "b" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:562:7:562:7 | b | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:562:10:562:12 | "c" | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:562:11:562:11 | c | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:562:20:562:39 | { ... } | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:562:23:562:23 | s | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:562:23:562:23 | s | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:562:26:562:26 | s | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:562:26:562:37 | call to capitalize | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:564:1:567:3 | SingletonUpCall_Base | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:557:1:557:19 | ProtectedMethodsSub | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:557:1:557:23 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:557:1:557:27 | call to foo | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:558:1:558:19 | ProtectedMethodsSub | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:558:1:558:23 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:558:1:558:27 | call to bar | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:559:1:559:19 | ProtectedMethodsSub | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:559:1:559:23 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:559:1:559:27 | call to baz | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:561:1:561:7 | Array | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:561:1:561:7 | [...] | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:561:1:561:7 | call to [] | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:561:1:561:26 | call to each | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:561:2:561:2 | C | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:561:2:561:6 | call to new | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:561:14:561:26 | { ... } | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:561:17:561:17 | c | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:561:17:561:17 | c | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:561:20:561:20 | c | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:561:20:561:24 | call to baz | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:562:1:562:13 | Array | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:562:1:562:13 | [...] | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:562:1:562:13 | call to [] | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:562:1:562:39 | call to each | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:562:2:562:4 | "a" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:562:3:562:3 | a | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:562:6:562:8 | "b" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:562:7:562:7 | b | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:562:10:562:12 | "c" | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:562:11:562:11 | c | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:562:20:562:39 | { ... } | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:562:23:562:23 | s | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:562:23:562:23 | s | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:562:26:562:26 | s | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:562:26:562:37 | call to capitalize | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:564:1:567:3 | SingletonUpCall_Base | calls.rb:1:1:632:1 | calls.rb | | calls.rb:565:5:566:7 | singleton | calls.rb:564:1:567:3 | SingletonUpCall_Base | | calls.rb:565:9:565:12 | self | calls.rb:564:1:567:3 | SingletonUpCall_Base | -| calls.rb:568:1:575:3 | SingletonUpCall_Sub | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:568:29:568:48 | SingletonUpCall_Base | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:568:1:575:3 | SingletonUpCall_Sub | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:568:29:568:48 | SingletonUpCall_Base | calls.rb:1:1:632:1 | calls.rb | | calls.rb:569:5:569:13 | call to singleton | calls.rb:568:1:575:3 | SingletonUpCall_Sub | | calls.rb:569:5:569:13 | self | calls.rb:568:1:575:3 | SingletonUpCall_Sub | | calls.rb:570:5:570:14 | call to singleton2 | calls.rb:568:1:575:3 | SingletonUpCall_Sub | @@ -1453,13 +1462,13 @@ enclosingModule | calls.rb:572:9:572:17 | self | calls.rb:568:1:575:3 | SingletonUpCall_Sub | | calls.rb:573:9:573:18 | call to singleton2 | calls.rb:568:1:575:3 | SingletonUpCall_Sub | | calls.rb:573:9:573:18 | self | calls.rb:568:1:575:3 | SingletonUpCall_Sub | -| calls.rb:576:1:581:3 | SingletonUpCall_SubSub | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:576:32:576:50 | SingletonUpCall_Sub | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:576:1:581:3 | SingletonUpCall_SubSub | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:576:32:576:50 | SingletonUpCall_Sub | calls.rb:1:1:632:1 | calls.rb | | calls.rb:577:5:578:7 | singleton2 | calls.rb:576:1:581:3 | SingletonUpCall_SubSub | | calls.rb:577:9:577:12 | self | calls.rb:576:1:581:3 | SingletonUpCall_SubSub | | calls.rb:580:5:580:14 | call to mid_method | calls.rb:576:1:581:3 | SingletonUpCall_SubSub | | calls.rb:580:5:580:14 | self | calls.rb:576:1:581:3 | SingletonUpCall_SubSub | -| calls.rb:583:1:594:3 | SingletonA | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:583:1:594:3 | SingletonA | calls.rb:1:1:632:1 | calls.rb | | calls.rb:584:5:585:7 | singleton1 | calls.rb:583:1:594:3 | SingletonA | | calls.rb:584:9:584:12 | self | calls.rb:583:1:594:3 | SingletonA | | calls.rb:587:5:589:7 | call_singleton1 | calls.rb:583:1:594:3 | SingletonA | @@ -1470,28 +1479,39 @@ enclosingModule | calls.rb:591:9:591:12 | self | calls.rb:583:1:594:3 | SingletonA | | calls.rb:592:9:592:23 | call to call_singleton1 | calls.rb:583:1:594:3 | SingletonA | | calls.rb:592:9:592:23 | self | calls.rb:583:1:594:3 | SingletonA | -| calls.rb:596:1:603:3 | SingletonB | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:596:20:596:29 | SingletonA | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:596:1:603:3 | SingletonB | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:596:20:596:29 | SingletonA | calls.rb:1:1:632:1 | calls.rb | | calls.rb:597:5:598:7 | singleton1 | calls.rb:596:1:603:3 | SingletonB | | calls.rb:597:9:597:12 | self | calls.rb:596:1:603:3 | SingletonB | | calls.rb:600:5:602:7 | call_singleton1 | calls.rb:596:1:603:3 | SingletonB | | calls.rb:600:9:600:12 | self | calls.rb:596:1:603:3 | SingletonB | | calls.rb:601:9:601:18 | call to singleton1 | calls.rb:596:1:603:3 | SingletonB | | calls.rb:601:9:601:18 | self | calls.rb:596:1:603:3 | SingletonB | -| calls.rb:605:1:612:3 | SingletonC | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:605:20:605:29 | SingletonA | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:605:1:612:3 | SingletonC | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:605:20:605:29 | SingletonA | calls.rb:1:1:632:1 | calls.rb | | calls.rb:606:5:607:7 | singleton1 | calls.rb:605:1:612:3 | SingletonC | | calls.rb:606:9:606:12 | self | calls.rb:605:1:612:3 | SingletonC | | calls.rb:609:5:611:7 | call_singleton1 | calls.rb:605:1:612:3 | SingletonC | | calls.rb:609:9:609:12 | self | calls.rb:605:1:612:3 | SingletonC | | calls.rb:610:9:610:18 | call to singleton1 | calls.rb:605:1:612:3 | SingletonC | | calls.rb:610:9:610:18 | self | calls.rb:605:1:612:3 | SingletonC | -| calls.rb:614:1:614:10 | SingletonA | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:614:1:614:31 | call to call_call_singleton1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:615:1:615:10 | SingletonB | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:615:1:615:31 | call to call_call_singleton1 | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:616:1:616:10 | SingletonC | calls.rb:1:1:616:32 | calls.rb | -| calls.rb:616:1:616:31 | call to call_call_singleton1 | calls.rb:1:1:616:32 | calls.rb | +| calls.rb:614:1:614:10 | SingletonA | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:614:1:614:31 | call to call_call_singleton1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:615:1:615:10 | SingletonB | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:615:1:615:31 | call to call_call_singleton1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:616:1:616:10 | SingletonC | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:616:1:616:31 | call to call_call_singleton1 | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:618:1:624:3 | Included | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:619:5:621:7 | foo | calls.rb:618:1:624:3 | Included | +| calls.rb:620:9:620:12 | self | calls.rb:618:1:624:3 | Included | +| calls.rb:620:9:620:16 | call to bar | calls.rb:618:1:624:3 | Included | +| calls.rb:622:5:623:7 | bar | calls.rb:618:1:624:3 | Included | +| calls.rb:626:1:631:3 | IncludesIncluded | calls.rb:1:1:632:1 | calls.rb | +| calls.rb:627:5:627:20 | call to include | calls.rb:626:1:631:3 | IncludesIncluded | +| calls.rb:627:5:627:20 | self | calls.rb:626:1:631:3 | IncludesIncluded | +| calls.rb:627:13:627:20 | Included | calls.rb:626:1:631:3 | IncludesIncluded | +| calls.rb:628:5:630:7 | bar | calls.rb:626:1:631:3 | IncludesIncluded | +| calls.rb:629:9:629:13 | call to super | calls.rb:626:1:631:3 | IncludesIncluded | | hello.rb:1:1:8:3 | EnglishWords | hello.rb:1:1:22:3 | hello.rb | | hello.rb:2:5:4:7 | hello | hello.rb:1:1:8:3 | EnglishWords | | hello.rb:3:9:3:22 | return | hello.rb:1:1:8:3 | EnglishWords | diff --git a/ruby/ql/test/library-tests/modules/superclasses.expected b/ruby/ql/test/library-tests/modules/superclasses.expected index 24907e7d81b..58df295f95f 100644 --- a/ruby/ql/test/library-tests/modules/superclasses.expected +++ b/ruby/ql/test/library-tests/modules/superclasses.expected @@ -131,6 +131,11 @@ calls.rb: # 605| SingletonC #-----| -> SingletonA +# 618| Included + +# 626| IncludesIncluded +#-----| -> Object + hello.rb: # 1| EnglishWords From 0dadf0bbb4254ddcaa41ddb9b780b553a21c1ad3 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Fri, 11 Nov 2022 17:16:24 +0000 Subject: [PATCH 0217/1420] Ruby: add flow summary for Enumerable#index_by --- .../codeql/ruby/frameworks/ActiveSupport.qll | 12 +++- .../ActiveSupportDataFlow.expected | 63 +++++++++++++++++++ .../active_support/hash_extensions.rb | 13 ++++ 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveSupport.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveSupport.qll index 47742531e30..7d3286a0a74 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveSupport.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveSupport.qll @@ -284,7 +284,17 @@ module ActiveSupport { preservesValue = true } } - // TODO: index_by, index_with, pick, pluck (they require Hash dataflow) + + private class IndexBySummary extends SimpleSummarizedCallable { + IndexBySummary() { this = "index_by" } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[self].Element[any]" and + output = ["Argument[block].Parameter[0]", "ReturnValue.Element[?]"] and + preservesValue = true + } + } + // TODO: index_with, pick, pluck (they require Hash dataflow) } } diff --git a/ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportDataFlow.expected b/ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportDataFlow.expected index 0df4f1235f8..11b6b46ee3e 100644 --- a/ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportDataFlow.expected +++ b/ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportDataFlow.expected @@ -258,6 +258,34 @@ edges | hash_extensions.rb:58:10:58:10 | x [element :a] : | hash_extensions.rb:58:10:58:14 | ...[...] | | hash_extensions.rb:59:10:59:10 | x [element :b] : | hash_extensions.rb:59:10:59:14 | ...[...] | | hash_extensions.rb:59:10:59:10 | x [element :b] : | hash_extensions.rb:59:10:59:14 | ...[...] | +| hash_extensions.rb:67:15:67:25 | call to source : | hash_extensions.rb:68:9:68:14 | values [element 0] : | +| hash_extensions.rb:67:15:67:25 | call to source : | hash_extensions.rb:68:9:68:14 | values [element 0] : | +| hash_extensions.rb:67:28:67:38 | call to source : | hash_extensions.rb:68:9:68:14 | values [element 1] : | +| hash_extensions.rb:67:28:67:38 | call to source : | hash_extensions.rb:68:9:68:14 | values [element 1] : | +| hash_extensions.rb:67:41:67:51 | call to source : | hash_extensions.rb:68:9:68:14 | values [element 2] : | +| hash_extensions.rb:67:41:67:51 | call to source : | hash_extensions.rb:68:9:68:14 | values [element 2] : | +| hash_extensions.rb:68:9:68:14 | values [element 0] : | hash_extensions.rb:68:9:71:7 | call to index_by [element] : | +| hash_extensions.rb:68:9:68:14 | values [element 0] : | hash_extensions.rb:68:9:71:7 | call to index_by [element] : | +| hash_extensions.rb:68:9:68:14 | values [element 0] : | hash_extensions.rb:68:29:68:33 | value : | +| hash_extensions.rb:68:9:68:14 | values [element 0] : | hash_extensions.rb:68:29:68:33 | value : | +| hash_extensions.rb:68:9:68:14 | values [element 1] : | hash_extensions.rb:68:9:71:7 | call to index_by [element] : | +| hash_extensions.rb:68:9:68:14 | values [element 1] : | hash_extensions.rb:68:9:71:7 | call to index_by [element] : | +| hash_extensions.rb:68:9:68:14 | values [element 1] : | hash_extensions.rb:68:29:68:33 | value : | +| hash_extensions.rb:68:9:68:14 | values [element 1] : | hash_extensions.rb:68:29:68:33 | value : | +| hash_extensions.rb:68:9:68:14 | values [element 2] : | hash_extensions.rb:68:9:71:7 | call to index_by [element] : | +| hash_extensions.rb:68:9:68:14 | values [element 2] : | hash_extensions.rb:68:9:71:7 | call to index_by [element] : | +| hash_extensions.rb:68:9:68:14 | values [element 2] : | hash_extensions.rb:68:29:68:33 | value : | +| hash_extensions.rb:68:9:68:14 | values [element 2] : | hash_extensions.rb:68:29:68:33 | value : | +| hash_extensions.rb:68:9:71:7 | call to index_by [element] : | hash_extensions.rb:73:10:73:10 | h [element] : | +| hash_extensions.rb:68:9:71:7 | call to index_by [element] : | hash_extensions.rb:73:10:73:10 | h [element] : | +| hash_extensions.rb:68:9:71:7 | call to index_by [element] : | hash_extensions.rb:74:10:74:10 | h [element] : | +| hash_extensions.rb:68:9:71:7 | call to index_by [element] : | hash_extensions.rb:74:10:74:10 | h [element] : | +| hash_extensions.rb:68:29:68:33 | value : | hash_extensions.rb:69:14:69:18 | value | +| hash_extensions.rb:68:29:68:33 | value : | hash_extensions.rb:69:14:69:18 | value | +| hash_extensions.rb:73:10:73:10 | h [element] : | hash_extensions.rb:73:10:73:16 | ...[...] | +| hash_extensions.rb:73:10:73:10 | h [element] : | hash_extensions.rb:73:10:73:16 | ...[...] | +| hash_extensions.rb:74:10:74:10 | h [element] : | hash_extensions.rb:74:10:74:16 | ...[...] | +| hash_extensions.rb:74:10:74:10 | h [element] : | hash_extensions.rb:74:10:74:16 | ...[...] | nodes | active_support.rb:10:9:10:18 | call to source : | semmle.label | call to source : | | active_support.rb:11:10:11:10 | x : | semmle.label | x : | @@ -594,6 +622,32 @@ nodes | hash_extensions.rb:59:10:59:10 | x [element :b] : | semmle.label | x [element :b] : | | hash_extensions.rb:59:10:59:14 | ...[...] | semmle.label | ...[...] | | hash_extensions.rb:59:10:59:14 | ...[...] | semmle.label | ...[...] | +| hash_extensions.rb:67:15:67:25 | call to source : | semmle.label | call to source : | +| hash_extensions.rb:67:15:67:25 | call to source : | semmle.label | call to source : | +| hash_extensions.rb:67:28:67:38 | call to source : | semmle.label | call to source : | +| hash_extensions.rb:67:28:67:38 | call to source : | semmle.label | call to source : | +| hash_extensions.rb:67:41:67:51 | call to source : | semmle.label | call to source : | +| hash_extensions.rb:67:41:67:51 | call to source : | semmle.label | call to source : | +| hash_extensions.rb:68:9:68:14 | values [element 0] : | semmle.label | values [element 0] : | +| hash_extensions.rb:68:9:68:14 | values [element 0] : | semmle.label | values [element 0] : | +| hash_extensions.rb:68:9:68:14 | values [element 1] : | semmle.label | values [element 1] : | +| hash_extensions.rb:68:9:68:14 | values [element 1] : | semmle.label | values [element 1] : | +| hash_extensions.rb:68:9:68:14 | values [element 2] : | semmle.label | values [element 2] : | +| hash_extensions.rb:68:9:68:14 | values [element 2] : | semmle.label | values [element 2] : | +| hash_extensions.rb:68:9:71:7 | call to index_by [element] : | semmle.label | call to index_by [element] : | +| hash_extensions.rb:68:9:71:7 | call to index_by [element] : | semmle.label | call to index_by [element] : | +| hash_extensions.rb:68:29:68:33 | value : | semmle.label | value : | +| hash_extensions.rb:68:29:68:33 | value : | semmle.label | value : | +| hash_extensions.rb:69:14:69:18 | value | semmle.label | value | +| hash_extensions.rb:69:14:69:18 | value | semmle.label | value | +| hash_extensions.rb:73:10:73:10 | h [element] : | semmle.label | h [element] : | +| hash_extensions.rb:73:10:73:10 | h [element] : | semmle.label | h [element] : | +| hash_extensions.rb:73:10:73:16 | ...[...] | semmle.label | ...[...] | +| hash_extensions.rb:73:10:73:16 | ...[...] | semmle.label | ...[...] | +| hash_extensions.rb:74:10:74:10 | h [element] : | semmle.label | h [element] : | +| hash_extensions.rb:74:10:74:10 | h [element] : | semmle.label | h [element] : | +| hash_extensions.rb:74:10:74:16 | ...[...] | semmle.label | ...[...] | +| hash_extensions.rb:74:10:74:16 | ...[...] | semmle.label | ...[...] | subpaths #select | active_support.rb:182:10:182:13 | ...[...] | active_support.rb:180:10:180:17 | call to source : | active_support.rb:182:10:182:13 | ...[...] | $@ | active_support.rb:180:10:180:17 | call to source : | call to source : | @@ -622,3 +676,12 @@ subpaths | hash_extensions.rb:56:10:56:14 | ...[...] | hash_extensions.rb:50:52:50:61 | call to taint : | hash_extensions.rb:56:10:56:14 | ...[...] | $@ | hash_extensions.rb:50:52:50:61 | call to taint : | call to taint : | | hash_extensions.rb:58:10:58:14 | ...[...] | hash_extensions.rb:50:14:50:23 | call to taint : | hash_extensions.rb:58:10:58:14 | ...[...] | $@ | hash_extensions.rb:50:14:50:23 | call to taint : | call to taint : | | hash_extensions.rb:59:10:59:14 | ...[...] | hash_extensions.rb:50:29:50:38 | call to taint : | hash_extensions.rb:59:10:59:14 | ...[...] | $@ | hash_extensions.rb:50:29:50:38 | call to taint : | call to taint : | +| hash_extensions.rb:69:14:69:18 | value | hash_extensions.rb:67:15:67:25 | call to source : | hash_extensions.rb:69:14:69:18 | value | $@ | hash_extensions.rb:67:15:67:25 | call to source : | call to source : | +| hash_extensions.rb:69:14:69:18 | value | hash_extensions.rb:67:28:67:38 | call to source : | hash_extensions.rb:69:14:69:18 | value | $@ | hash_extensions.rb:67:28:67:38 | call to source : | call to source : | +| hash_extensions.rb:69:14:69:18 | value | hash_extensions.rb:67:41:67:51 | call to source : | hash_extensions.rb:69:14:69:18 | value | $@ | hash_extensions.rb:67:41:67:51 | call to source : | call to source : | +| hash_extensions.rb:73:10:73:16 | ...[...] | hash_extensions.rb:67:15:67:25 | call to source : | hash_extensions.rb:73:10:73:16 | ...[...] | $@ | hash_extensions.rb:67:15:67:25 | call to source : | call to source : | +| hash_extensions.rb:73:10:73:16 | ...[...] | hash_extensions.rb:67:28:67:38 | call to source : | hash_extensions.rb:73:10:73:16 | ...[...] | $@ | hash_extensions.rb:67:28:67:38 | call to source : | call to source : | +| hash_extensions.rb:73:10:73:16 | ...[...] | hash_extensions.rb:67:41:67:51 | call to source : | hash_extensions.rb:73:10:73:16 | ...[...] | $@ | hash_extensions.rb:67:41:67:51 | call to source : | call to source : | +| hash_extensions.rb:74:10:74:16 | ...[...] | hash_extensions.rb:67:15:67:25 | call to source : | hash_extensions.rb:74:10:74:16 | ...[...] | $@ | hash_extensions.rb:67:15:67:25 | call to source : | call to source : | +| hash_extensions.rb:74:10:74:16 | ...[...] | hash_extensions.rb:67:28:67:38 | call to source : | hash_extensions.rb:74:10:74:16 | ...[...] | $@ | hash_extensions.rb:67:28:67:38 | call to source : | call to source : | +| hash_extensions.rb:74:10:74:16 | ...[...] | hash_extensions.rb:67:41:67:51 | call to source : | hash_extensions.rb:74:10:74:16 | ...[...] | $@ | hash_extensions.rb:67:41:67:51 | call to source : | call to source : | diff --git a/ruby/ql/test/library-tests/frameworks/active_support/hash_extensions.rb b/ruby/ql/test/library-tests/frameworks/active_support/hash_extensions.rb index 6e477f17aa8..6669cd83c37 100644 --- a/ruby/ql/test/library-tests/frameworks/active_support/hash_extensions.rb +++ b/ruby/ql/test/library-tests/frameworks/active_support/hash_extensions.rb @@ -62,3 +62,16 @@ def m_extract!(x) end m_extract!(:c) + +def m_index_by + values = [source("a"), source("b"), source("c")] + h = values.index_by do |value| + sink value # $ hasValueFlow=a $ hasValueFlow=b $ hasValueFlow=c + make_key(value) + end + + sink h[:foo] # $ hasValueFlow=a $ hasValueFlow=b $ hasValueFlow=c + sink h[:bar] # $ hasValueFlow=a $ hasValueFlow=b $ hasValueFlow=c +end + +m_index_by() From 16ba5b1bb5602b8deeb99b15be076c4f3c890f16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Mon, 14 Nov 2022 12:30:16 +0100 Subject: [PATCH 0218/1420] Swift: update doctests --- swift/ql/src/queries/Security/CWE-094/UnsafeJsEvalBad.swift | 2 +- swift/ql/src/queries/Security/CWE-094/UnsafeJsEvalGood.swift | 2 +- swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.swift | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-094/UnsafeJsEvalBad.swift b/swift/ql/src/queries/Security/CWE-094/UnsafeJsEvalBad.swift index 2e5b0233e79..111f8100ee2 100644 --- a/swift/ql/src/queries/Security/CWE-094/UnsafeJsEvalBad.swift +++ b/swift/ql/src/queries/Security/CWE-094/UnsafeJsEvalBad.swift @@ -3,4 +3,4 @@ let remoteData = try String(contentsOf: URL(string: "http://example.com/evil.jso ... -_ = try await webview.evaluateJavaScript("alert(" + remoteData + ")") // BAD +_ = try await webview.evaluateJavaScript("console.log(" + remoteData + ")") // BAD diff --git a/swift/ql/src/queries/Security/CWE-094/UnsafeJsEvalGood.swift b/swift/ql/src/queries/Security/CWE-094/UnsafeJsEvalGood.swift index a51ffd60b63..ea8f6c0d5ee 100644 --- a/swift/ql/src/queries/Security/CWE-094/UnsafeJsEvalGood.swift +++ b/swift/ql/src/queries/Security/CWE-094/UnsafeJsEvalGood.swift @@ -4,7 +4,7 @@ let remoteData = try String(contentsOf: URL(string: "http://example.com/evil.jso ... _ = try await webview.callAsyncJavaScript( - "alert(JSON.parse(data))", + "console.log(data)", arguments: ["data": remoteData], // GOOD contentWorld: .page ) diff --git a/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.swift b/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.swift index 36cee943b66..e65c182a4c3 100644 --- a/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.swift +++ b/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.swift @@ -317,10 +317,10 @@ func testQHelpExamples() { let webview = WKWebView() let remoteData = try String(contentsOf: URL(string: "http://example.com/evil.json")!) - _ = try await webview.evaluateJavaScript("alert(" + remoteData + ")") // BAD [NOT DETECTED - TODO: extract Callables of @MainActor method calls] + _ = try await webview.evaluateJavaScript("console.log(" + remoteData + ")") // BAD [NOT DETECTED - TODO: extract Callables of @MainActor method calls] _ = try await webview.callAsyncJavaScript( - "alert(JSON.parse(data))", + "console.log(data)", arguments: ["data": remoteData], // GOOD contentWorld: .page ) From 87ee979a122f6ae22afb5c7a696ae6b5a77ed312 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Mon, 14 Nov 2022 11:31:37 +0000 Subject: [PATCH 0219/1420] Java/Kotlin: Add compilation info to telemetry This will give info about which kotlinc versions are used. --- java/ql/src/Telemetry/ExtractorInformation.ql | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/java/ql/src/Telemetry/ExtractorInformation.ql b/java/ql/src/Telemetry/ExtractorInformation.ql index 0eb420ba651..00ce79796b2 100644 --- a/java/ql/src/Telemetry/ExtractorInformation.ql +++ b/java/ql/src/Telemetry/ExtractorInformation.ql @@ -9,6 +9,13 @@ import java import semmle.code.java.Diagnostics +predicate compilationInfo(string key, int value) { + exists(Compilation c, string infoKey | + key = infoKey + ": " + c.getInfo(infoKey) and + value = 1 + ) +} + predicate fileCount(string key, int value) { key = "Number of files" and value = strictcount(File f) @@ -55,6 +62,7 @@ predicate extractorDiagnostics(string key, int value) { from string key, int value where + compilationInfo(key, value) or fileCount(key, value) or fileCountByExtension(key, value) or totalNumberOfLines(key, value) or From c03eab2410cfcd446dd6a0238e19c29cbc940a9d Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Fri, 4 Nov 2022 12:31:59 +0100 Subject: [PATCH 0220/1420] Add XMLDocument sinks --- swift/ql/lib/codeql/swift/security/XXE.qll | 27 +++++- .../Security/CWE-611/testXMLDocumentXXE.swift | 86 +++++++++++++++++++ .../{testXXE.swift => testXMLParserXXE.swift} | 0 3 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 swift/ql/test/query-tests/Security/CWE-611/testXMLDocumentXXE.swift rename swift/ql/test/query-tests/Security/CWE-611/{testXXE.swift => testXMLParserXXE.swift} (100%) diff --git a/swift/ql/lib/codeql/swift/security/XXE.qll b/swift/ql/lib/codeql/swift/security/XXE.qll index 506225f54a1..da8513be645 100644 --- a/swift/ql/lib/codeql/swift/security/XXE.qll +++ b/swift/ql/lib/codeql/swift/security/XXE.qll @@ -20,8 +20,8 @@ class XxeAdditionalTaintStep extends Unit { } /** The XML argument of a `XMLParser` vulnerable to XXE. */ -private class DefaultXxeSink extends XxeSink { - DefaultXxeSink() { +private class XmlParserXxeSink extends XxeSink { + XmlParserXxeSink() { this.asExpr() = any(Argument a | a.getApplyExpr() instanceof VulnerableParser).getExpr() } } @@ -67,3 +67,26 @@ private class XmlParserRef extends Expr { private class XmlParserType extends NominalType { XmlParserType() { this.getFullName() = "XMLParser" } } + +/** The XML argument of a `XMLDocument` vulnerable to XXE. */ +private class XmlDocumentXxeSink extends XxeSink { + XmlDocumentXxeSink() { this.asExpr() = any(VulnerableXmlDocument d).getArgument(0).getExpr() } +} + +/** An `XMLDocument` that sets `nodeLoadExternalEntitiesAlways` in its options. */ +private class VulnerableXmlDocument extends ApplyExpr { + VulnerableXmlDocument() { + this.getStaticTarget().(ConstructorDecl).getEnclosingDecl().(NominalTypeDecl).getFullName() = + "XMLDocument" and + this.getArgument(1).getExpr().(ArrayExpr).getAnElement().(MemberRefExpr).getMember() instanceof + NodeLoadExternalEntitiesAlways + } +} + +/** The option `XMLNode.Options.nodeLoadExternalEntitiesAlways`. */ +private class NodeLoadExternalEntitiesAlways extends VarDecl { + NodeLoadExternalEntitiesAlways() { + this.getName() = "nodeLoadExternalEntitiesAlways" and + this.getEnclosingDecl().(StructDecl).getFullName() = "XMLNode.Options" + } +} diff --git a/swift/ql/test/query-tests/Security/CWE-611/testXMLDocumentXXE.swift b/swift/ql/test/query-tests/Security/CWE-611/testXMLDocumentXXE.swift new file mode 100644 index 00000000000..cbfd2cb58ea --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-611/testXMLDocumentXXE.swift @@ -0,0 +1,86 @@ +// --- stubs --- + +class Data { + init(_ elements: S) {} +} + +struct URL { + init?(string: String) {} +} + +extension String { + init(contentsOf: URL) { + let data = "" + self.init(data) + } +} + +class XMLNode { + struct Options : OptionSet { + let rawValue: Int + static let nodeLoadExternalEntitiesAlways = XMLNode.Options(rawValue: 1 << 0) + static let nodeLoadExternalEntitiesNever = XMLNode.Options(rawValue: 1 << 1) + } +} + +class XMLElement {} + +class XMLDocument { + init(contentsOf: URL, options: XMLNode.Options = []) {} + init(data: Data, options: XMLNode.Options = []) {} + init(rootElement: XMLElement?) {} + init(xmlString: String, options: XMLNode.Options = []) {} +} + +// --- tests --- + +func testUrl() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteUrl = URL(string: remoteString)! + let _ = XMLDocument(contentsOf: remoteUrl, options: [.nodeLoadExternalEntitiesAlways]) // $ hasXXE=39 +} + +func testUrlSafeImplicit() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteUrl = URL(string: remoteString)! + let _ = XMLDocument(contentsOf: remoteUrl, options: []) // NO XXE: document doesn't enable external entities +} + +func testUrlSafeExplicit() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteUrl = URL(string: remoteString)! + let _ = XMLDocument(contentsOf: remoteUrl, options: [.nodeLoadExternalEntitiesNever]) // NO XXE: document disables external entities +} + +func testData() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteData = Data(remoteString) + let _ = XMLDocument(data: remoteData, options: [.nodeLoadExternalEntitiesAlways]) // $ hasXXE=57 +} + +func testDataSafeImplicit() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteData = Data(remoteString) + let _ = XMLDocument(data: remoteData, options: []) // NO XXE: document doesn't enable external entities +} + +func testDataSafeExplicit() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteData = Data(remoteString) + let _ = XMLDocument(data: remoteData, options: [.nodeLoadExternalEntitiesNever]) // NO XXE: document disables external entities +} + +func testString() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let _ = XMLDocument(xmlString: remoteString, options: [.nodeLoadExternalEntitiesAlways]) // $ hasXXE=75 +} + +func testStringSafeImplicit() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let _ = XMLDocument(xmlString: remoteString, options: []) // NO XXE: document doesn't enable external entities +} + +func testStringSafeExplicit() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let _ = XMLDocument(xmlString: remoteString, options: [.nodeLoadExternalEntitiesNever]) // NO XXE: document disables external entities +} diff --git a/swift/ql/test/query-tests/Security/CWE-611/testXXE.swift b/swift/ql/test/query-tests/Security/CWE-611/testXMLParserXXE.swift similarity index 100% rename from swift/ql/test/query-tests/Security/CWE-611/testXXE.swift rename to swift/ql/test/query-tests/Security/CWE-611/testXMLParserXXE.swift From 52bd14021382cedb2e3ca98b79f73bb4276ca357 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Fri, 4 Nov 2022 13:02:02 +0100 Subject: [PATCH 0221/1420] Fix test expectations --- .../query-tests/Security/CWE-611/testXMLDocumentXXE.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/swift/ql/test/query-tests/Security/CWE-611/testXMLDocumentXXE.swift b/swift/ql/test/query-tests/Security/CWE-611/testXMLDocumentXXE.swift index cbfd2cb58ea..07180301e72 100644 --- a/swift/ql/test/query-tests/Security/CWE-611/testXMLDocumentXXE.swift +++ b/swift/ql/test/query-tests/Security/CWE-611/testXMLDocumentXXE.swift @@ -37,7 +37,7 @@ class XMLDocument { func testUrl() { let remoteString = String(contentsOf: URL(string: "http://example.com/")!) let remoteUrl = URL(string: remoteString)! - let _ = XMLDocument(contentsOf: remoteUrl, options: [.nodeLoadExternalEntitiesAlways]) // $ hasXXE=39 + let _ = XMLDocument(contentsOf: remoteUrl, options: [.nodeLoadExternalEntitiesAlways]) // $ hasXXE=38 } func testUrlSafeImplicit() { @@ -55,7 +55,7 @@ func testUrlSafeExplicit() { func testData() { let remoteString = String(contentsOf: URL(string: "http://example.com/")!) let remoteData = Data(remoteString) - let _ = XMLDocument(data: remoteData, options: [.nodeLoadExternalEntitiesAlways]) // $ hasXXE=57 + let _ = XMLDocument(data: remoteData, options: [.nodeLoadExternalEntitiesAlways]) // $ hasXXE=56 } func testDataSafeImplicit() { @@ -72,7 +72,7 @@ func testDataSafeExplicit() { func testString() { let remoteString = String(contentsOf: URL(string: "http://example.com/")!) - let _ = XMLDocument(xmlString: remoteString, options: [.nodeLoadExternalEntitiesAlways]) // $ hasXXE=75 + let _ = XMLDocument(xmlString: remoteString, options: [.nodeLoadExternalEntitiesAlways]) // $ hasXXE=74 } func testStringSafeImplicit() { From b39e2ef71cbee9fcae4dc97b5afe58566702ddce Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Fri, 29 Jul 2022 17:27:16 +0100 Subject: [PATCH 0222/1420] Ruby: add stacktrace exposure query --- .../StackTraceExposureCustomizations.qll | 55 +++++++++++++++++++ .../ruby/security/StackTraceExposureQuery.qll | 25 +++++++++ .../security/cwe-209/StackTraceExposure.qhelp | 49 +++++++++++++++++ .../security/cwe-209/StackTraceExposure.ql | 23 ++++++++ .../cwe-209/examples/StackTraceExposure.rb | 18 ++++++ .../cwe-209/StackTraceExposure.expected | 10 ++++ .../security/cwe-209/StackTraceExposure.qlref | 1 + .../security/cwe-209/StackTraceExposure.rb | 15 +++++ 8 files changed, 196 insertions(+) create mode 100644 ruby/ql/lib/codeql/ruby/security/StackTraceExposureCustomizations.qll create mode 100644 ruby/ql/lib/codeql/ruby/security/StackTraceExposureQuery.qll create mode 100644 ruby/ql/src/queries/security/cwe-209/StackTraceExposure.qhelp create mode 100644 ruby/ql/src/queries/security/cwe-209/StackTraceExposure.ql create mode 100644 ruby/ql/src/queries/security/cwe-209/examples/StackTraceExposure.rb create mode 100644 ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.expected create mode 100644 ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.qlref create mode 100644 ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.rb diff --git a/ruby/ql/lib/codeql/ruby/security/StackTraceExposureCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/StackTraceExposureCustomizations.qll new file mode 100644 index 00000000000..9a25dda844d --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/StackTraceExposureCustomizations.qll @@ -0,0 +1,55 @@ +/** + * Provides default sources, sinks and sanitizers for detecting stack trace + * exposure vulnerabilities, as well as extension points for adding your own. + */ + +private import codeql.ruby.AST +private import codeql.ruby.Concepts +private import codeql.ruby.DataFlow +private import codeql.ruby.controlflow.CfgNodes +private import codeql.ruby.frameworks.core.Kernel + +/** + * Provides default sources, sinks and sanitizers for detecting stack trace + * exposure vulnerabilities, as well as extension points for adding your own. + */ +module StackTraceExposure { + /** A data flow source for stack trace exposure vulnerabilities. */ + abstract class Source extends DataFlow::Node { } + + /** A data flow sink for stack trace exposure vulnerabilities. */ + abstract class Sink extends DataFlow::Node { } + + /** A data flow sanitizer for stack trace exposure vulnerabilities. */ + abstract class Sanitizer extends DataFlow::Node { } + + /** + * A call to `backtrace` or `backtrace_locations` on a `rescue` variable, + * considered as a flow source. + */ + class BacktraceCall extends Source, DataFlow::CallNode { + BacktraceCall() { + exists(DataFlow::LocalSourceNode varAccess | + varAccess.asExpr().(ExprNodes::VariableReadAccessCfgNode).getExpr().getVariable() = + any(RescueClause rc).getVariableExpr().(VariableAccess).getVariable() and + varAccess.flowsTo(this.getReceiver()) + ) and + this.getMethodName() = ["backtrace", "backtrace_locations"] + } + } + + /** + * A call to `Kernel#caller`, considered as a flow source. + */ + class KernelCallerCall extends Source, Kernel::KernelMethodCall { + KernelCallerCall() { this.getMethodName() = "caller" } + } + + /** + * The body of an HTTP response that will be returned from a server, + * considered as a flow sink. + */ + class ServerHttpResponseBodyAsSink extends Sink { + ServerHttpResponseBodyAsSink() { this = any(Http::Server::HttpResponse response).getBody() } + } +} diff --git a/ruby/ql/lib/codeql/ruby/security/StackTraceExposureQuery.qll b/ruby/ql/lib/codeql/ruby/security/StackTraceExposureQuery.qll new file mode 100644 index 00000000000..408d903d7b4 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/StackTraceExposureQuery.qll @@ -0,0 +1,25 @@ +/** + * Provides a taint-tracking configuration for detecting stack-trace exposure + * vulnerabilities. + * + * Note, for performance reasons: only import this file if + * `StackTraceExposure::Configuration` is needed; otherwise, + * `StackTraceExposureCustomizations` should be imported instead. + */ + +private import codeql.ruby.DataFlow +private import codeql.ruby.TaintTracking +private import StackTraceExposureCustomizations::StackTraceExposure + +/** + * A taint-tracking configuration for detecting "stack trace exposure" vulnerabilities. + */ +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "StackTraceExposure" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer } +} diff --git a/ruby/ql/src/queries/security/cwe-209/StackTraceExposure.qhelp b/ruby/ql/src/queries/security/cwe-209/StackTraceExposure.qhelp new file mode 100644 index 00000000000..916f6cfcbeb --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-209/StackTraceExposure.qhelp @@ -0,0 +1,49 @@ + + + + +

    +Software developers often add stack traces to error messages, as a debugging +aid. Whenever that error message occurs for an end user, the developer can use +the stack trace to help identify how to fix the problem. In particular, stack +traces can tell the developer more about the sequence of events that led to a +failure, as opposed to merely the final state of the software when the error +occurred. +

    + +

    +Unfortunately, the same information can be useful to an attacker. The sequence +of class or method names in a stack trace can reveal the structure of the +application as well as any internal components it relies on. Furthermore, the +error message at the top of a stack trace can include information such as +server-side file names and SQL code that the application relies on, allowing an +attacker to fine-tune a subsequent injection attack. +

    +
    + + +

    +Send the user a more generic error message that reveals less information. +Either suppress the stack trace entirely, or log it only on the server. +

    +
    + + +

    +In the following example, an exception is handled in two different ways. In the +first version, labeled BAD, the exception is exposted to the remote user by +rendering it as an HTTP response. As such, the user is able to see a detailed +stack trace, which may contain sensitive information. In the second version, the +error message is logged only on the server, and a generic error message is +displayed to the user. That way, the developers can still access and use the +error log, but remote users will not see the information.

    + + +
    + + +
  • OWASP: Improper Error Handling.
  • +
    + diff --git a/ruby/ql/src/queries/security/cwe-209/StackTraceExposure.ql b/ruby/ql/src/queries/security/cwe-209/StackTraceExposure.ql new file mode 100644 index 00000000000..95ea4a86908 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-209/StackTraceExposure.ql @@ -0,0 +1,23 @@ +/** + * @name Information exposure through an exception + * @description Leaking information about an exception, such as messages and stack traces, to an + * external user can expose implementation details that are useful to an attacker for + * developing a subsequent exploit. + * @kind path-problem + * @problem.severity error + * @security-severity 5.4 + * @precision high + * @id rb/stack-trace-exposure + * @tags security + * external/cwe/cwe-209 + * external/cwe/cwe-497 + */ + +import codeql.ruby.DataFlow +import codeql.ruby.security.StackTraceExposureQuery +import DataFlow::PathGraph + +from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ can be exposed to an external user.", source.getNode(), + "Error information" diff --git a/ruby/ql/src/queries/security/cwe-209/examples/StackTraceExposure.rb b/ruby/ql/src/queries/security/cwe-209/examples/StackTraceExposure.rb new file mode 100644 index 00000000000..591be56014f --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-209/examples/StackTraceExposure.rb @@ -0,0 +1,18 @@ +class UsersController < ApplicationController + + def update_bad(id) + do_computation() + rescue => e + # BAD + render e.backtrace, content_type: "text/plain" + end + + def update_good(id) + do_computation() + rescue => e + # GOOD + log e.backtrace + redner "Computation failed", content_type: "text/plain" + end + +end \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.expected b/ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.expected new file mode 100644 index 00000000000..7a0e35973be --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.expected @@ -0,0 +1,10 @@ +edges +| StackTraceExposure.rb:11:10:11:17 | call to caller : | StackTraceExposure.rb:12:12:12:13 | bt | +nodes +| StackTraceExposure.rb:6:12:6:22 | call to backtrace | semmle.label | call to backtrace | +| StackTraceExposure.rb:11:10:11:17 | call to caller : | semmle.label | call to caller : | +| StackTraceExposure.rb:12:12:12:13 | bt | semmle.label | bt | +subpaths +#select +| StackTraceExposure.rb:6:12:6:22 | call to backtrace | StackTraceExposure.rb:6:12:6:22 | call to backtrace | StackTraceExposure.rb:6:12:6:22 | call to backtrace | $@ can be exposed to an external user. | StackTraceExposure.rb:6:12:6:22 | call to backtrace | Error information | +| StackTraceExposure.rb:12:12:12:13 | bt | StackTraceExposure.rb:11:10:11:17 | call to caller : | StackTraceExposure.rb:12:12:12:13 | bt | $@ can be exposed to an external user. | StackTraceExposure.rb:11:10:11:17 | call to caller | Error information | diff --git a/ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.qlref b/ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.qlref new file mode 100644 index 00000000000..c110f2b1765 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.qlref @@ -0,0 +1 @@ +queries/security/cwe-209/StackTraceExposure.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.rb b/ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.rb new file mode 100644 index 00000000000..a9faa66af02 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-209/StackTraceExposure.rb @@ -0,0 +1,15 @@ +class FooController < ApplicationController + + def show + something_that_might_fail() + rescue => e + render e.backtrace, content_type: "text/plain" + end + + + def show2 + bt = caller() + render bt, content_type: "text/plain" + end + +end From c660ea100b422fb05ca733dbc1b832edd06ba58c Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Mon, 14 Nov 2022 12:08:56 +0000 Subject: [PATCH 0223/1420] Ruby: add changenote for rb/stack-trace-exposure --- ruby/ql/src/change-notes/2022-11-14-stack-trace-exposure.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 ruby/ql/src/change-notes/2022-11-14-stack-trace-exposure.md diff --git a/ruby/ql/src/change-notes/2022-11-14-stack-trace-exposure.md b/ruby/ql/src/change-notes/2022-11-14-stack-trace-exposure.md new file mode 100644 index 00000000000..c4b1e73766b --- /dev/null +++ b/ruby/ql/src/change-notes/2022-11-14-stack-trace-exposure.md @@ -0,0 +1,4 @@ +--- +category: newQuery +--- +* Added a new query, `rb/stack-trace-exposure`, to detect exposure of stack-traces to users via HTTP responses. From b20f8fc8c9efe88282e542187aebe00c84d78e58 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Mon, 14 Nov 2022 12:27:54 +0000 Subject: [PATCH 0224/1420] Kotlin: Add total number of diagnostics to telemetry --- .../src/main/kotlin/utils/Logger.kt | 11 +++++-- java/ql/src/Telemetry/ExtractorInformation.ql | 31 ++++++++++++++++++- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt b/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt index 43c6ee4cc10..6ec568e769a 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt @@ -138,6 +138,10 @@ open class LoggerBase(val logCounter: LogCounter) { fullMsgBuilder.append(suffix) val fullMsg = fullMsgBuilder.toString() + emitDiagnostic(tw, severity, diagnosticLocStr, msg, fullMsg, locationString, mkLocationId) + } + + fun emitDiagnostic(tw: TrapWriter, severity: Severity, diagnosticLocStr: String, msg: String, fullMsg: String, locationString: String? = null, mkLocationId: () -> Label = { tw.unknownLocation }) { val locStr = if (locationString == null) "" else "At " + locationString + ": " val kind = if (severity <= Severity.WarnHigh) "WARN" else "ERROR" val logMessage = LogMessage(kind, "Diagnostic($diagnosticLocStr): $locStr$fullMsg") @@ -190,9 +194,10 @@ open class LoggerBase(val logCounter: LogCounter) { // We don't know if this location relates to an error // or a warning, so we just declare hitting the limit // to be an error regardless. - val logMessage = LogMessage("ERROR", "Total of $count diagnostics from $caller.") - tw.writeComment(logMessage.toText()) - logStream.write(logMessage.toJsonLine()) + val message = "Total of $count diagnostics (reached limit of ${logCounter.diagnosticLimit}) from $caller." + if (verbosity >= 1) { + emitDiagnostic(tw, Severity.Error, "Limit", message, message) + } } } } diff --git a/java/ql/src/Telemetry/ExtractorInformation.ql b/java/ql/src/Telemetry/ExtractorInformation.ql index 0eb420ba651..861cde5c661 100644 --- a/java/ql/src/Telemetry/ExtractorInformation.ql +++ b/java/ql/src/Telemetry/ExtractorInformation.ql @@ -53,6 +53,34 @@ predicate extractorDiagnostics(string key, int value) { ) } +/* + * Just counting the diagnostics doesn't give the full picture, as + * CODEQL_EXTRACTOR_KOTLIN_DIAGNOSTIC_LIMIT means that some diagnostics + * will be suppressed. In that case, we need to look for the + * suppression message, uncount those that did get emitted, uncount the + * suppression message itself, and then add on the full count. + */ + +predicate extractorTotalDiagnostics(string key, int value) { + exists(string extractor | + key = "Total number of diagnostics from " + extractor and + value = + strictcount(Diagnostic d | d.getGeneratedBy() = extractor) + + sum(Diagnostic d | + d.getGeneratedBy() = extractor + | + d.getMessage() + .regexpCapture("Total of ([0-9]+) diagnostics \\(reached limit of ([0-9]+)\\).*", + 1) + .toInt() - + d.getMessage() + .regexpCapture("Total of ([0-9]+) diagnostics \\(reached limit of ([0-9]+)\\).*", + 2) + .toInt() - 1 + ) + ) +} + from string key, int value where fileCount(key, value) or @@ -61,5 +89,6 @@ where numberOfLinesOfCode(key, value) or totalNumberOfLinesByExtension(key, value) or numberOfLinesOfCodeByExtension(key, value) or - extractorDiagnostics(key, value) + extractorDiagnostics(key, value) or + extractorTotalDiagnostics(key, value) select key, value From c80fbff648e9c33fd44c94b4bcf19bb55ec4fbc0 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Mon, 14 Nov 2022 12:47:50 +0000 Subject: [PATCH 0225/1420] Ruby: add changenote for Enumerable#index_by flow summary --- .../2022-11-14-activesupport-enumerable-index-by.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 ruby/ql/lib/change-notes/2022-11-14-activesupport-enumerable-index-by.md diff --git a/ruby/ql/lib/change-notes/2022-11-14-activesupport-enumerable-index-by.md b/ruby/ql/lib/change-notes/2022-11-14-activesupport-enumerable-index-by.md new file mode 100644 index 00000000000..812c292dd94 --- /dev/null +++ b/ruby/ql/lib/change-notes/2022-11-14-activesupport-enumerable-index-by.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Data flow through the `ActiveSupport` extension `Enumerable#index_by` is now modeled. From 847ecd1eec06213714ef9907fdc461f333e77e59 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Mon, 14 Nov 2022 13:09:49 +0000 Subject: [PATCH 0226/1420] Java/Kotlin: Small refactoring of ExtractorInformation --- java/ql/src/Telemetry/ExtractorInformation.ql | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/java/ql/src/Telemetry/ExtractorInformation.ql b/java/ql/src/Telemetry/ExtractorInformation.ql index 861cde5c661..eaf0eac1fa9 100644 --- a/java/ql/src/Telemetry/ExtractorInformation.ql +++ b/java/ql/src/Telemetry/ExtractorInformation.ql @@ -62,21 +62,16 @@ predicate extractorDiagnostics(string key, int value) { */ predicate extractorTotalDiagnostics(string key, int value) { - exists(string extractor | + exists(string extractor, string limitRegex | + limitRegex = "Total of ([0-9]+) diagnostics \\(reached limit of ([0-9]+)\\).*" and key = "Total number of diagnostics from " + extractor and value = strictcount(Diagnostic d | d.getGeneratedBy() = extractor) + sum(Diagnostic d | d.getGeneratedBy() = extractor | - d.getMessage() - .regexpCapture("Total of ([0-9]+) diagnostics \\(reached limit of ([0-9]+)\\).*", - 1) - .toInt() - - d.getMessage() - .regexpCapture("Total of ([0-9]+) diagnostics \\(reached limit of ([0-9]+)\\).*", - 2) - .toInt() - 1 + d.getMessage().regexpCapture(limitRegex, 1).toInt() - + d.getMessage().regexpCapture(limitRegex, 2).toInt() - 1 ) ) } From fab2d30f381154f01abd35ff844cdb5a9228d8b0 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Mon, 14 Nov 2022 13:53:16 +0000 Subject: [PATCH 0227/1420] Kotlin: Make emitDiagnostic private --- java/kotlin-extractor/src/main/kotlin/utils/Logger.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt b/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt index 6ec568e769a..36ebda9dd1c 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt @@ -141,7 +141,7 @@ open class LoggerBase(val logCounter: LogCounter) { emitDiagnostic(tw, severity, diagnosticLocStr, msg, fullMsg, locationString, mkLocationId) } - fun emitDiagnostic(tw: TrapWriter, severity: Severity, diagnosticLocStr: String, msg: String, fullMsg: String, locationString: String? = null, mkLocationId: () -> Label = { tw.unknownLocation }) { + private fun emitDiagnostic(tw: TrapWriter, severity: Severity, diagnosticLocStr: String, msg: String, fullMsg: String, locationString: String? = null, mkLocationId: () -> Label = { tw.unknownLocation }) { val locStr = if (locationString == null) "" else "At " + locationString + ": " val kind = if (severity <= Severity.WarnHigh) "WARN" else "ERROR" val logMessage = LogMessage(kind, "Diagnostic($diagnosticLocStr): $locStr$fullMsg") From b028d72d51bffe870b427fa416bbaaf67593b390 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 14 Nov 2022 15:07:59 +0100 Subject: [PATCH 0228/1420] JS: Handle DynamicImport in the context of a type --- .../semmle/js/extractor/TypeExprKinds.java | 15 +- .../extractor/tests/ts/input/dynamic-type.ts | 1 + .../tests/ts/output/trap/dynamic-type.ts.trap | 139 ++++++++++++++++++ 3 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 javascript/extractor/tests/ts/input/dynamic-type.ts create mode 100644 javascript/extractor/tests/ts/output/trap/dynamic-type.ts.trap diff --git a/javascript/extractor/src/com/semmle/js/extractor/TypeExprKinds.java b/javascript/extractor/src/com/semmle/js/extractor/TypeExprKinds.java index a1c7b219a8a..82d4e4319c8 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/TypeExprKinds.java +++ b/javascript/extractor/src/com/semmle/js/extractor/TypeExprKinds.java @@ -10,6 +10,7 @@ import com.semmle.js.ast.TemplateElement; import com.semmle.js.extractor.ASTExtractor.IdContext; import com.semmle.ts.ast.ArrayTypeExpr; import com.semmle.ts.ast.ConditionalTypeExpr; +import com.semmle.js.ast.DynamicImport; import com.semmle.ts.ast.FunctionTypeExpr; import com.semmle.ts.ast.GenericTypeExpr; import com.semmle.ts.ast.ImportTypeExpr; @@ -221,8 +222,7 @@ public class TypeExprKinds { return inferTypeExpr; } - @Override - public Integer visit(ImportTypeExpr nd, Void c) { + private Integer handleInlineImport() { switch (idcontext) { case NAMESPACE_BIND: return importNamespaceAccess; @@ -235,6 +235,17 @@ public class TypeExprKinds { } } + @Override + public Integer visit(ImportTypeExpr nd, Void c) { + return handleInlineImport(); + } + + @Override + public Integer visit(DynamicImport nd, Void c) { + // These may appear in interface 'extend' clauses + return handleInlineImport(); + } + @Override public Integer visit(OptionalTypeExpr nd, Void c) { return optionalTypeExpr; diff --git a/javascript/extractor/tests/ts/input/dynamic-type.ts b/javascript/extractor/tests/ts/input/dynamic-type.ts new file mode 100644 index 00000000000..2b2f90337a2 --- /dev/null +++ b/javascript/extractor/tests/ts/input/dynamic-type.ts @@ -0,0 +1 @@ +interface Foo extends import("foo").Bar {} diff --git a/javascript/extractor/tests/ts/output/trap/dynamic-type.ts.trap b/javascript/extractor/tests/ts/output/trap/dynamic-type.ts.trap new file mode 100644 index 00000000000..71410d093cc --- /dev/null +++ b/javascript/extractor/tests/ts/output/trap/dynamic-type.ts.trap @@ -0,0 +1,139 @@ +#10000=@"/dynamic-type.ts;sourcefile" +files(#10000,"/dynamic-type.ts") +#10001=@"/;folder" +folders(#10001,"/") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +#20002=* +lines(#20002,#20001,"interface Foo extends import(""foo"").Bar {}"," +") +#20003=@"loc,{#10000},1,1,1,42" +locations_default(#20003,#10000,1,1,1,42) +hasLocation(#20002,#20003) +numlines(#20001,1,1,0) +#20004=* +tokeninfo(#20004,7,#20001,0,"interface") +#20005=@"loc,{#10000},1,1,1,9" +locations_default(#20005,#10000,1,1,1,9) +hasLocation(#20004,#20005) +#20006=* +tokeninfo(#20006,6,#20001,1,"Foo") +#20007=@"loc,{#10000},1,11,1,13" +locations_default(#20007,#10000,1,11,1,13) +hasLocation(#20006,#20007) +#20008=* +tokeninfo(#20008,7,#20001,2,"extends") +#20009=@"loc,{#10000},1,15,1,21" +locations_default(#20009,#10000,1,15,1,21) +hasLocation(#20008,#20009) +#20010=* +tokeninfo(#20010,7,#20001,3,"import") +#20011=@"loc,{#10000},1,23,1,28" +locations_default(#20011,#10000,1,23,1,28) +hasLocation(#20010,#20011) +#20012=* +tokeninfo(#20012,8,#20001,4,"(") +#20013=@"loc,{#10000},1,29,1,29" +locations_default(#20013,#10000,1,29,1,29) +hasLocation(#20012,#20013) +#20014=* +tokeninfo(#20014,4,#20001,5,"""foo""") +#20015=@"loc,{#10000},1,30,1,34" +locations_default(#20015,#10000,1,30,1,34) +hasLocation(#20014,#20015) +#20016=* +tokeninfo(#20016,8,#20001,6,")") +#20017=@"loc,{#10000},1,35,1,35" +locations_default(#20017,#10000,1,35,1,35) +hasLocation(#20016,#20017) +#20018=* +tokeninfo(#20018,8,#20001,7,".") +#20019=@"loc,{#10000},1,36,1,36" +locations_default(#20019,#10000,1,36,1,36) +hasLocation(#20018,#20019) +#20020=* +tokeninfo(#20020,6,#20001,8,"Bar") +#20021=@"loc,{#10000},1,37,1,39" +locations_default(#20021,#10000,1,37,1,39) +hasLocation(#20020,#20021) +#20022=* +tokeninfo(#20022,8,#20001,9,"{") +#20023=@"loc,{#10000},1,41,1,41" +locations_default(#20023,#10000,1,41,1,41) +hasLocation(#20022,#20023) +#20024=* +tokeninfo(#20024,8,#20001,10,"}") +#20025=@"loc,{#10000},1,42,1,42" +locations_default(#20025,#10000,1,42,1,42) +hasLocation(#20024,#20025) +#20026=* +tokeninfo(#20026,0,#20001,11,"") +#20027=@"loc,{#10000},2,1,2,0" +locations_default(#20027,#10000,2,1,2,0) +hasLocation(#20026,#20027) +toplevels(#20001,0) +#20028=@"loc,{#10000},1,1,2,0" +locations_default(#20028,#10000,1,1,2,0) +hasLocation(#20001,#20028) +#20029=@"local_type_name;{Foo};{#20000}" +local_type_names(#20029,"Foo",#20000) +#20030=* +stmts(#20030,34,#20001,0,"interfa ... .Bar {}") +hasLocation(#20030,#20003) +stmt_containers(#20030,#20001) +#20031=* +typeexprs(#20031,13,#20030,-1,"import(""foo"").Bar") +#20032=@"loc,{#10000},1,23,1,39" +locations_default(#20032,#10000,1,23,1,39) +hasLocation(#20031,#20032) +enclosing_stmt(#20031,#20030) +expr_containers(#20031,#20001) +#20033=* +typeexprs(#20033,31,#20031,0,"import(""foo"")") +#20034=@"loc,{#10000},1,23,1,35" +locations_default(#20034,#10000,1,23,1,35) +hasLocation(#20033,#20034) +enclosing_stmt(#20033,#20030) +expr_containers(#20033,#20001) +#20035=* +exprs(#20035,4,#20033,0,"""foo""") +hasLocation(#20035,#20015) +enclosing_stmt(#20035,#20030) +expr_containers(#20035,#20001) +literals("foo","""foo""",#20035) +#20036=* +regexpterm(#20036,14,#20035,0,"foo") +#20037=@"loc,{#10000},1,31,1,33" +locations_default(#20037,#10000,1,31,1,33) +hasLocation(#20036,#20037) +regexp_const_value(#20036,"foo") +#20038=* +typeexprs(#20038,15,#20031,1,"Bar") +hasLocation(#20038,#20021) +enclosing_stmt(#20038,#20030) +expr_containers(#20038,#20001) +literals("Bar","Bar",#20038) +#20039=* +typeexprs(#20039,1,#20030,0,"Foo") +hasLocation(#20039,#20007) +enclosing_stmt(#20039,#20030) +expr_containers(#20039,#20001) +literals("Foo","Foo",#20039) +typedecl(#20039,#20029) +#20040=* +entry_cfg_node(#20040,#20001) +#20041=@"loc,{#10000},1,1,1,0" +locations_default(#20041,#10000,1,1,1,0) +hasLocation(#20040,#20041) +#20042=* +exit_cfg_node(#20042,#20001) +hasLocation(#20042,#20027) +successor(#20030,#20042) +successor(#20040,#20030) +numlines(#10000,1,1,0) +filetype(#10000,"typescript") From 5f18484fa97a5dd32a0a424865bdbae395668560 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 14 Nov 2022 15:09:30 +0100 Subject: [PATCH 0229/1420] JS: Change note --- .../src/change-notes/2022-11-14-dynamic-import-type-expr.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 javascript/ql/src/change-notes/2022-11-14-dynamic-import-type-expr.md diff --git a/javascript/ql/src/change-notes/2022-11-14-dynamic-import-type-expr.md b/javascript/ql/src/change-notes/2022-11-14-dynamic-import-type-expr.md new file mode 100644 index 00000000000..5f975516620 --- /dev/null +++ b/javascript/ql/src/change-notes/2022-11-14-dynamic-import-type-expr.md @@ -0,0 +1,5 @@ +--- +category: fix +--- +* Fixed a bug that would cause the extractor to crash when an `import` type is used in + the `extends` clause of an `interface`. From 2bcf9b86cfa976972a16bb2d5d3452aedd7e7274 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 14 Nov 2022 15:09:50 +0100 Subject: [PATCH 0230/1420] JS: Bump extractor version string --- javascript/extractor/src/com/semmle/js/extractor/Main.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java index d03b345af59..325e9e9123d 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/Main.java +++ b/javascript/extractor/src/com/semmle/js/extractor/Main.java @@ -41,7 +41,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-11-08"; + public static final String EXTRACTOR_VERSION = "2022-11-14"; public static final Pattern NEWLINE = Pattern.compile("\n"); From 3e6eedec300bcd6b9f70961225d63704d3a0f220 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 14 Nov 2022 14:42:56 +0000 Subject: [PATCH 0231/1420] Swift: Fix test output after merge. --- .../Security/CWE-311/CleartextTransmission.expected | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected b/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected index 0ce9f3b10c7..3245aa68d4e 100644 --- a/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected +++ b/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected @@ -1,11 +1,11 @@ edges +| testAlamofire.swift:150:45:150:45 | password : | testAlamofire.swift:150:13:150:45 | ... .+(_:_:) ... | +| testAlamofire.swift:152:51:152:51 | password : | testAlamofire.swift:152:19:152:51 | ... .+(_:_:) ... | +| testAlamofire.swift:154:38:154:38 | email : | testAlamofire.swift:154:14:154:46 | ... .+(_:_:) ... | | testSend.swift:5:5:5:29 | [summary param] 0 in init(_:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(_:) : | | testSend.swift:33:14:33:32 | call to init(_:) : | testSend.swift:37:19:37:19 | data2 | | testSend.swift:33:19:33:19 | passwordPlain : | testSend.swift:5:5:5:29 | [summary param] 0 in init(_:) : | | testSend.swift:33:19:33:19 | passwordPlain : | testSend.swift:33:14:33:32 | call to init(_:) : | -| testAlamofire.swift:150:45:150:45 | password : | testAlamofire.swift:150:13:150:45 | ... .+(_:_:) ... | -| testAlamofire.swift:152:51:152:51 | password : | testAlamofire.swift:152:19:152:51 | ... .+(_:_:) ... | -| testAlamofire.swift:154:38:154:38 | email : | testAlamofire.swift:154:14:154:46 | ... .+(_:_:) ... | | testSend.swift:41:10:41:18 | data : | testSend.swift:41:45:41:45 | data : | | testSend.swift:45:13:45:13 | password : | testSend.swift:52:27:52:27 | str1 | | testSend.swift:46:13:46:13 | password : | testSend.swift:53:27:53:27 | str2 | @@ -16,13 +16,13 @@ edges | testURL.swift:16:55:16:55 | credit_card_no : | testURL.swift:16:22:16:55 | ... .+(_:_:) ... | nodes | file://:0:0:0:0 | [summary] to write: return (return) in init(_:) : | semmle.label | [summary] to write: return (return) in init(_:) : | -| testSend.swift:5:5:5:29 | [summary param] 0 in init(_:) : | semmle.label | [summary param] 0 in init(_:) : | | testAlamofire.swift:150:13:150:45 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... | | testAlamofire.swift:150:45:150:45 | password : | semmle.label | password : | | testAlamofire.swift:152:19:152:51 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... | | testAlamofire.swift:152:51:152:51 | password : | semmle.label | password : | | testAlamofire.swift:154:14:154:46 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... | -| testAlamofire.swift:154:38:154:38 | email : | semmle.label | email : |= +| testAlamofire.swift:154:38:154:38 | email : | semmle.label | email : | +| testSend.swift:5:5:5:29 | [summary param] 0 in init(_:) : | semmle.label | [summary param] 0 in init(_:) : | | testSend.swift:29:19:29:19 | passwordPlain | semmle.label | passwordPlain | | testSend.swift:33:14:33:32 | call to init(_:) : | semmle.label | call to init(_:) : | | testSend.swift:33:19:33:19 | passwordPlain : | semmle.label | passwordPlain : | From f2888dcb1e9b456470d09dd6800e9df55b7127f1 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Fri, 4 Nov 2022 20:16:41 +0100 Subject: [PATCH 0232/1420] Add sinks and tests for the AEXML library. --- .../codeql/swift/frameworks/AEXML/AEXML.qll | 56 +++++++ swift/ql/lib/codeql/swift/security/XXE.qll | 71 +++++++++ .../CWE-611/testAEXMLDocumentXXE.swift | 148 ++++++++++++++++++ 3 files changed, 275 insertions(+) create mode 100644 swift/ql/lib/codeql/swift/frameworks/AEXML/AEXML.qll create mode 100644 swift/ql/test/query-tests/Security/CWE-611/testAEXMLDocumentXXE.swift diff --git a/swift/ql/lib/codeql/swift/frameworks/AEXML/AEXML.qll b/swift/ql/lib/codeql/swift/frameworks/AEXML/AEXML.qll new file mode 100644 index 00000000000..c779c8f7122 --- /dev/null +++ b/swift/ql/lib/codeql/swift/frameworks/AEXML/AEXML.qll @@ -0,0 +1,56 @@ +import swift + +/** The creation of an `AEXMLParser`. */ +class AexmlParser extends ApplyExpr { + AexmlParser() { + this.getStaticTarget().(ConstructorDecl).getEnclosingDecl() instanceof AexmlParserDecl + } +} + +/** The creation of an `AEXMLDocument`. */ +class AexmlDocument extends ApplyExpr { + AexmlDocument() { + this.getStaticTarget().(ConstructorDecl).getEnclosingDecl() instanceof AexmlDocumentDecl + } +} + +/** A call to `AEXMLDocument.loadXML(_:)`. */ +class AexmlDocumentLoadXml extends MethodApplyExpr { + AexmlDocumentLoadXml() { + exists(MethodDecl f | + this.getStaticTarget() = f and + f.hasName("loadXML(_:)") and + f.getEnclosingDecl() instanceof AexmlDocumentDecl + ) + } +} + +/** The class `AEXMLParser`. */ +class AexmlParserDecl extends ClassDecl { + AexmlParserDecl() { this.getFullName() = "AEXMLParser" } +} + +/** The class `AEXMLDocument`. */ +class AexmlDocumentDecl extends ClassDecl { + AexmlDocumentDecl() { this.getFullName() = "AEXMLDocument" } +} + +/** A reference to the field `AEXMLOptions.ParserSettings.shouldResolveExternalEntities`. */ +class AexmlShouldResolveExternalEntities extends MemberRefExpr { + AexmlShouldResolveExternalEntities() { + exists(FieldDecl f | this.getMember() = f | + f.getName() = "shouldResolveExternalEntities" and + f.getEnclosingDecl().(NominalTypeDecl).getType() instanceof AexmlOptionsParserSettingsType + ) + } +} + +/** The type `AEXMLOptions`. */ +class AexmlOptionsType extends StructType { + AexmlOptionsType() { this.getFullName() = "AEXMLOptions" } +} + +/** The type `AEXMLOptions.ParserSettings`. */ +class AexmlOptionsParserSettingsType extends StructType { + AexmlOptionsParserSettingsType() { this.getFullName() = "AEXMLOptions.ParserSettings" } +} diff --git a/swift/ql/lib/codeql/swift/security/XXE.qll b/swift/ql/lib/codeql/swift/security/XXE.qll index da8513be645..cc102a34ff0 100644 --- a/swift/ql/lib/codeql/swift/security/XXE.qll +++ b/swift/ql/lib/codeql/swift/security/XXE.qll @@ -2,6 +2,7 @@ import swift private import codeql.swift.dataflow.DataFlow +private import codeql.swift.frameworks.AEXML.AEXML /** A data flow sink for XML external entities (XXE) vulnerabilities. */ abstract class XxeSink extends DataFlow::Node { } @@ -90,3 +91,73 @@ private class NodeLoadExternalEntitiesAlways extends VarDecl { this.getEnclosingDecl().(StructDecl).getFullName() = "XMLNode.Options" } } + +/** The XML argument of an `AEXMLDocument` vulnerable to XXE. */ +private class AexmlDocumentSink extends XxeSink { + AexmlDocumentSink() { + // `AEXMLDocument` initialized with vulnerable options. + exists(ApplyExpr call | this.asExpr() = call.getArgument(0).getExpr() | + call.(VulnerableAexmlDocument) + .getStaticTarget() + .hasName(["init(xml:options:)", "init(xml:encoding:options:)"]) + or + // `loadXML` called on a vulnerable AEXMLDocument. + DataFlow::localExprFlow(any(VulnerableAexmlDocument v), + call.(AexmlDocumentLoadXml).getQualifier()) + ) + } +} + +/** The XML argument of an `AEXMLParser` initialized with an `AEXMLDocument` vulnerable to XXE. */ +private class AexmlParserSink extends XxeSink { + AexmlParserSink() { + exists(AexmlParser parser | this.asExpr() = parser.getArgument(1).getExpr() | + DataFlow::localExprFlow(any(VulnerableAexmlDocument v), parser.getArgument(0).getExpr()) + ) + } +} + +/** The creation of an `AEXMLDocument` that receives a vulnerable `AEXMLOptions` argument. */ +private class VulnerableAexmlDocument extends AexmlDocument { + VulnerableAexmlDocument() { + exists(AexmlOptions optionsArgument, VulnerableAexmlOptions vulnOpts | + this.getAnArgument().getExpr() = optionsArgument and + DataFlow::localExprFlow(vulnOpts, optionsArgument) + ) + } +} + +/** + * An `AEXMLOptions` object which contains a `parserSettings` with `shouldResolveExternalEntities` + * set to `true`. + */ +private class VulnerableAexmlOptions extends AexmlOptions { + VulnerableAexmlOptions() { + exists(ParserSettings parserSettings, AexmlShouldResolveExternalEntities sree, AssignExpr a | + a.getSource() = any(BooleanLiteralExpr b | b.getValue() = true) and + a.getDest() = sree and + sree.(MemberRefExpr).getBase() = parserSettings and + parserSettings.(MemberRefExpr).getBase() = this + ) + } +} + +/** An expression of type `AEXMLOptions.ParserSettings`. */ +class ParserSettings extends Expr { + pragma[inline] + ParserSettings() { + this.getType() instanceof AexmlOptionsParserSettingsType or + this.getType() = any(OptionalType t | t.getBaseType() instanceof AexmlOptionsParserSettingsType) or + this.getType() = any(LValueType t | t.getObjectType() instanceof AexmlOptionsParserSettingsType) + } +} + +/** An expression of type `AEXMLOptions`. */ +class AexmlOptions extends Expr { + pragma[inline] + AexmlOptions() { + this.getType() instanceof AexmlOptionsType or + this.getType() = any(OptionalType t | t.getBaseType() instanceof AexmlOptionsType) or + this.getType() = any(LValueType t | t.getObjectType() instanceof AexmlOptionsType) + } +} diff --git a/swift/ql/test/query-tests/Security/CWE-611/testAEXMLDocumentXXE.swift b/swift/ql/test/query-tests/Security/CWE-611/testAEXMLDocumentXXE.swift new file mode 100644 index 00000000000..9f337030158 --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-611/testAEXMLDocumentXXE.swift @@ -0,0 +1,148 @@ +// --- stubs --- + +class Data { + init(_ elements: S) {} +} + +struct URL { + init?(string: String) {} +} + +extension String { + struct Encoding: Hashable { + let rawValue: UInt + static let utf8 = String.Encoding(rawValue: 1) + } + + init(contentsOf: URL) { + let data = "" + self.init(data) + } +} + +class AEXMLElement {} + +struct AEXMLOptions { + var parserSettings = ParserSettings() + + struct ParserSettings { + public var shouldResolveExternalEntities = false + } +} + +class AEXMLDocument { + init(root: AEXMLElement? = nil, options: AEXMLOptions) {} + init(xml: Data, options: AEXMLOptions = AEXMLOptions()) {} + init(xml: String, encoding: String.Encoding, options: AEXMLOptions) {} + func loadXML(_: Data) {} +} + +class AEXMLParser { + init(document: AEXMLDocument, data: Data) {} +} + +// --- tests --- + +func testString() { + var options = AEXMLOptions() + options.parserSettings.shouldResolveExternalEntities = true + + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let _ = AEXMLDocument(xml: remoteString, encoding: String.Encoding.utf8, options: options) // $ hasXXE=50 +} + +func testStringSafeImplicit() { + var options = AEXMLOptions() + + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let _ = AEXMLDocument(xml: remoteString, encoding: String.Encoding.utf8, options: options) // NO XXE +} + +func testStringSafeExplicit() { + var options = AEXMLOptions() + options.parserSettings.shouldResolveExternalEntities = false + + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let _ = AEXMLDocument(xml: remoteString, encoding: String.Encoding.utf8, options: options) // NO XXE +} + +func testData() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteData = Data(remoteString) + var options = AEXMLOptions() + options.parserSettings.shouldResolveExternalEntities = true + let _ = AEXMLDocument(xml: remoteData, options: options) // $ hasXXE=70 +} + +func testDataSafeImplicit() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteData = Data(remoteString) + var options = AEXMLOptions() + let _ = AEXMLDocument(xml: remoteData, options: options) // NO XXE +} + +func testDataSafeExplicit() { + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteData = Data(remoteString) + var options = AEXMLOptions() + options.parserSettings.shouldResolveExternalEntities = false + let _ = AEXMLDocument(xml: remoteData, options: options) // NO XXE +} + +func testDataLoadXml() { + var options = AEXMLOptions() + options.parserSettings.shouldResolveExternalEntities = true + let doc = AEXMLDocument(root: nil, options: options) + + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteData = Data(remoteString) + doc.loadXML(remoteData) // $ hasXXE=97 +} + +func testDataLoadXmlSafeImplicit() { + var options = AEXMLOptions() + let doc = AEXMLDocument(root: nil, options: options) + + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteData = Data(remoteString) + doc.loadXML(remoteData) // NO XXE +} + +func testDataLoadXmlSafeExplicit() { + var options = AEXMLOptions() + options.parserSettings.shouldResolveExternalEntities = false + let doc = AEXMLDocument(root: nil, options: options) + + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteData = Data(remoteString) + doc.loadXML(remoteData) // NO XXE +} + +func testParser() { + var options = AEXMLOptions() + options.parserSettings.shouldResolveExternalEntities = true + let doc = AEXMLDocument(root: nil, options: options) + + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteData = Data(remoteString) + let _ = AEXMLParser(document: doc, data: remoteData) // $ hasXXE=126 +} + +func testParserSafeImplicit() { + var options = AEXMLOptions() + let doc = AEXMLDocument(root: nil, options: options) + + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteData = Data(remoteString) + let _ = AEXMLParser(document: doc, data: remoteData) // NO XXE +} + +func testParserSafeExplicit() { + var options = AEXMLOptions() + options.parserSettings.shouldResolveExternalEntities = false + let doc = AEXMLDocument(root: nil, options: options) + + let remoteString = String(contentsOf: URL(string: "http://example.com/")!) + let remoteData = Data(remoteString) + let _ = AEXMLParser(document: doc, data: remoteData) // NO XXE +} \ No newline at end of file From 07de92cdb6d93b9b052639208e7065bab3ba9319 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Mon, 7 Nov 2022 17:09:18 +0100 Subject: [PATCH 0233/1420] Move AEXML.qll to avoid nesting --- swift/ql/lib/codeql/swift/frameworks/{AEXML => }/AEXML.qll | 0 swift/ql/lib/codeql/swift/security/XXE.qll | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename swift/ql/lib/codeql/swift/frameworks/{AEXML => }/AEXML.qll (100%) diff --git a/swift/ql/lib/codeql/swift/frameworks/AEXML/AEXML.qll b/swift/ql/lib/codeql/swift/frameworks/AEXML.qll similarity index 100% rename from swift/ql/lib/codeql/swift/frameworks/AEXML/AEXML.qll rename to swift/ql/lib/codeql/swift/frameworks/AEXML.qll diff --git a/swift/ql/lib/codeql/swift/security/XXE.qll b/swift/ql/lib/codeql/swift/security/XXE.qll index cc102a34ff0..eeb6170a7c7 100644 --- a/swift/ql/lib/codeql/swift/security/XXE.qll +++ b/swift/ql/lib/codeql/swift/security/XXE.qll @@ -2,7 +2,7 @@ import swift private import codeql.swift.dataflow.DataFlow -private import codeql.swift.frameworks.AEXML.AEXML +private import codeql.swift.frameworks.AEXML /** A data flow sink for XML external entities (XXE) vulnerabilities. */ abstract class XxeSink extends DataFlow::Node { } From 5791e8b9a2650a6e674a1db02503c1275b61486a Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Tue, 8 Nov 2022 10:31:49 +0100 Subject: [PATCH 0234/1420] Slight renaming --- swift/ql/lib/codeql/swift/security/XXE.qll | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/swift/ql/lib/codeql/swift/security/XXE.qll b/swift/ql/lib/codeql/swift/security/XXE.qll index eeb6170a7c7..5f80426fa56 100644 --- a/swift/ql/lib/codeql/swift/security/XXE.qll +++ b/swift/ql/lib/codeql/swift/security/XXE.qll @@ -133,7 +133,9 @@ private class VulnerableAexmlDocument extends AexmlDocument { */ private class VulnerableAexmlOptions extends AexmlOptions { VulnerableAexmlOptions() { - exists(ParserSettings parserSettings, AexmlShouldResolveExternalEntities sree, AssignExpr a | + exists( + AexmlParserSettings parserSettings, AexmlShouldResolveExternalEntities sree, AssignExpr a + | a.getSource() = any(BooleanLiteralExpr b | b.getValue() = true) and a.getDest() = sree and sree.(MemberRefExpr).getBase() = parserSettings and @@ -143,9 +145,9 @@ private class VulnerableAexmlOptions extends AexmlOptions { } /** An expression of type `AEXMLOptions.ParserSettings`. */ -class ParserSettings extends Expr { +private class AexmlParserSettings extends Expr { pragma[inline] - ParserSettings() { + AexmlParserSettings() { this.getType() instanceof AexmlOptionsParserSettingsType or this.getType() = any(OptionalType t | t.getBaseType() instanceof AexmlOptionsParserSettingsType) or this.getType() = any(LValueType t | t.getObjectType() instanceof AexmlOptionsParserSettingsType) @@ -153,7 +155,7 @@ class ParserSettings extends Expr { } /** An expression of type `AEXMLOptions`. */ -class AexmlOptions extends Expr { +private class AexmlOptions extends Expr { pragma[inline] AexmlOptions() { this.getType() instanceof AexmlOptionsType or From 7e5970f338aba1e089d0c7c8444dd537d2ac0917 Mon Sep 17 00:00:00 2001 From: Edward Minnix III Date: Mon, 14 Nov 2022 10:02:20 -0500 Subject: [PATCH 0235/1420] Java: Fix typos/formatting in setJavascriptEnabled query Typos and formatting changes. Co-authored-by: Tony Torralba --- .../AndroidWebViewSettingsEnabledJavaScript.qhelp | 10 +++++----- .../CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql | 6 ++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.qhelp b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.qhelp index c10856588c6..14c465b0664 100644 --- a/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.qhelp +++ b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.qhelp @@ -11,22 +11,22 @@ man-in-the-middle attack, where the attacker can inject arbitrary JavaScript.

    You can enable or disbale Javascript execution using - the setJavaScriptEnabled method of the settings of a webview. + the setJavaScriptEnabled method of the settings of a WebView.

    -

    If Javascript does not need to be enabled, call setJavaScriptEnabled(false) on the settings of the webview.

    +

    If Javascript does not need to be enabled, call setJavaScriptEnabled(false) on the settings of the WebView.

    -

    If JavaScript is necessary, only load content from trusted servers using encrypted channels, such as https with certificate verification.

    +

    If JavaScript is necessary, only load content from trusted servers using encrypted channels, such as HTTPS with certificate verification.

    -

    In the following (bad) example, a webview has JavaScript enabled in its settings.

    +

    In the following (bad) example, a WebView has JavaScript enabled in its settings.

    -

    In the following (good) example, a webview explicitly disallows JavaScript execution.

    +

    In the following (good) example, a WebView explicitly disallows JavaScript execution.

    diff --git a/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql index 409ab786791..a12f57618ec 100644 --- a/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql +++ b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql @@ -15,8 +15,6 @@ import semmle.code.java.frameworks.android.WebView from MethodAccess ma where - ( - ma.getMethod() instanceof AllowJavaScriptMethod and - ma.getArgument(0).(CompileTimeConstantExpr).getBooleanValue() = true - ) + ma.getMethod() instanceof AllowJavaScriptMethod and + ma.getArgument(0).(CompileTimeConstantExpr).getBooleanValue() = true select ma, "JavaScript execution enabled in WebView." From 55fad8ab23b11d0def8ce554cb0e220c57b644c5 Mon Sep 17 00:00:00 2001 From: Edward Minnix III Date: Mon, 14 Nov 2022 10:04:01 -0500 Subject: [PATCH 0236/1420] Java: Fix description of setJavascriptEnabled query Co-authored-by: Tony Torralba --- .../CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql index a12f57618ec..5ed884610b0 100644 --- a/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql +++ b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql @@ -1,6 +1,6 @@ /** * @name Android WebView JavaScript settings - * @description Enabling JavaScript execution in a WebView can result in man-in-the-middle attacks. + * @description Enabling JavaScript execution in a WebView can result in cross-site scripting attacks. * @kind problem * @id java/android-websettings-javascript-enabled * @problem.severity warning From a7e7334f0f711ff96043dec1145c4d5377cc353e Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Mon, 14 Nov 2022 11:10:43 -0500 Subject: [PATCH 0237/1420] Java: Documentation cleanup for setJavascriptEnabled --- ...roidWebViewSettingsEnabledJavaScript.qhelp | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.qhelp b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.qhelp index 14c465b0664..077e80700b9 100644 --- a/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.qhelp +++ b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.qhelp @@ -3,16 +3,22 @@ "qhelp.dtd"> -

    - Enabling JavaScript in an Android WebView allows for the running of JavaScript -code in the context of the running application. This opens the possibility for a -man-in-the-middle attack, where the attacker can inject arbitrary JavaScript. -

    +

    + Enabling JavaScript in an Android WebView allows for the running of JavaScript + code in the context of the running application. This opens the possibility for + cross-site scripting if the attacker can inject arbitrary JavaScript. +

    -

    - You can enable or disbale Javascript execution using - the setJavaScriptEnabled method of the settings of a WebView. -

    +

    + For example, if your application's WebView allows for visitng web pages + which you do not trust, it is possible for an attacker to lead the user to + a page which loads malicious JavaScript. +

    + +

    + You can enable or disbale Javascript execution using + the setJavaScriptEnabled method of the settings of a WebView. +

    @@ -33,13 +39,9 @@ man-in-the-middle attack, where the attacker can inject arbitrary JavaScript.
    -
  • - Oversecured Android Vulnerabilities Guide: Enabled JavaScript -
  • Android documentation: setJavaScriptEnabled
  • -
    From 324e0e8f9030fa0675f31ce497c5a91b481e12d5 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Mon, 14 Nov 2022 17:33:48 +0100 Subject: [PATCH 0238/1420] always sort both by location and by term tostring --- shared/regex/codeql/regex/nfa/NfaUtils.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/regex/codeql/regex/nfa/NfaUtils.qll b/shared/regex/codeql/regex/nfa/NfaUtils.qll index b3be9c73bfd..cb5091c40aa 100644 --- a/shared/regex/codeql/regex/nfa/NfaUtils.qll +++ b/shared/regex/codeql/regex/nfa/NfaUtils.qll @@ -166,7 +166,7 @@ module Make { min(RelevantRegExpTerm t | str = getCanonicalizationString(t) | - t order by getTermLocationString(t) + t order by getTermLocationString(t), t.toString() ) } @@ -949,7 +949,7 @@ module Make { isStartState(s) and getRoot(s.getRepr()) = root | - s order by getTermLocationString(s.getRepr()) + s order by getTermLocationString(s.getRepr()), s.getRepr().toString() ) ) } @@ -1047,7 +1047,7 @@ module Make { isCandidate(s, _) and s.getRepr() instanceof InfiniteRepetitionQuantifier | - s order by getTermLocationString(s.getRepr()) + s order by getTermLocationString(s.getRepr()), s.getRepr().toString() ) ) } From 99636ba3445e204696e9e25956a3a12d62973dfa Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 14 Nov 2022 17:35:55 +0100 Subject: [PATCH 0239/1420] fix typo Co-authored-by: yoff --- python/ql/lib/semmle/python/RegexTreeView.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/lib/semmle/python/RegexTreeView.qll b/python/ql/lib/semmle/python/RegexTreeView.qll index 4af5ca19523..9ef211d53b0 100644 --- a/python/ql/lib/semmle/python/RegexTreeView.qll +++ b/python/ql/lib/semmle/python/RegexTreeView.qll @@ -82,7 +82,7 @@ private RegExpTerm seqChild(Regex re, int start, int end, int i) { ) } -/** An implementation that statisfies the RegexTreeView signature. */ +/** An implementation that satisfies the RegexTreeView signature. */ module Impl implements RegexTreeViewSig { /** * An element containing a regular expression term, that is, either From 113257262055ffdaa6191bb92cab839a7f17280f Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Mon, 14 Nov 2022 14:33:12 -0500 Subject: [PATCH 0240/1420] Java: add test cases for setJavaScriptEnabled query --- .../semmle/tests/SetJavascriptEnabled.java | 18 ++++++++++++++++++ .../tests/WebViewSetEnabledJavaScript.expected | 1 + .../tests/WebViewSetEnabledJavaScript.qlref | 1 + .../security/CWE-079/semmle/tests/options | 2 +- 4 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 java/ql/test/query-tests/security/CWE-079/semmle/tests/SetJavascriptEnabled.java create mode 100644 java/ql/test/query-tests/security/CWE-079/semmle/tests/WebViewSetEnabledJavaScript.expected create mode 100644 java/ql/test/query-tests/security/CWE-079/semmle/tests/WebViewSetEnabledJavaScript.qlref diff --git a/java/ql/test/query-tests/security/CWE-079/semmle/tests/SetJavascriptEnabled.java b/java/ql/test/query-tests/security/CWE-079/semmle/tests/SetJavascriptEnabled.java new file mode 100644 index 00000000000..d5593e38678 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-079/semmle/tests/SetJavascriptEnabled.java @@ -0,0 +1,18 @@ +package com.example.test; + +import android.webkit.WebView; +import android.webkit.WebSettings; + +public class SetJavascriptEnabled { + public static void configureWebViewUnsafe(WebView view) { + WebSettings settings = view.getSettings(); + settings.setJavaScriptEnabled(true); // $javascriptEnabled + } + + public static void configureWebViewSafe(WebView view) { + WebSettings settings = view.getSettings(); + + // Safe: Javascript disabled + settings.setJavaScriptEnabled(false); + } +} diff --git a/java/ql/test/query-tests/security/CWE-079/semmle/tests/WebViewSetEnabledJavaScript.expected b/java/ql/test/query-tests/security/CWE-079/semmle/tests/WebViewSetEnabledJavaScript.expected new file mode 100644 index 00000000000..22cb40574f4 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-079/semmle/tests/WebViewSetEnabledJavaScript.expected @@ -0,0 +1 @@ +| SetJavascriptEnabled.java:9:9:9:43 | setJavaScriptEnabled(...) | JavaScript execution enabled in WebView. | diff --git a/java/ql/test/query-tests/security/CWE-079/semmle/tests/WebViewSetEnabledJavaScript.qlref b/java/ql/test/query-tests/security/CWE-079/semmle/tests/WebViewSetEnabledJavaScript.qlref new file mode 100644 index 00000000000..e9e8006886d --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-079/semmle/tests/WebViewSetEnabledJavaScript.qlref @@ -0,0 +1 @@ +Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql diff --git a/java/ql/test/query-tests/security/CWE-079/semmle/tests/options b/java/ql/test/query-tests/security/CWE-079/semmle/tests/options index 22487fb2daf..62fc56e6792 100644 --- a/java/ql/test/query-tests/security/CWE-079/semmle/tests/options +++ b/java/ql/test/query-tests/security/CWE-079/semmle/tests/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/servlet-api-2.4:${testdir}/../../../../../stubs/javax-ws-rs-api-2.1.1/:${testdir}/../../../../../stubs/springframework-5.3.8:${testdir}/../../../../../stubs/javax-faces-2.3/ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/servlet-api-2.4:${testdir}/../../../../../stubs/javax-ws-rs-api-2.1.1/:${testdir}/../../../../../stubs/springframework-5.3.8:${testdir}/../../../../../stubs/javax-faces-2.3/:${testdir}/../../../../../stubs/google-android-9.0.0 From 89411a1db25ec151ebca387f7d41bd2a8d639bab Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Mon, 14 Nov 2022 14:41:06 -0500 Subject: [PATCH 0241/1420] Fix alert message style --- .../Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql b/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql index c705ac52425..b57fc4ad816 100644 --- a/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql +++ b/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql @@ -15,5 +15,4 @@ import semmle.code.java.frameworks.android.WebView from MethodAccess ma where ma.getMethod() instanceof CrossOriginAccessMethod -select ma, "WebView setting $@ may allow for unauthorized access of sensitive information.", ma, - ma.getMethod().getName() +select ma, "WebView setting " + ma.getMethod().getName() + " may allow for unauthorized access of sensitive information." From 73d6360eee6ac4733e75d9d67275c2234df1ad3f Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Mon, 14 Nov 2022 15:07:09 -0500 Subject: [PATCH 0242/1420] Java: add setAllowFileAccess to CrossOriginAccessMethod in WebView.qll Local file access is enabled using the `WebSettings#setAllowFileAccess` method. --- java/ql/lib/semmle/code/java/frameworks/android/WebView.qll | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/java/ql/lib/semmle/code/java/frameworks/android/WebView.qll b/java/ql/lib/semmle/code/java/frameworks/android/WebView.qll index 8dd91f73f65..b514ca94be7 100644 --- a/java/ql/lib/semmle/code/java/frameworks/android/WebView.qll +++ b/java/ql/lib/semmle/code/java/frameworks/android/WebView.qll @@ -45,7 +45,10 @@ class WebViewGetUrlMethod extends Method { class CrossOriginAccessMethod extends Method { CrossOriginAccessMethod() { this.getDeclaringType() instanceof TypeWebSettings and - this.hasName(["setAllowUniversalAccessFromFileURLs", "setAllowFileAccessFromFileURLs"]) + this.hasName([ + "setAllowFileAccess", "setAllowUniversalAccessFromFileURLs", + "setAllowFileAccessFromFileURLs" + ]) } } From 7a0544d80e2772f1f87bc13f6f049460ef98569b Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Mon, 14 Nov 2022 15:11:15 -0500 Subject: [PATCH 0243/1420] Java: test files for WebView file access query --- .../semmle/tests/WebViewFileAccess.expected | 3 +++ .../CWE-200/semmle/tests/WebViewFileAccess.java | 14 ++++++++++++++ .../CWE-200/semmle/tests/WebViewFileAccess.qlref | 1 + .../security/CWE-200/semmle/tests/options | 2 +- 4 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 java/ql/test/query-tests/security/CWE-200/semmle/tests/WebViewFileAccess.expected create mode 100644 java/ql/test/query-tests/security/CWE-200/semmle/tests/WebViewFileAccess.java create mode 100644 java/ql/test/query-tests/security/CWE-200/semmle/tests/WebViewFileAccess.qlref diff --git a/java/ql/test/query-tests/security/CWE-200/semmle/tests/WebViewFileAccess.expected b/java/ql/test/query-tests/security/CWE-200/semmle/tests/WebViewFileAccess.expected new file mode 100644 index 00000000000..78cf64f9aa5 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-200/semmle/tests/WebViewFileAccess.expected @@ -0,0 +1,3 @@ +| WebViewFileAccess.java:8:9:8:41 | setAllowFileAccess(...) | WebView setting setAllowFileAccess may allow for unauthorized access of sensitive information. | +| WebViewFileAccess.java:10:9:10:53 | setAllowFileAccessFromFileURLs(...) | WebView setting setAllowFileAccessFromFileURLs may allow for unauthorized access of sensitive information. | +| WebViewFileAccess.java:12:9:12:58 | setAllowUniversalAccessFromFileURLs(...) | WebView setting setAllowUniversalAccessFromFileURLs may allow for unauthorized access of sensitive information. | \ No newline at end of file diff --git a/java/ql/test/query-tests/security/CWE-200/semmle/tests/WebViewFileAccess.java b/java/ql/test/query-tests/security/CWE-200/semmle/tests/WebViewFileAccess.java new file mode 100644 index 00000000000..8599f6a4e3a --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-200/semmle/tests/WebViewFileAccess.java @@ -0,0 +1,14 @@ +import android.webkit.WebView; +import android.webkit.WebSettings; + +class WebViewFileAccess { + void configure(WebView view) { + WebSettings settings = view.getSettings(); + + settings.setAllowFileAccess(true); + + settings.setAllowFileAccessFromFileURLs(true); + + settings.setAllowUniversalAccessFromFileURLs(true); + } +} diff --git a/java/ql/test/query-tests/security/CWE-200/semmle/tests/WebViewFileAccess.qlref b/java/ql/test/query-tests/security/CWE-200/semmle/tests/WebViewFileAccess.qlref new file mode 100644 index 00000000000..6c3224a4a61 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-200/semmle/tests/WebViewFileAccess.qlref @@ -0,0 +1 @@ +Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql diff --git a/java/ql/test/query-tests/security/CWE-200/semmle/tests/options b/java/ql/test/query-tests/security/CWE-200/semmle/tests/options index 8b14bf08cd2..21cf6382c4e 100644 --- a/java/ql/test/query-tests/security/CWE-200/semmle/tests/options +++ b/java/ql/test/query-tests/security/CWE-200/semmle/tests/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/apache-commons-lang3-3.7/ \ No newline at end of file +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/apache-commons-lang3-3.7/:${testdir}/../../../../../stubs/google-android-9.0.0 \ No newline at end of file From 2fb95368475991aa4aaf2d86f9c44b988c887b53 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Mon, 14 Nov 2022 15:14:09 -0500 Subject: [PATCH 0244/1420] Java: documentation cleanup for WebView file access query --- .../AndroidWebViewSettingsFileAccess.qhelp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.qhelp b/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.qhelp index eba8aea88b2..f54843be7b2 100644 --- a/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.qhelp +++ b/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.qhelp @@ -6,8 +6,8 @@

    File access in an Android WebView can expose the device's file system to the JavaScript running in the WebView. If there are vulnerabilities in the - JavaScript, file access may allow an attacker to access or steal the - user's data. + JavaScript or untrusted content is loaded in the WebView, file access may + allow an attacker to access or steal the user's data.

    @@ -40,18 +40,6 @@
  • Android documentation: WebSettings.setAllowFileAccessFromFileURLs.
  • -
  • - Android documentation: WebSettings.setAllowUniversalAccessFromFileURLs. -
  • -
  • - File access from URLs is enabled for WebView: File access for URLs is enabled for WebView. -
  • -
  • - File access is enabled for WebView: File access is enabled for WebView. -
  • -
  • - Universal file access from file URLs is enabled for WebView: Universal file access from file URLs is enabled for WebView. -
  • From af1470de076fd30e81285568151f611697255bec Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Tue, 1 Nov 2022 12:13:10 +0100 Subject: [PATCH 0245/1420] add codeql/regex as a dependency --- java/ql/lib/qlpack.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index 0d8258e5ef1..32f1ef40ae8 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -5,3 +5,5 @@ dbscheme: config/semmlecode.dbscheme extractor: java library: true upgrades: upgrades +dependencies: + codeql/regex: 0.0.1 \ No newline at end of file From 20254dfc084010dccc7ef1851e4fbe69e19c850a Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Tue, 1 Nov 2022 12:14:50 +0100 Subject: [PATCH 0246/1420] move existing regex-tree into a module --- .../semmle/code/java/regex/RegexTreeView.qll | 1949 +++++++++-------- 1 file changed, 977 insertions(+), 972 deletions(-) diff --git a/java/ql/lib/semmle/code/java/regex/RegexTreeView.qll b/java/ql/lib/semmle/code/java/regex/RegexTreeView.qll index 14663327103..ceccd3efd5f 100644 --- a/java/ql/lib/semmle/code/java/regex/RegexTreeView.qll +++ b/java/ql/lib/semmle/code/java/regex/RegexTreeView.qll @@ -2,6 +2,10 @@ private import java private import semmle.code.java.regex.regex +import Impl + +/** Gets the parse tree resulting from parsing `re`, if such has been constructed. */ +RegExpTerm getParsedRegExp(StringLiteral re) { result.getRegex() = re and result.isRootTerm() } /** * An element containing a regular expression term, that is, either @@ -49,1038 +53,1039 @@ private newtype TRegExpParent = /** A back reference */ TRegExpBackRef(Regex re, int start, int end) { re.backreference(start, end) } -/** - * An element containing a regular expression term, that is, either - * a string literal (parsed as a regular expression; the root of the parse tree) - * or another regular expression term (a descendant of the root). - */ -class RegExpParent extends TRegExpParent { - /** Gets a textual representation of this element. */ - string toString() { result = "RegExpParent" } +module Impl { + /** + * An element containing a regular expression term, that is, either + * a string literal (parsed as a regular expression; the root of the parse tree) + * or another regular expression term (a descendant of the root). + */ + class RegExpParent extends TRegExpParent { + /** Gets a textual representation of this element. */ + string toString() { result = "RegExpParent" } - /** Gets the `i`th child term. */ - RegExpTerm getChild(int i) { none() } + /** Gets the `i`th child term. */ + RegExpTerm getChild(int i) { none() } - /** Gets a child term . */ - RegExpTerm getAChild() { result = this.getChild(_) } + /** Gets a child term . */ + RegExpTerm getAChild() { result = this.getChild(_) } - /** Gets the number of child terms. */ - int getNumChild() { result = count(this.getAChild()) } + /** Gets the number of child terms. */ + int getNumChild() { result = count(this.getAChild()) } - /** Gets the associated regex. */ - abstract Regex getRegex(); -} - -/** - * A string literal used as a regular expression. - * - * As an optimisation, only regexes containing an infinite repitition quatifier (`+`, `*`, or `{x,}`) - * and therefore may be relevant for ReDoS queries are considered. - */ -class RegExpLiteral extends TRegExpLiteral, RegExpParent { - Regex re; - - RegExpLiteral() { this = TRegExpLiteral(re) } - - override string toString() { result = re.toString() } - - override RegExpTerm getChild(int i) { i = 0 and result.getRegex() = re and result.isRootTerm() } - - /** Holds if dot, `.`, matches all characters, including newlines. */ - predicate isDotAll() { re.getAMode() = "DOTALL" } - - /** Holds if this regex matching is case-insensitive for this regex. */ - predicate isIgnoreCase() { re.getAMode() = "IGNORECASE" } - - /** Get a string representing all modes for this regex. */ - string getFlags() { result = concat(string mode | mode = re.getAMode() | mode, " | ") } - - override Regex getRegex() { result = re } - - /** Gets the primary QL class for this regex. */ - string getPrimaryQLClass() { result = "RegExpLiteral" } -} - -/** - * A regular expression term, that is, a syntactic part of a regular expression. - * These are the tree nodes that form the parse tree of a regular expression literal. - */ -class RegExpTerm extends RegExpParent { - Regex re; - int start; - int end; - - RegExpTerm() { - this = TRegExpAlt(re, start, end) - or - this = TRegExpBackRef(re, start, end) - or - this = TRegExpCharacterClass(re, start, end) - or - this = TRegExpCharacterRange(re, start, end) - or - this = TRegExpNormalChar(re, start, end) - or - this = TRegExpQuote(re, start, end) - or - this = TRegExpGroup(re, start, end) - or - this = TRegExpQuantifier(re, start, end) - or - this = TRegExpSequence(re, start, end) - or - this = TRegExpSpecialChar(re, start, end) + /** Gets the associated regex. */ + abstract Regex getRegex(); } /** - * Gets the outermost term of this regular expression. + * A string literal used as a regular expression. + * + * As an optimisation, only regexes containing an infinite repitition quatifier (`+`, `*`, or `{x,}`) + * and therefore may be relevant for ReDoS queries are considered. */ - RegExpTerm getRootTerm() { - this.isRootTerm() and result = this - or - result = this.getParent().(RegExpTerm).getRootTerm() + class RegExpLiteral extends TRegExpLiteral, RegExpParent { + Regex re; + + RegExpLiteral() { this = TRegExpLiteral(re) } + + override string toString() { result = re.toString() } + + override RegExpTerm getChild(int i) { i = 0 and result.getRegex() = re and result.isRootTerm() } + + /** Holds if dot, `.`, matches all characters, including newlines. */ + predicate isDotAll() { re.getAMode() = "DOTALL" } + + /** Holds if this regex matching is case-insensitive for this regex. */ + predicate isIgnoreCase() { re.getAMode() = "IGNORECASE" } + + /** Get a string representing all modes for this regex. */ + string getFlags() { result = concat(string mode | mode = re.getAMode() | mode, " | ") } + + override Regex getRegex() { result = re } + + /** Gets the primary QL class for this regex. */ + string getPrimaryQLClass() { result = "RegExpLiteral" } } /** - * Holds if this term is part of a string literal - * that is interpreted as a regular expression. + * A regular expression term, that is, a syntactic part of a regular expression. + * These are the tree nodes that form the parse tree of a regular expression literal. */ - predicate isUsedAsRegExp() { any() } + class RegExpTerm extends RegExpParent { + Regex re; + int start; + int end; - /** - * Holds if this is the root term of a regular expression. - */ - predicate isRootTerm() { start = 0 and end = re.getText().length() } + RegExpTerm() { + this = TRegExpAlt(re, start, end) + or + this = TRegExpBackRef(re, start, end) + or + this = TRegExpCharacterClass(re, start, end) + or + this = TRegExpCharacterRange(re, start, end) + or + this = TRegExpNormalChar(re, start, end) + or + this = TRegExpQuote(re, start, end) + or + this = TRegExpGroup(re, start, end) + or + this = TRegExpQuantifier(re, start, end) + or + this = TRegExpSequence(re, start, end) + or + this = TRegExpSpecialChar(re, start, end) + } - /** - * Gets the parent term of this regular expression term, or the - * regular expression literal if this is the root term. - */ - RegExpParent getParent() { result.getAChild() = this } - - override Regex getRegex() { result = re } - - /** Gets the offset at which this term starts. */ - int getStart() { result = start } - - /** Gets the offset at which this term ends. */ - int getEnd() { result = end } - - /** Holds if this term occurs in regex `inRe` offsets `startOffset` to `endOffset`. */ - predicate occursInRegex(Regex inRe, int startOffset, int endOffset) { - inRe = re and startOffset = start and endOffset = end - } - - override string toString() { result = re.getText().substring(start, end) } - - /** - * Gets the location of the surrounding regex, as locations inside the regex do not exist. - * To get location information corresponding to the term inside the regex, - * use `hasLocationInfo`. - */ - Location getLocation() { result = re.getLocation() } - - /** Holds if this term is found at the specified location offsets. */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - /* - * This is an approximation that handles the simple and common case of single, - * normal string literal written in the source, but does not give correct results in more complex cases - * such as compile-time concatenation, or multi-line string literals. + /** + * Gets the outermost term of this regular expression. */ - - exists(int re_start, int re_end, int src_start, int src_end | - re.getLocation().hasLocationInfo(filepath, startline, re_start, endline, re_end) and - re.sourceCharacter(start, src_start, _) and - re.sourceCharacter(end - 1, _, src_end) and - startcolumn = re_start + src_start and - endcolumn = re_start + src_end - 1 - ) - } - - /** Gets the file in which this term is found. */ - File getFile() { result = this.getLocation().getFile() } - - /** Gets the raw source text of this term. */ - string getRawValue() { result = this.toString() } - - /** Gets the string literal in which this term is found. */ - RegExpLiteral getLiteral() { result = TRegExpLiteral(re) } - - /** Gets the regular expression term that is matched (textually) before this one, if any. */ - RegExpTerm getPredecessor() { - exists(RegExpTerm parent | parent = this.getParent() | - result = parent.(RegExpSequence).previousElement(this) + RegExpTerm getRootTerm() { + this.isRootTerm() and result = this or - not exists(parent.(RegExpSequence).previousElement(this)) and - not parent instanceof RegExpSubPattern and - result = parent.getPredecessor() - ) + result = this.getParent().(RegExpTerm).getRootTerm() + } + + /** + * Holds if this term is part of a string literal + * that is interpreted as a regular expression. + */ + predicate isUsedAsRegExp() { any() } + + /** + * Holds if this is the root term of a regular expression. + */ + predicate isRootTerm() { start = 0 and end = re.getText().length() } + + /** + * Gets the parent term of this regular expression term, or the + * regular expression literal if this is the root term. + */ + RegExpParent getParent() { result.getAChild() = this } + + override Regex getRegex() { result = re } + + /** Gets the offset at which this term starts. */ + int getStart() { result = start } + + /** Gets the offset at which this term ends. */ + int getEnd() { result = end } + + /** Holds if this term occurs in regex `inRe` offsets `startOffset` to `endOffset`. */ + predicate occursInRegex(Regex inRe, int startOffset, int endOffset) { + inRe = re and startOffset = start and endOffset = end + } + + override string toString() { result = re.getText().substring(start, end) } + + /** + * Gets the location of the surrounding regex, as locations inside the regex do not exist. + * To get location information corresponding to the term inside the regex, + * use `hasLocationInfo`. + */ + Location getLocation() { result = re.getLocation() } + + /** Holds if this term is found at the specified location offsets. */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + /* + * This is an approximation that handles the simple and common case of single, + * normal string literal written in the source, but does not give correct results in more complex cases + * such as compile-time concatenation, or multi-line string literals. + */ + + exists(int re_start, int re_end, int src_start, int src_end | + re.getLocation().hasLocationInfo(filepath, startline, re_start, endline, re_end) and + re.sourceCharacter(start, src_start, _) and + re.sourceCharacter(end - 1, _, src_end) and + startcolumn = re_start + src_start and + endcolumn = re_start + src_end - 1 + ) + } + + /** Gets the file in which this term is found. */ + File getFile() { result = this.getLocation().getFile() } + + /** Gets the raw source text of this term. */ + string getRawValue() { result = this.toString() } + + /** Gets the string literal in which this term is found. */ + RegExpLiteral getLiteral() { result = TRegExpLiteral(re) } + + /** Gets the regular expression term that is matched (textually) before this one, if any. */ + RegExpTerm getPredecessor() { + exists(RegExpTerm parent | parent = this.getParent() | + result = parent.(RegExpSequence).previousElement(this) + or + not exists(parent.(RegExpSequence).previousElement(this)) and + not parent instanceof RegExpSubPattern and + result = parent.getPredecessor() + ) + } + + /** Gets the regular expression term that is matched (textually) after this one, if any. */ + RegExpTerm getSuccessor() { + exists(RegExpTerm parent | parent = this.getParent() | + result = parent.(RegExpSequence).nextElement(this) + or + not exists(parent.(RegExpSequence).nextElement(this)) and + not parent instanceof RegExpSubPattern and + result = parent.getSuccessor() + ) + } + + /** Gets the primary QL class for this term. */ + string getPrimaryQLClass() { result = "RegExpTerm" } } - /** Gets the regular expression term that is matched (textually) after this one, if any. */ - RegExpTerm getSuccessor() { - exists(RegExpTerm parent | parent = this.getParent() | - result = parent.(RegExpSequence).nextElement(this) - or - not exists(parent.(RegExpSequence).nextElement(this)) and - not parent instanceof RegExpSubPattern and - result = parent.getSuccessor() - ) - } - - /** Gets the primary QL class for this term. */ - string getPrimaryQLClass() { result = "RegExpTerm" } -} - -/** - * A quantified regular expression term. - * - * Example: - * - * ``` - * ((ECMA|Java)[sS]cript)* - * ``` - */ -class RegExpQuantifier extends RegExpTerm, TRegExpQuantifier { - int part_end; - boolean maybe_empty; - boolean may_repeat_forever; - - RegExpQuantifier() { - this = TRegExpQuantifier(re, start, end) and - re.quantifiedPart(start, part_end, end, maybe_empty, may_repeat_forever) - } - - override RegExpTerm getChild(int i) { - i = 0 and - result.occursInRegex(re, start, part_end) - } - - /** Holds if this term may match zero times. */ - predicate mayBeEmpty() { maybe_empty = true } - - /** Holds if this term may match an unlimited number of times. */ - predicate mayRepeatForever() { may_repeat_forever = true } - - /** Gets the quantifier for this term. That is e.g "?" for "a?". */ - string getQuantifier() { result = re.getText().substring(part_end, end) } - - /** Holds if this is a possessive quantifier, e.g. a*+. */ - predicate isPossessive() { - exists(string q | q = this.getQuantifier() | q.length() > 1 and q.charAt(q.length() - 1) = "+") - } - - override string getPrimaryQLClass() { result = "RegExpQuantifier" } -} - -/** - * A regular expression term that permits unlimited repetitions. - */ -class InfiniteRepetitionQuantifier extends RegExpQuantifier { - InfiniteRepetitionQuantifier() { this.mayRepeatForever() } -} - -/** - * A star-quantified term. - * - * Example: - * - * ``` - * \w* - * ``` - */ -class RegExpStar extends InfiniteRepetitionQuantifier { - RegExpStar() { this.getQuantifier().charAt(0) = "*" } - - override string getPrimaryQLClass() { result = "RegExpStar" } -} - -/** - * A plus-quantified term. - * - * Example: - * - * ``` - * \w+ - * ``` - */ -class RegExpPlus extends InfiniteRepetitionQuantifier { - RegExpPlus() { this.getQuantifier().charAt(0) = "+" } - - override string getPrimaryQLClass() { result = "RegExpPlus" } -} - -/** - * An optional term. - * - * Example: - * - * ``` - * ;? - * ``` - */ -class RegExpOpt extends RegExpQuantifier { - RegExpOpt() { this.getQuantifier().charAt(0) = "?" } - - override string getPrimaryQLClass() { result = "RegExpOpt" } -} - -/** - * A range-quantified term - * - * Examples: - * - * ``` - * \w{2,4} - * \w{2,} - * \w{2} - * ``` - */ -class RegExpRange extends RegExpQuantifier { - string upper; - string lower; - - RegExpRange() { re.multiples(part_end, end, lower, upper) } - - /** Gets the string defining the upper bound of this range, which is empty when no such bound exists. */ - string getUpper() { result = upper } - - /** Gets the string defining the lower bound of this range, which is empty when no such bound exists. */ - string getLower() { result = lower } - /** - * Gets the upper bound of the range, if any. + * A quantified regular expression term. * - * If there is no upper bound, any number of repetitions is allowed. - * For a term of the form `r{lo}`, both the lower and the upper bound - * are `lo`. + * Example: + * + * ``` + * ((ECMA|Java)[sS]cript)* + * ``` */ - int getUpperBound() { result = this.getUpper().toInt() } + class RegExpQuantifier extends RegExpTerm, TRegExpQuantifier { + int part_end; + boolean maybe_empty; + boolean may_repeat_forever; - /** Gets the lower bound of the range. */ - int getLowerBound() { result = this.getLower().toInt() } + RegExpQuantifier() { + this = TRegExpQuantifier(re, start, end) and + re.quantifiedPart(start, part_end, end, maybe_empty, may_repeat_forever) + } - override string getPrimaryQLClass() { result = "RegExpRange" } -} - -/** - * A sequence term. - * - * Example: - * - * ``` - * (ECMA|Java)Script - * ``` - * - * This is a sequence with the elements `(ECMA|Java)` and `Script`. - */ -class RegExpSequence extends RegExpTerm, TRegExpSequence { - RegExpSequence() { this = TRegExpSequence(re, start, end) } - - override RegExpTerm getChild(int i) { result = seqChild(re, start, end, i) } - - /** Gets the element preceding `element` in this sequence. */ - RegExpTerm previousElement(RegExpTerm element) { element = this.nextElement(result) } - - /** Gets the element following `element` in this sequence. */ - RegExpTerm nextElement(RegExpTerm element) { - exists(int i | - element = this.getChild(i) and - result = this.getChild(i + 1) - ) - } - - override string getPrimaryQLClass() { result = "RegExpSequence" } -} - -pragma[nomagic] -private int seqChildEnd(Regex re, int start, int end, int i) { - result = seqChild(re, start, end, i).getEnd() -} - -// moved out so we can use it in the charpred -private RegExpTerm seqChild(Regex re, int start, int end, int i) { - re.sequence(start, end) and - ( - i = 0 and - exists(int itemEnd | - re.item(start, itemEnd) and - result.occursInRegex(re, start, itemEnd) - ) - or - i > 0 and - exists(int itemStart, int itemEnd | itemStart = seqChildEnd(re, start, end, i - 1) | - re.item(itemStart, itemEnd) and - result.occursInRegex(re, itemStart, itemEnd) - ) - ) -} - -/** - * An alternative term, that is, a term of the form `a|b`. - * - * Example: - * - * ``` - * ECMA|Java - * ``` - */ -class RegExpAlt extends RegExpTerm, TRegExpAlt { - RegExpAlt() { this = TRegExpAlt(re, start, end) } - - override RegExpTerm getChild(int i) { - i = 0 and - exists(int part_end | - re.alternationOption(start, end, start, part_end) and + override RegExpTerm getChild(int i) { + i = 0 and result.occursInRegex(re, start, part_end) - ) - or - i > 0 and - exists(int part_start, int part_end | - part_start = this.getChild(i - 1).getEnd() + 1 // allow for the | - | - re.alternationOption(start, end, part_start, part_end) and - result.occursInRegex(re, part_start, part_end) - ) + } + + /** Holds if this term may match zero times. */ + predicate mayBeEmpty() { maybe_empty = true } + + /** Holds if this term may match an unlimited number of times. */ + predicate mayRepeatForever() { may_repeat_forever = true } + + /** Gets the quantifier for this term. That is e.g "?" for "a?". */ + string getQuantifier() { result = re.getText().substring(part_end, end) } + + /** Holds if this is a possessive quantifier, e.g. a*+. */ + predicate isPossessive() { + exists(string q | q = this.getQuantifier() | + q.length() > 1 and q.charAt(q.length() - 1) = "+" + ) + } + + override string getPrimaryQLClass() { result = "RegExpQuantifier" } } - override string getPrimaryQLClass() { result = "RegExpAlt" } -} - -/** - * An escaped regular expression term, that is, a regular expression - * term starting with a backslash, which is not a backreference. - * - * Example: - * - * ``` - * \. - * \w - * ``` - */ -class RegExpEscape extends RegExpNormalChar { - RegExpEscape() { re.escapedCharacter(start, end) } - /** - * Gets the name of the escaped; for example, `w` for `\w`. - * TODO: Handle named escapes. + * A regular expression term that permits unlimited repetitions. */ - override string getValue() { - not this.isUnicode() and - this.isIdentityEscape() and - result = this.getUnescaped() - or - this.getUnescaped() = "n" and result = "\n" - or - this.getUnescaped() = "r" and result = "\r" - or - this.getUnescaped() = "t" and result = "\t" - or - this.getUnescaped() = "f" and result = 12.toUnicode() // form feed - or - this.getUnescaped() = "a" and result = 7.toUnicode() // alert/bell - or - this.getUnescaped() = "e" and result = 27.toUnicode() // escape (0x1B) - or - this.isUnicode() and - result = this.getUnicode() - } - - /** Holds if this terms name is given by the part following the escape character. */ - predicate isIdentityEscape() { not this.getUnescaped() in ["n", "r", "t", "f", "a", "e"] } - - override string getPrimaryQLClass() { result = "RegExpEscape" } - - /** Gets the part of the term following the escape character. That is e.g. "w" if the term is "\w". */ - private string getUnescaped() { result = this.getText().suffix(1) } - - /** - * Gets the text for this escape. That is e.g. "\w". - */ - private string getText() { result = re.getText().substring(start, end) } - - /** - * Holds if this is a unicode escape. - */ - private predicate isUnicode() { this.getText().matches(["\\u%", "\\x%"]) } - - /** - * Gets the unicode char for this escape. - * E.g. for `\u0061` this returns "a". - */ - private string getUnicode() { - exists(int codepoint | codepoint = sum(this.getHexValueFromUnicode(_)) | - result = codepoint.toUnicode() - ) - } - - /** Gets the part of this escape that is a hexidecimal string */ - private string getHexString() { - this.isUnicode() and - if this.getText().matches("\\u%") // \uhhhh - then result = this.getText().suffix(2) - else - if this.getText().matches("\\x{%") // \x{h..h} - then result = this.getText().substring(3, this.getText().length() - 1) - else result = this.getText().suffix(2) // \xhh + class InfiniteRepetitionQuantifier extends RegExpQuantifier { + InfiniteRepetitionQuantifier() { this.mayRepeatForever() } } /** - * Gets int value for the `index`th char in the hex number of the unicode escape. - * E.g. for `\u0061` and `index = 2` this returns 96 (the number `6` interpreted as hex). - */ - private int getHexValueFromUnicode(int index) { - this.isUnicode() and - exists(string hex, string char | hex = this.getHexString() | - char = hex.charAt(index) and - result = 16.pow(hex.length() - index - 1) * toHex(char) - ) - } -} - -/** - * Gets the hex number for the `hex` char. - */ -private int toHex(string hex) { - result = [0 .. 9] and hex = result.toString() - or - result = 10 and hex = ["a", "A"] - or - result = 11 and hex = ["b", "B"] - or - result = 12 and hex = ["c", "C"] - or - result = 13 and hex = ["d", "D"] - or - result = 14 and hex = ["e", "E"] - or - result = 15 and hex = ["f", "F"] -} - -/** - * A character class escape in a regular expression. - * That is, an escaped character that denotes multiple characters. - * - * Examples: - * - * ``` - * \w - * \S - * ``` - */ -class RegExpCharacterClassEscape extends RegExpEscape { - RegExpCharacterClassEscape() { - this.getValue() in ["d", "D", "s", "S", "w", "W", "h", "H", "v", "V"] or - this.getValue().charAt(0) in ["p", "P"] - } - - override RegExpTerm getChild(int i) { none() } - - override string getPrimaryQLClass() { result = "RegExpCharacterClassEscape" } -} - -/** - * A named character class in a regular expression. - * - * Examples: - * - * ``` - * \p{Digit} - * \p{IsLowerCase} - */ -class RegExpNamedProperty extends RegExpCharacterClassEscape { - boolean inverted; - string name; - - RegExpNamedProperty() { - name = this.getValue().substring(2, this.getValue().length() - 1) and - ( - inverted = false and - this.getValue().charAt(0) = "p" - or - inverted = true and - this.getValue().charAt(0) = "P" - ) - } - - /** Holds if this class is inverted. */ - predicate isInverted() { inverted = true } - - /** Gets the name of this class. */ - string getClassName() { result = name } - - /** - * Gets an equivalent single-chcracter escape sequence for this class (e.g. \d) if possible, excluding the escape character. - */ - string getBackslashEquivalent() { - exists(string eq | if inverted = true then result = eq.toUpperCase() else result = eq | - name = ["Digit", "IsDigit"] and - eq = "d" - or - name = ["Space", "IsWhite_Space"] and - eq = "s" - ) - } -} - -/** - * A character class in a regular expression. - * - * Examples: - * - * ``` - * [a-z_] - * [^<>&] - * ``` - */ -class RegExpCharacterClass extends RegExpTerm, TRegExpCharacterClass { - RegExpCharacterClass() { this = TRegExpCharacterClass(re, start, end) } - - /** Holds if this character class is inverted, matching the opposite of its content. */ - predicate isInverted() { re.getChar(start + 1) = "^" } - - /** Holds if this character class can match anything. */ - predicate isUniversalClass() { - // [^] - this.isInverted() and not exists(this.getAChild()) - or - // [\w\W] and similar - not this.isInverted() and - exists(string cce1, string cce2 | - cce1 = this.getAChild().(RegExpCharacterClassEscape).getValue() and - cce2 = this.getAChild().(RegExpCharacterClassEscape).getValue() - | - cce1 != cce2 and cce1.toLowerCase() = cce2.toLowerCase() - ) - } - - override RegExpTerm getChild(int i) { - i = 0 and - exists(int itemStart, int itemEnd | - re.charSetStart(start, itemStart) and - re.charSetChild(start, itemStart, itemEnd) and - result.occursInRegex(re, itemStart, itemEnd) - ) - or - i > 0 and - exists(int itemStart, int itemEnd | itemStart = this.getChild(i - 1).getEnd() | - result.occursInRegex(re, itemStart, itemEnd) and - re.charSetChild(start, itemStart, itemEnd) - ) - } - - override string getPrimaryQLClass() { result = "RegExpCharacterClass" } -} - -/** - * A character range in a character class in a regular expression. - * - * Example: - * - * ``` - * a-z - * ``` - */ -class RegExpCharacterRange extends RegExpTerm, TRegExpCharacterRange { - int lower_end; - int upper_start; - - RegExpCharacterRange() { - this = TRegExpCharacterRange(re, start, end) and - re.charRange(_, start, lower_end, upper_start, end) - } - - /** Holds if this range goes from `lo` to `hi`, in effect is `lo-hi`. */ - predicate isRange(string lo, string hi) { - lo = re.getText().substring(start, lower_end) and - hi = re.getText().substring(upper_start, end) - } - - override RegExpTerm getChild(int i) { - i = 0 and - result.occursInRegex(re, start, lower_end) - or - i = 1 and - result.occursInRegex(re, upper_start, end) - } - - override string getPrimaryQLClass() { result = "RegExpCharacterRange" } -} - -/** - * A normal character in a regular expression, that is, a character - * without special meaning. This includes escaped characters. - * It also includes escape sequences that represent character classes. - * - * Examples: - * ``` - * t - * \t - * ``` - */ -class RegExpNormalChar extends RegExpTerm, TRegExpNormalChar { - RegExpNormalChar() { this = TRegExpNormalChar(re, start, end) } - - /** - * Holds if this constant represents a valid Unicode character (as opposed - * to a surrogate code point that does not correspond to a character by itself.) - */ - predicate isCharacter() { any() } - - /** Gets the string representation of the char matched by this term. */ - string getValue() { result = re.getText().substring(start, end) } - - override RegExpTerm getChild(int i) { none() } - - override string getPrimaryQLClass() { result = "RegExpNormalChar" } -} - -/** - * A quoted sequence. - * - * Example: - * ``` - * \Qabc\E - * ``` - */ -class RegExpQuote extends RegExpTerm, TRegExpQuote { - string value; - - RegExpQuote() { - exists(int inner_start, int inner_end | - this = TRegExpQuote(re, start, end) and - re.quote(start, end, inner_start, inner_end) and - value = re.getText().substring(inner_start, inner_end) - ) - } - - /** Gets the string matched by this quote term. */ - string getValue() { result = value } - - override string getPrimaryQLClass() { result = "RegExpQuote" } -} - -/** - * A constant regular expression term, that is, a regular expression - * term matching a single string. This can be a single character or a quoted sequence. - * - * Example: - * - * ``` - * a - * ``` - */ -class RegExpConstant extends RegExpTerm { - string value; - - RegExpConstant() { - (value = this.(RegExpNormalChar).getValue() or value = this.(RegExpQuote).getValue()) and - not this instanceof RegExpCharacterClassEscape - } - - /** - * Holds if this constant represents a valid Unicode character (as opposed - * to a surrogate code point that does not correspond to a character by itself.) - */ - predicate isCharacter() { any() } - - /** Gets the string matched by this constant term. */ - string getValue() { result = value } - - override RegExpTerm getChild(int i) { none() } - - override string getPrimaryQLClass() { result = "RegExpConstant" } -} - -/** - * A grouped regular expression. - * - * Examples: - * - * ``` - * (ECMA|Java) - * (?:ECMA|Java) - * (?['"]) - * ``` - */ -class RegExpGroup extends RegExpTerm, TRegExpGroup { - RegExpGroup() { this = TRegExpGroup(re, start, end) } - - /** - * Gets the index of this capture group within the enclosing regular - * expression literal. + * A star-quantified term. * - * For example, in the regular expression `/((a?).)(?:b)/`, the - * group `((a?).)` has index 1, the group `(a?)` nested inside it - * has index 2, and the group `(?:b)` has no index, since it is - * not a capture group. + * Example: + * + * ``` + * \w* + * ``` */ - int getNumber() { result = re.getGroupNumber(start, end) } + class RegExpStar extends InfiniteRepetitionQuantifier { + RegExpStar() { this.getQuantifier().charAt(0) = "*" } - /** Holds if this is a named capture group. */ - predicate isNamed() { exists(this.getName()) } + override string getPrimaryQLClass() { result = "RegExpStar" } + } - /** Gets the name of this capture group, if any. */ - string getName() { result = re.getGroupName(start, end) } + /** + * A plus-quantified term. + * + * Example: + * + * ``` + * \w+ + * ``` + */ + class RegExpPlus extends InfiniteRepetitionQuantifier { + RegExpPlus() { this.getQuantifier().charAt(0) = "+" } - override RegExpTerm getChild(int i) { - i = 0 and - exists(int in_start, int in_end | re.groupContents(start, end, in_start, in_end) | - result.occursInRegex(re, in_start, in_end) + override string getPrimaryQLClass() { result = "RegExpPlus" } + } + + /** + * An optional term. + * + * Example: + * + * ``` + * ;? + * ``` + */ + class RegExpOpt extends RegExpQuantifier { + RegExpOpt() { this.getQuantifier().charAt(0) = "?" } + + override string getPrimaryQLClass() { result = "RegExpOpt" } + } + + /** + * A range-quantified term + * + * Examples: + * + * ``` + * \w{2,4} + * \w{2,} + * \w{2} + * ``` + */ + class RegExpRange extends RegExpQuantifier { + string upper; + string lower; + + RegExpRange() { re.multiples(part_end, end, lower, upper) } + + /** Gets the string defining the upper bound of this range, which is empty when no such bound exists. */ + string getUpper() { result = upper } + + /** Gets the string defining the lower bound of this range, which is empty when no such bound exists. */ + string getLower() { result = lower } + + /** + * Gets the upper bound of the range, if any. + * + * If there is no upper bound, any number of repetitions is allowed. + * For a term of the form `r{lo}`, both the lower and the upper bound + * are `lo`. + */ + int getUpperBound() { result = this.getUpper().toInt() } + + /** Gets the lower bound of the range. */ + int getLowerBound() { result = this.getLower().toInt() } + + override string getPrimaryQLClass() { result = "RegExpRange" } + } + + /** + * A sequence term. + * + * Example: + * + * ``` + * (ECMA|Java)Script + * ``` + * + * This is a sequence with the elements `(ECMA|Java)` and `Script`. + */ + class RegExpSequence extends RegExpTerm, TRegExpSequence { + RegExpSequence() { this = TRegExpSequence(re, start, end) } + + override RegExpTerm getChild(int i) { result = seqChild(re, start, end, i) } + + /** Gets the element preceding `element` in this sequence. */ + RegExpTerm previousElement(RegExpTerm element) { element = this.nextElement(result) } + + /** Gets the element following `element` in this sequence. */ + RegExpTerm nextElement(RegExpTerm element) { + exists(int i | + element = this.getChild(i) and + result = this.getChild(i + 1) + ) + } + + override string getPrimaryQLClass() { result = "RegExpSequence" } + } + + pragma[nomagic] + private int seqChildEnd(Regex re, int start, int end, int i) { + result = seqChild(re, start, end, i).getEnd() + } + + // moved out so we can use it in the charpred + private RegExpTerm seqChild(Regex re, int start, int end, int i) { + re.sequence(start, end) and + ( + i = 0 and + exists(int itemEnd | + re.item(start, itemEnd) and + result.occursInRegex(re, start, itemEnd) + ) + or + i > 0 and + exists(int itemStart, int itemEnd | itemStart = seqChildEnd(re, start, end, i - 1) | + re.item(itemStart, itemEnd) and + result.occursInRegex(re, itemStart, itemEnd) + ) ) } - override string getPrimaryQLClass() { result = "RegExpGroup" } + /** + * An alternative term, that is, a term of the form `a|b`. + * + * Example: + * + * ``` + * ECMA|Java + * ``` + */ + class RegExpAlt extends RegExpTerm, TRegExpAlt { + RegExpAlt() { this = TRegExpAlt(re, start, end) } - /** Holds if this is the `n`th numbered group of literal `lit`. */ - predicate isNumberedGroupOfLiteral(RegExpLiteral lit, int n) { - lit = this.getLiteral() and n = this.getNumber() - } + override RegExpTerm getChild(int i) { + i = 0 and + exists(int part_end | + re.alternationOption(start, end, start, part_end) and + result.occursInRegex(re, start, part_end) + ) + or + i > 0 and + exists(int part_start, int part_end | + part_start = this.getChild(i - 1).getEnd() + 1 // allow for the | + | + re.alternationOption(start, end, part_start, part_end) and + result.occursInRegex(re, part_start, part_end) + ) + } - /** Holds if this is a group with name `name` of literal `lit`. */ - predicate isNamedGroupOfLiteral(RegExpLiteral lit, string name) { - lit = this.getLiteral() and name = this.getName() - } -} - -/** - * A special character in a regular expression. - * - * Examples: - * ``` - * ^ - * $ - * . - * ``` - */ -class RegExpSpecialChar extends RegExpTerm, TRegExpSpecialChar { - string char; - - RegExpSpecialChar() { - this = TRegExpSpecialChar(re, start, end) and - re.specialCharacter(start, end, char) + override string getPrimaryQLClass() { result = "RegExpAlt" } } /** - * Holds if this constant represents a valid Unicode character (as opposed - * to a surrogate code point that does not correspond to a character by itself.) + * An escaped regular expression term, that is, a regular expression + * term starting with a backslash, which is not a backreference. + * + * Example: + * + * ``` + * \. + * \w + * ``` */ - predicate isCharacter() { any() } + class RegExpEscape extends RegExpNormalChar { + RegExpEscape() { re.escapedCharacter(start, end) } - /** Gets the char for this term. */ - string getChar() { result = char } + /** + * Gets the name of the escaped; for example, `w` for `\w`. + * TODO: Handle named escapes. + */ + override string getValue() { + not this.isUnicode() and + this.isIdentityEscape() and + result = this.getUnescaped() + or + this.getUnescaped() = "n" and result = "\n" + or + this.getUnescaped() = "r" and result = "\r" + or + this.getUnescaped() = "t" and result = "\t" + or + this.getUnescaped() = "f" and result = 12.toUnicode() // form feed + or + this.getUnescaped() = "a" and result = 7.toUnicode() // alert/bell + or + this.getUnescaped() = "e" and result = 27.toUnicode() // escape (0x1B) + or + this.isUnicode() and + result = this.getUnicode() + } - override RegExpTerm getChild(int i) { none() } + /** Holds if this terms name is given by the part following the escape character. */ + predicate isIdentityEscape() { not this.getUnescaped() in ["n", "r", "t", "f", "a", "e"] } - override string getPrimaryQLClass() { result = "RegExpSpecialChar" } -} + override string getPrimaryQLClass() { result = "RegExpEscape" } -/** - * A dot regular expression. - * - * Example: - * - * ``` - * . - * ``` - */ -class RegExpDot extends RegExpSpecialChar { - RegExpDot() { this.getChar() = "." } + /** Gets the part of the term following the escape character. That is e.g. "w" if the term is "\w". */ + private string getUnescaped() { result = this.getText().suffix(1) } - override string getPrimaryQLClass() { result = "RegExpDot" } -} + /** + * Gets the text for this escape. That is e.g. "\w". + */ + private string getText() { result = re.getText().substring(start, end) } -/** - * A dollar assertion `$` matching the end of a line. - * - * Example: - * - * ``` - * $ - * ``` - */ -class RegExpDollar extends RegExpSpecialChar { - RegExpDollar() { this.getChar() = "$" } + /** + * Holds if this is a unicode escape. + */ + private predicate isUnicode() { this.getText().matches(["\\u%", "\\x%"]) } - override string getPrimaryQLClass() { result = "RegExpDollar" } -} + /** + * Gets the unicode char for this escape. + * E.g. for `\u0061` this returns "a". + */ + private string getUnicode() { + exists(int codepoint | codepoint = sum(this.getHexValueFromUnicode(_)) | + result = codepoint.toUnicode() + ) + } -/** - * A caret assertion `^` matching the beginning of a line. - * - * Example: - * - * ``` - * ^ - * ``` - */ -class RegExpCaret extends RegExpSpecialChar { - RegExpCaret() { this.getChar() = "^" } + /** Gets the part of this escape that is a hexidecimal string */ + private string getHexString() { + this.isUnicode() and + if this.getText().matches("\\u%") // \uhhhh + then result = this.getText().suffix(2) + else + if this.getText().matches("\\x{%") // \x{h..h} + then result = this.getText().substring(3, this.getText().length() - 1) + else result = this.getText().suffix(2) // \xhh + } - override string getPrimaryQLClass() { result = "RegExpCaret" } -} - -/** - * A zero-width match, that is, either an empty group or an assertion. - * - * Examples: - * ``` - * () - * (?=\w) - * ``` - */ -class RegExpZeroWidthMatch extends RegExpGroup { - RegExpZeroWidthMatch() { re.zeroWidthMatch(start, end) } - - override RegExpTerm getChild(int i) { none() } - - override string getPrimaryQLClass() { result = "RegExpZeroWidthMatch" } -} - -/** - * A zero-width lookahead or lookbehind assertion. - * - * Examples: - * - * ``` - * (?=\w) - * (?!\n) - * (?<=\.) - * (?` - * in a regular expression. - * - * Examples: - * - * ``` - * \1 - * (?P=quote) - * ``` - */ -class RegExpBackRef extends RegExpTerm, TRegExpBackRef { - RegExpBackRef() { this = TRegExpBackRef(re, start, end) } /** - * Gets the number of the capture group this back reference refers to, if any. + * Gets the hex number for the `hex` char. */ - int getNumber() { result = re.getBackrefNumber(start, end) } - - /** - * Gets the name of the capture group this back reference refers to, if any. - */ - string getName() { result = re.getBackrefName(start, end) } - - /** Gets the capture group this back reference refers to. */ - RegExpGroup getGroup() { - result.isNumberedGroupOfLiteral(this.getLiteral(), this.getNumber()) + private int toHex(string hex) { + result = [0 .. 9] and hex = result.toString() or - result.isNamedGroupOfLiteral(this.getLiteral(), this.getName()) + result = 10 and hex = ["a", "A"] + or + result = 11 and hex = ["b", "B"] + or + result = 12 and hex = ["c", "C"] + or + result = 13 and hex = ["d", "D"] + or + result = 14 and hex = ["e", "E"] + or + result = 15 and hex = ["f", "F"] } - override RegExpTerm getChild(int i) { none() } + /** + * A character class escape in a regular expression. + * That is, an escaped character that denotes multiple characters. + * + * Examples: + * + * ``` + * \w + * \S + * ``` + */ + class RegExpCharacterClassEscape extends RegExpEscape { + RegExpCharacterClassEscape() { + this.getValue() in ["d", "D", "s", "S", "w", "W", "h", "H", "v", "V"] or + this.getValue().charAt(0) in ["p", "P"] + } - override string getPrimaryQLClass() { result = "RegExpBackRef" } + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpCharacterClassEscape" } + } + + /** + * A named character class in a regular expression. + * + * Examples: + * + * ``` + * \p{Digit} + * \p{IsLowerCase} + */ + class RegExpNamedProperty extends RegExpCharacterClassEscape { + boolean inverted; + string name; + + RegExpNamedProperty() { + name = this.getValue().substring(2, this.getValue().length() - 1) and + ( + inverted = false and + this.getValue().charAt(0) = "p" + or + inverted = true and + this.getValue().charAt(0) = "P" + ) + } + + /** Holds if this class is inverted. */ + predicate isInverted() { inverted = true } + + /** Gets the name of this class. */ + string getClassName() { result = name } + + /** + * Gets an equivalent single-chcracter escape sequence for this class (e.g. \d) if possible, excluding the escape character. + */ + string getBackslashEquivalent() { + exists(string eq | if inverted = true then result = eq.toUpperCase() else result = eq | + name = ["Digit", "IsDigit"] and + eq = "d" + or + name = ["Space", "IsWhite_Space"] and + eq = "s" + ) + } + } + + /** + * A character class in a regular expression. + * + * Examples: + * + * ``` + * [a-z_] + * [^<>&] + * ``` + */ + class RegExpCharacterClass extends RegExpTerm, TRegExpCharacterClass { + RegExpCharacterClass() { this = TRegExpCharacterClass(re, start, end) } + + /** Holds if this character class is inverted, matching the opposite of its content. */ + predicate isInverted() { re.getChar(start + 1) = "^" } + + /** Holds if this character class can match anything. */ + predicate isUniversalClass() { + // [^] + this.isInverted() and not exists(this.getAChild()) + or + // [\w\W] and similar + not this.isInverted() and + exists(string cce1, string cce2 | + cce1 = this.getAChild().(RegExpCharacterClassEscape).getValue() and + cce2 = this.getAChild().(RegExpCharacterClassEscape).getValue() + | + cce1 != cce2 and cce1.toLowerCase() = cce2.toLowerCase() + ) + } + + override RegExpTerm getChild(int i) { + i = 0 and + exists(int itemStart, int itemEnd | + re.charSetStart(start, itemStart) and + re.charSetChild(start, itemStart, itemEnd) and + result.occursInRegex(re, itemStart, itemEnd) + ) + or + i > 0 and + exists(int itemStart, int itemEnd | itemStart = this.getChild(i - 1).getEnd() | + result.occursInRegex(re, itemStart, itemEnd) and + re.charSetChild(start, itemStart, itemEnd) + ) + } + + override string getPrimaryQLClass() { result = "RegExpCharacterClass" } + } + + /** + * A character range in a character class in a regular expression. + * + * Example: + * + * ``` + * a-z + * ``` + */ + class RegExpCharacterRange extends RegExpTerm, TRegExpCharacterRange { + int lower_end; + int upper_start; + + RegExpCharacterRange() { + this = TRegExpCharacterRange(re, start, end) and + re.charRange(_, start, lower_end, upper_start, end) + } + + /** Holds if this range goes from `lo` to `hi`, in effect is `lo-hi`. */ + predicate isRange(string lo, string hi) { + lo = re.getText().substring(start, lower_end) and + hi = re.getText().substring(upper_start, end) + } + + override RegExpTerm getChild(int i) { + i = 0 and + result.occursInRegex(re, start, lower_end) + or + i = 1 and + result.occursInRegex(re, upper_start, end) + } + + override string getPrimaryQLClass() { result = "RegExpCharacterRange" } + } + + /** + * A normal character in a regular expression, that is, a character + * without special meaning. This includes escaped characters. + * It also includes escape sequences that represent character classes. + * + * Examples: + * ``` + * t + * \t + * ``` + */ + class RegExpNormalChar extends RegExpTerm, TRegExpNormalChar { + RegExpNormalChar() { this = TRegExpNormalChar(re, start, end) } + + /** + * Holds if this constant represents a valid Unicode character (as opposed + * to a surrogate code point that does not correspond to a character by itself.) + */ + predicate isCharacter() { any() } + + /** Gets the string representation of the char matched by this term. */ + string getValue() { result = re.getText().substring(start, end) } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpNormalChar" } + } + + /** + * A quoted sequence. + * + * Example: + * ``` + * \Qabc\E + * ``` + */ + class RegExpQuote extends RegExpTerm, TRegExpQuote { + string value; + + RegExpQuote() { + exists(int inner_start, int inner_end | + this = TRegExpQuote(re, start, end) and + re.quote(start, end, inner_start, inner_end) and + value = re.getText().substring(inner_start, inner_end) + ) + } + + /** Gets the string matched by this quote term. */ + string getValue() { result = value } + + override string getPrimaryQLClass() { result = "RegExpQuote" } + } + + /** + * A constant regular expression term, that is, a regular expression + * term matching a single string. This can be a single character or a quoted sequence. + * + * Example: + * + * ``` + * a + * ``` + */ + class RegExpConstant extends RegExpTerm { + string value; + + RegExpConstant() { + (value = this.(RegExpNormalChar).getValue() or value = this.(RegExpQuote).getValue()) and + not this instanceof RegExpCharacterClassEscape + } + + /** + * Holds if this constant represents a valid Unicode character (as opposed + * to a surrogate code point that does not correspond to a character by itself.) + */ + predicate isCharacter() { any() } + + /** Gets the string matched by this constant term. */ + string getValue() { result = value } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpConstant" } + } + + /** + * A grouped regular expression. + * + * Examples: + * + * ``` + * (ECMA|Java) + * (?:ECMA|Java) + * (?['"]) + * ``` + */ + class RegExpGroup extends RegExpTerm, TRegExpGroup { + RegExpGroup() { this = TRegExpGroup(re, start, end) } + + /** + * Gets the index of this capture group within the enclosing regular + * expression literal. + * + * For example, in the regular expression `/((a?).)(?:b)/`, the + * group `((a?).)` has index 1, the group `(a?)` nested inside it + * has index 2, and the group `(?:b)` has no index, since it is + * not a capture group. + */ + int getNumber() { result = re.getGroupNumber(start, end) } + + /** Holds if this is a named capture group. */ + predicate isNamed() { exists(this.getName()) } + + /** Gets the name of this capture group, if any. */ + string getName() { result = re.getGroupName(start, end) } + + override RegExpTerm getChild(int i) { + i = 0 and + exists(int in_start, int in_end | re.groupContents(start, end, in_start, in_end) | + result.occursInRegex(re, in_start, in_end) + ) + } + + override string getPrimaryQLClass() { result = "RegExpGroup" } + + /** Holds if this is the `n`th numbered group of literal `lit`. */ + predicate isNumberedGroupOfLiteral(RegExpLiteral lit, int n) { + lit = this.getLiteral() and n = this.getNumber() + } + + /** Holds if this is a group with name `name` of literal `lit`. */ + predicate isNamedGroupOfLiteral(RegExpLiteral lit, string name) { + lit = this.getLiteral() and name = this.getName() + } + } + + /** + * A special character in a regular expression. + * + * Examples: + * ``` + * ^ + * $ + * . + * ``` + */ + class RegExpSpecialChar extends RegExpTerm, TRegExpSpecialChar { + string char; + + RegExpSpecialChar() { + this = TRegExpSpecialChar(re, start, end) and + re.specialCharacter(start, end, char) + } + + /** + * Holds if this constant represents a valid Unicode character (as opposed + * to a surrogate code point that does not correspond to a character by itself.) + */ + predicate isCharacter() { any() } + + /** Gets the char for this term. */ + string getChar() { result = char } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpSpecialChar" } + } + + /** + * A dot regular expression. + * + * Example: + * + * ``` + * . + * ``` + */ + class RegExpDot extends RegExpSpecialChar { + RegExpDot() { this.getChar() = "." } + + override string getPrimaryQLClass() { result = "RegExpDot" } + } + + /** + * A dollar assertion `$` matching the end of a line. + * + * Example: + * + * ``` + * $ + * ``` + */ + class RegExpDollar extends RegExpSpecialChar { + RegExpDollar() { this.getChar() = "$" } + + override string getPrimaryQLClass() { result = "RegExpDollar" } + } + + /** + * A caret assertion `^` matching the beginning of a line. + * + * Example: + * + * ``` + * ^ + * ``` + */ + class RegExpCaret extends RegExpSpecialChar { + RegExpCaret() { this.getChar() = "^" } + + override string getPrimaryQLClass() { result = "RegExpCaret" } + } + + /** + * A zero-width match, that is, either an empty group or an assertion. + * + * Examples: + * ``` + * () + * (?=\w) + * ``` + */ + class RegExpZeroWidthMatch extends RegExpGroup { + RegExpZeroWidthMatch() { re.zeroWidthMatch(start, end) } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpZeroWidthMatch" } + } + + /** + * A zero-width lookahead or lookbehind assertion. + * + * Examples: + * + * ``` + * (?=\w) + * (?!\n) + * (?<=\.) + * (?` + * in a regular expression. + * + * Examples: + * + * ``` + * \1 + * (?P=quote) + * ``` + */ + class RegExpBackRef extends RegExpTerm, TRegExpBackRef { + RegExpBackRef() { this = TRegExpBackRef(re, start, end) } + + /** + * Gets the number of the capture group this back reference refers to, if any. + */ + int getNumber() { result = re.getBackrefNumber(start, end) } + + /** + * Gets the name of the capture group this back reference refers to, if any. + */ + string getName() { result = re.getBackrefName(start, end) } + + /** Gets the capture group this back reference refers to. */ + RegExpGroup getGroup() { + result.isNumberedGroupOfLiteral(this.getLiteral(), this.getNumber()) + or + result.isNamedGroupOfLiteral(this.getLiteral(), this.getName()) + } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpBackRef" } + } } - -/** Gets the parse tree resulting from parsing `re`, if such has been constructed. */ -RegExpTerm getParsedRegExp(StringLiteral re) { result.getRegex() = re and result.isRootTerm() } From b737bdbca037dd178690c3245d1b0d3d5b08ed2e Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Tue, 1 Nov 2022 12:16:28 +0100 Subject: [PATCH 0247/1420] add a Java implementation of `RegexTreeViewSig` --- .../semmle/code/java/regex/RegexTreeView.qll | 91 ++++++++++++++++++- 1 file changed, 87 insertions(+), 4 deletions(-) diff --git a/java/ql/lib/semmle/code/java/regex/RegexTreeView.qll b/java/ql/lib/semmle/code/java/regex/RegexTreeView.qll index ceccd3efd5f..2eecff1e627 100644 --- a/java/ql/lib/semmle/code/java/regex/RegexTreeView.qll +++ b/java/ql/lib/semmle/code/java/regex/RegexTreeView.qll @@ -1,11 +1,19 @@ /** Provides a class hierarchy corresponding to a parse tree of regular expressions. */ -private import java -private import semmle.code.java.regex.regex +private import semmle.code.java.regex.regex as RE // importing under a namescape to avoid naming conflict for `Top`. +private import codeql.regex.nfa.NfaUtils as NfaUtils +// exporting as RegexTreeView, and in the top-level scope. +import Impl as RegexTreeView import Impl /** Gets the parse tree resulting from parsing `re`, if such has been constructed. */ -RegExpTerm getParsedRegExp(StringLiteral re) { result.getRegex() = re and result.isRootTerm() } +RegExpTerm getParsedRegExp(RE::StringLiteral re) { result.getRegex() = re and result.isRootTerm() } + +private class Regex = RE::Regex; + +private class Location = RE::Location; + +private class File = RE::File; /** * An element containing a regular expression term, that is, either @@ -53,7 +61,10 @@ private newtype TRegExpParent = /** A back reference */ TRegExpBackRef(Regex re, int start, int end) { re.backreference(start, end) } -module Impl { +private import codeql.regex.RegexTreeView + +/** An implementation that statisfies the RegexTreeView signature. */ +module Impl implements RegexTreeViewSig { /** * An element containing a regular expression term, that is, either * a string literal (parsed as a regular expression; the root of the parse tree) @@ -547,6 +558,13 @@ module Impl { } } + /** + * A word boundary, that is, a regular expression term of the form `\b`. + */ + class RegExpWordBoundary extends RegExpSpecialChar { + RegExpWordBoundary() { this.getChar() = "\\b" } + } + /** * Gets the hex number for the `hex` char. */ @@ -1088,4 +1106,69 @@ module Impl { override string getPrimaryQLClass() { result = "RegExpBackRef" } } + + class Top = RegExpParent; + + /** + * Holds if `term` is an escape class representing e.g. `\d`. + * `clazz` is which character class it represents, e.g. "d" for `\d`. + */ + predicate isEscapeClass(RegExpTerm term, string clazz) { + term.(RegExpCharacterClassEscape).getValue() = clazz + or + term.(RegExpNamedProperty).getBackslashEquivalent() = clazz + } + + /** + * Holds if `term` is a possessive quantifier, e.g. `a*+`. + */ + predicate isPossessive(RegExpQuantifier term) { term.isPossessive() } + + /** + * Holds if the regex that `term` is part of is used in a way that ignores any leading prefix of the input it's matched against. + */ + predicate matchesAnyPrefix(RegExpTerm term) { not term.getRegex().matchesFullString() } + + /** + * Holds if the regex that `term` is part of is used in a way that ignores any trailing suffix of the input it's matched against. + */ + predicate matchesAnySuffix(RegExpTerm term) { not term.getRegex().matchesFullString() } + + /** + * Holds if the regular expression should not be considered. + * + * We make the pragmatic performance optimization to ignore regular expressions in files + * that do not belong to the project code (such as installed dependencies). + */ + predicate isExcluded(RegExpParent parent) { + not exists(parent.getRegex().getLocation().getFile().getRelativePath()) + or + // Regexes with many occurrences of ".*" may cause the polynomial ReDoS computation to explode, so + // we explicitly exclude these. + strictcount(int i | exists(parent.getRegex().getText().regexpFind("\\.\\*", i, _)) | i) > 10 + } + + /** + * Holds if `root` has the `i` flag for case-insensitive matching. + */ + predicate isIgnoreCase(RegExpTerm root) { + root.isRootTerm() and + root.getLiteral().isIgnoreCase() + } + + /** + * Gets the flags for `root`, or the empty string if `root` has no flags. + */ + deprecated string getFlags(RegExpTerm root) { + root.isRootTerm() and + result = root.getLiteral().getFlags() + } + + /** + * Holds if `root` has the `s` flag for multi-line matching. + */ + predicate isDotAll(RegExpTerm root) { + root.isRootTerm() and + root.getLiteral().isDotAll() + } } From d5b066636f41c5e09f547b4a410d87ced9c24265 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Tue, 1 Nov 2022 12:17:46 +0100 Subject: [PATCH 0248/1420] use namespace in `PrintAst.qll` to avoid conflict with `Top` --- java/ql/lib/semmle/code/java/PrintAst.qll | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/java/ql/lib/semmle/code/java/PrintAst.qll b/java/ql/lib/semmle/code/java/PrintAst.qll index 4fe7e3f1ec5..4df23f4ec14 100644 --- a/java/ql/lib/semmle/code/java/PrintAst.qll +++ b/java/ql/lib/semmle/code/java/PrintAst.qll @@ -7,7 +7,7 @@ */ import java -import semmle.code.java.regex.RegexTreeView +import semmle.code.java.regex.RegexTreeView as RegexTreeView private newtype TPrintAstConfiguration = MkPrintAstConfiguration() @@ -134,8 +134,10 @@ private newtype TPrintAstNode = TImportsNode(CompilationUnit cu) { shouldPrint(cu, _) and exists(Import i | i.getCompilationUnit() = cu) } or - TRegExpTermNode(RegExpTerm term) { - exists(StringLiteral str | term.getRootTerm() = getParsedRegExp(str) and shouldPrint(str, _)) + TRegExpTermNode(RegexTreeView::RegExpTerm term) { + exists(StringLiteral str | + term.getRootTerm() = RegexTreeView::getParsedRegExp(str) and shouldPrint(str, _) + ) } /** @@ -316,7 +318,7 @@ final class StringLiteralNode extends ExprStmtNode { override PrintAstNode getChild(int childIndex) { childIndex = 0 and - result.(RegExpTermNode).getTerm() = getParsedRegExp(element) + result.(RegExpTermNode).getTerm() = RegexTreeView::getParsedRegExp(element) } } @@ -324,12 +326,12 @@ final class StringLiteralNode extends ExprStmtNode { * A node representing a regular expression term. */ class RegExpTermNode extends TRegExpTermNode, PrintAstNode { - RegExpTerm term; + RegexTreeView::RegExpTerm term; RegExpTermNode() { this = TRegExpTermNode(term) } /** Gets the `RegExpTerm` for this node. */ - RegExpTerm getTerm() { result = term } + RegexTreeView::RegExpTerm getTerm() { result = term } override PrintAstNode getChild(int childIndex) { result.(RegExpTermNode).getTerm() = term.getChild(childIndex) From c0290483066af7f687e79a6ee07f49170e594b30 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Tue, 1 Nov 2022 12:20:05 +0100 Subject: [PATCH 0249/1420] port the Java regex/redos queries to use the shared pack --- .../java/security/OverlyLargeRangeQuery.qll | 289 +--- .../regexp/ExponentialBackTracking.qll | 285 +--- .../code/java/security/regexp/NfaUtils.qll | 1333 +---------------- .../java/security/regexp/NfaUtilsSpecific.qll | 76 - .../security/regexp/PolynomialReDoSQuery.qll | 11 +- .../regexp/SuperlinearBackTracking.qll | 385 +---- .../Security/CWE/CWE-020/OverlyLargeRange.ql | 9 +- .../Security/CWE/CWE-730/PolynomialReDoS.ql | 4 +- java/ql/src/Security/CWE/CWE-730/ReDoS.ql | 8 +- .../regex/parser/RegexParseTests.ql | 12 +- .../security/CWE-730/PolynomialReDoS.ql | 6 +- .../query-tests/security/CWE-730/ReDoS.ql | 7 +- 12 files changed, 49 insertions(+), 2376 deletions(-) delete mode 100644 java/ql/lib/semmle/code/java/security/regexp/NfaUtilsSpecific.qll diff --git a/java/ql/lib/semmle/code/java/security/OverlyLargeRangeQuery.qll b/java/ql/lib/semmle/code/java/security/OverlyLargeRangeQuery.qll index 65e662f0bc5..06b538d4a63 100644 --- a/java/ql/lib/semmle/code/java/security/OverlyLargeRangeQuery.qll +++ b/java/ql/lib/semmle/code/java/security/OverlyLargeRangeQuery.qll @@ -2,288 +2,7 @@ * Classes and predicates for working with suspicious character ranges. */ -// We don't need the NFA utils, just the regexp tree. -// but the below is a nice shared library that exposes the API we need. -import regexp.NfaUtils - -/** - * Gets a rank for `range` that is unique for ranges in the same file. - * Prioritizes ranges that match more characters. - */ -int rankRange(RegExpCharacterRange range) { - range = - rank[result](RegExpCharacterRange r, Location l, int low, int high | - r.getLocation() = l and - isRange(r, low, high) - | - r order by (high - low) desc, l.getStartLine(), l.getStartColumn() - ) -} - -/** Holds if `range` spans from the unicode code points `low` to `high` (both inclusive). */ -predicate isRange(RegExpCharacterRange range, int low, int high) { - exists(string lowc, string highc | - range.isRange(lowc, highc) and - low.toUnicode() = lowc and - high.toUnicode() = highc - ) -} - -/** Holds if `char` is an alpha-numeric character. */ -predicate isAlphanumeric(string char) { - // written like this to avoid having a bindingset for the predicate - char = [[48 .. 57], [65 .. 90], [97 .. 122]].toUnicode() // 0-9, A-Z, a-z -} - -/** - * Holds if the given ranges are from the same character class - * and there exists at least one character matched by both ranges. - */ -predicate overlap(RegExpCharacterRange a, RegExpCharacterRange b) { - exists(RegExpCharacterClass clz | - a = clz.getAChild() and - b = clz.getAChild() and - a != b - | - exists(int alow, int ahigh, int blow, int bhigh | - isRange(a, alow, ahigh) and - isRange(b, blow, bhigh) and - alow <= bhigh and - blow <= ahigh - ) - ) -} - -/** - * Holds if `range` overlaps with the char class `escape` from the same character class. - */ -predicate overlapsWithCharEscape(RegExpCharacterRange range, RegExpCharacterClassEscape escape) { - exists(RegExpCharacterClass clz, string low, string high | - range = clz.getAChild() and - escape = clz.getAChild() and - range.isRange(low, high) - | - escape.getValue() = "w" and - getInRange(low, high).regexpMatch("\\w") - or - escape.getValue() = "d" and - getInRange(low, high).regexpMatch("\\d") - or - escape.getValue() = "s" and - getInRange(low, high).regexpMatch("\\s") - ) -} - -/** Gets the unicode code point for a `char`. */ -bindingset[char] -int toCodePoint(string char) { result.toUnicode() = char } - -/** A character range that appears to be overly wide. */ -class OverlyWideRange extends RegExpCharacterRange { - OverlyWideRange() { - exists(int low, int high, int numChars | - isRange(this, low, high) and - numChars = (1 + high - low) and - this.getRootTerm().isUsedAsRegExp() and - numChars >= 10 - | - // across the Z-a range (which includes backticks) - toCodePoint("Z") >= low and - toCodePoint("a") <= high - or - // across the 9-A range (which includes e.g. ; and ?) - toCodePoint("9") >= low and - toCodePoint("A") <= high - or - // a non-alphanumeric char as part of the range boundaries - exists(int bound | bound = [low, high] | not isAlphanumeric(bound.toUnicode())) and - // while still being ascii - low < 128 and - high < 128 - ) and - // allowlist for known ranges - not this = allowedWideRanges() - } - - /** Gets a string representation of a character class that matches the same chars as this range. */ - string printEquivalent() { result = RangePrinter::printEquivalentCharClass(this) } -} - -/** Gets a range that should not be reported as an overly wide range. */ -RegExpCharacterRange allowedWideRanges() { - // ~ is the last printable ASCII character, it's used right in various wide ranges. - result.isRange(_, "~") - or - // the same with " " and "!". " " is the first printable character, and "!" is the first non-white-space printable character. - result.isRange([" ", "!"], _) - or - // the `[@-_]` range is intentional - result.isRange("@", "_") - or - // starting from the zero byte is a good indication that it's purposely matching a large range. - result.isRange(0.toUnicode(), _) -} - -/** Gets a char between (and including) `low` and `high`. */ -bindingset[low, high] -private string getInRange(string low, string high) { - result = [toCodePoint(low) .. toCodePoint(high)].toUnicode() -} - -/** A module computing an equivalent character class for an overly wide range. */ -module RangePrinter { - bindingset[char] - bindingset[result] - private string next(string char) { - exists(int prev, int next | - prev.toUnicode() = char and - next.toUnicode() = result and - next = prev + 1 - ) - } - - /** Gets the points where the parts of the pretty printed range should be cut off. */ - private string cutoffs() { result = ["A", "Z", "a", "z", "0", "9"] } - - /** Gets the char to use in the low end of a range for a given `cut` */ - private string lowCut(string cut) { - cut = ["A", "a", "0"] and - result = cut - or - cut = ["Z", "z", "9"] and - result = next(cut) - } - - /** Gets the char to use in the high end of a range for a given `cut` */ - private string highCut(string cut) { - cut = ["Z", "z", "9"] and - result = cut - or - cut = ["A", "a", "0"] and - next(result) = cut - } - - /** Gets the cutoff char used for a given `part` of a range when pretty-printing it. */ - private string cutoff(OverlyWideRange range, int part) { - exists(int low, int high | isRange(range, low, high) | - result = - rank[part + 1](string cut | - cut = cutoffs() and low < toCodePoint(cut) and toCodePoint(cut) < high - | - cut order by toCodePoint(cut) - ) - ) - } - - /** Gets the number of parts we should print for a given `range`. */ - private int parts(OverlyWideRange range) { result = 1 + count(cutoff(range, _)) } - - /** Holds if the given part of a range should span from `low` to `high`. */ - private predicate part(OverlyWideRange range, int part, string low, string high) { - // first part. - part = 0 and - ( - range.isRange(low, high) and - parts(range) = 1 - or - parts(range) >= 2 and - range.isRange(low, _) and - high = highCut(cutoff(range, part)) - ) - or - // middle - part >= 1 and - part < parts(range) - 1 and - low = lowCut(cutoff(range, part - 1)) and - high = highCut(cutoff(range, part)) - or - // last. - part = parts(range) - 1 and - low = lowCut(cutoff(range, part - 1)) and - range.isRange(_, high) - } - - /** Gets an escaped `char` for use in a character class. */ - bindingset[char] - private string escape(string char) { - exists(string reg | reg = "(\\[|\\]|\\\\|-|/)" | - if char.regexpMatch(reg) then result = "\\" + char else result = char - ) - } - - /** Gets a part of the equivalent range. */ - private string printEquivalentCharClass(OverlyWideRange range, int part) { - exists(string low, string high | part(range, part, low, high) | - if - isAlphanumeric(low) and - isAlphanumeric(high) - then result = low + "-" + high - else - result = - strictconcat(string char | char = getInRange(low, high) | escape(char) order by char) - ) - } - - /** Gets the entire pretty printed equivalent range. */ - string printEquivalentCharClass(OverlyWideRange range) { - result = - strictconcat(string r, int part | - r = "[" and part = -1 and exists(range) - or - r = printEquivalentCharClass(range, part) - or - r = "]" and part = parts(range) - | - r order by part - ) - } -} - -/** Gets a char range that is overly large because of `reason`. */ -RegExpCharacterRange getABadRange(string reason, int priority) { - result instanceof OverlyWideRange and - priority = 0 and - exists(string equiv | equiv = result.(OverlyWideRange).printEquivalent() | - if equiv.length() <= 50 - then reason = "is equivalent to " + equiv - else reason = "is equivalent to " + equiv.substring(0, 50) + "..." - ) - or - priority = 1 and - exists(RegExpCharacterRange other | - reason = "overlaps with " + other + " in the same character class" and - rankRange(result) < rankRange(other) and - overlap(result, other) - ) - or - priority = 2 and - exists(RegExpCharacterClassEscape escape | - reason = "overlaps with " + escape + " in the same character class" and - overlapsWithCharEscape(result, escape) - ) - or - reason = "is empty" and - priority = 3 and - exists(int low, int high | - isRange(result, low, high) and - low > high - ) -} - -/** Holds if `range` matches suspiciously many characters. */ -predicate problem(RegExpCharacterRange range, string reason) { - reason = - strictconcat(string m, int priority | - range = getABadRange(m, priority) - | - m, ", and " order by priority desc - ) and - // specifying a range using an escape is usually OK. - not range.getAChild() instanceof RegExpEscape and - // Unicode escapes in strings are interpreted before it turns into a regexp, - // so e.g. [\u0001-\uFFFF] will just turn up as a range between two constants. - // We therefore exclude these ranges. - range.getRootTerm().getParent() instanceof RegExpLiteral and - // is used as regexp (mostly for JS where regular expressions are parsed eagerly) - range.getRootTerm().isUsedAsRegExp() -} +private import semmle.code.java.regex.RegexTreeView::RegexTreeView as TreeView +// OverlyLargeRangeQuery should be used directly from the shared pack, and not from this file. +deprecated import codeql.regex.OverlyLargeRangeQuery::Make as Dep +import Dep diff --git a/java/ql/lib/semmle/code/java/security/regexp/ExponentialBackTracking.qll b/java/ql/lib/semmle/code/java/security/regexp/ExponentialBackTracking.qll index 4a608890249..d0a08dc88bf 100644 --- a/java/ql/lib/semmle/code/java/security/regexp/ExponentialBackTracking.qll +++ b/java/ql/lib/semmle/code/java/security/regexp/ExponentialBackTracking.qll @@ -62,284 +62,7 @@ * a suffix `x` (possible empty) that is most likely __not__ accepted. */ -import NfaUtils - -/** - * Holds if state `s` might be inside a backtracking repetition. - */ -pragma[noinline] -private predicate stateInsideBacktracking(State s) { - s.getRepr().getParent*() instanceof MaybeBacktrackingRepetition -} - -/** - * A infinitely repeating quantifier that might backtrack. - */ -private class MaybeBacktrackingRepetition extends InfiniteRepetitionQuantifier { - MaybeBacktrackingRepetition() { - exists(RegExpTerm child | - child instanceof RegExpAlt or - child instanceof RegExpQuantifier - | - child.getParent+() = this - ) - } -} - -/** - * A state in the product automaton. - */ -private newtype TStatePair = - /** - * We lazily only construct those states that we are actually - * going to need: `(q, q)` for every fork state `q`, and any - * pair of states that can be reached from a pair that we have - * already constructed. To cut down on the number of states, - * we only represent states `(q1, q2)` where `q1` is lexicographically - * no bigger than `q2`. - * - * States are only constructed if both states in the pair are - * inside a repetition that might backtrack. - */ - MkStatePair(State q1, State q2) { - isFork(q1, _, _, _, _) and q2 = q1 - or - (step(_, _, _, q1, q2) or step(_, _, _, q2, q1)) and - rankState(q1) <= rankState(q2) - } - -/** - * Gets a unique number for a `state`. - * Is used to create an ordering of states, where states with the same `toString()` will be ordered differently. - */ -private int rankState(State state) { - state = - rank[result](State s, Location l | - stateInsideBacktracking(s) and - l = s.getRepr().getLocation() - | - s order by l.getStartLine(), l.getStartColumn(), s.toString() - ) -} - -/** - * A state in the product automaton. - */ -private class StatePair extends TStatePair { - State q1; - State q2; - - StatePair() { this = MkStatePair(q1, q2) } - - /** Gets a textual representation of this element. */ - string toString() { result = "(" + q1 + ", " + q2 + ")" } - - /** Gets the first component of the state pair. */ - State getLeft() { result = q1 } - - /** Gets the second component of the state pair. */ - State getRight() { result = q2 } -} - -/** - * Holds for `(fork, fork)` state pairs when `isFork(fork, _, _, _, _)` holds. - * - * Used in `statePairDistToFork` - */ -private predicate isStatePairFork(StatePair p) { - exists(State fork | p = MkStatePair(fork, fork) and isFork(fork, _, _, _, _)) -} - -/** - * Holds if there are transitions from the components of `q` to the corresponding - * components of `r`. - * - * Used in `statePairDistToFork` - */ -private predicate reverseStep(StatePair r, StatePair q) { step(q, _, _, r) } - -/** - * Gets the minimum length of a path from `q` to `r` in the - * product automaton. - */ -private int statePairDistToFork(StatePair q, StatePair r) = - shortestDistances(isStatePairFork/1, reverseStep/2)(r, q, result) - -/** - * Holds if there are transitions from `q` to `r1` and from `q` to `r2` - * labelled with `s1` and `s2`, respectively, where `s1` and `s2` do not - * trivially have an empty intersection. - * - * This predicate only holds for states associated with regular expressions - * that have at least one repetition quantifier in them (otherwise the - * expression cannot be vulnerable to ReDoS attacks anyway). - */ -pragma[noopt] -private predicate isFork(State q, InputSymbol s1, InputSymbol s2, State r1, State r2) { - stateInsideBacktracking(q) and - exists(State q1, State q2 | - q1 = epsilonSucc*(q) and - delta(q1, s1, r1) and - q2 = epsilonSucc*(q) and - delta(q2, s2, r2) and - // Use pragma[noopt] to prevent intersect(s1,s2) from being the starting point of the join. - // From (s1,s2) it would find a huge number of intermediate state pairs (q1,q2) originating from different literals, - // and discover at the end that no `q` can reach both `q1` and `q2` by epsilon transitions. - exists(intersect(s1, s2)) - | - s1 != s2 - or - r1 != r2 - or - r1 = r2 and q1 != q2 - or - // If q can reach itself by epsilon transitions, then there are two distinct paths to the q1/q2 state: - // one that uses the loop and one that doesn't. The engine will separately attempt to match with each path, - // despite ending in the same state. The "fork" thus arises from the choice of whether to use the loop or not. - // To avoid every state in the loop becoming a fork state, - // we arbitrarily pick the InfiniteRepetitionQuantifier state as the canonical fork state for the loop - // (every epsilon-loop must contain such a state). - // - // We additionally require that the there exists another InfiniteRepetitionQuantifier `mid` on the path from `q` to itself. - // This is done to avoid flagging regular expressions such as `/(a?)*b/` - that only has polynomial runtime, and is detected by `js/polynomial-redos`. - // The below code is therefore a heuristic, that only flags regular expressions such as `/(a*)*b/`, - // and does not flag regular expressions such as `/(a?b?)c/`, but the latter pattern is not used frequently. - r1 = r2 and - q1 = q2 and - epsilonSucc+(q) = q and - exists(RegExpTerm term | term = q.getRepr() | term instanceof InfiniteRepetitionQuantifier) and - // One of the mid states is an infinite quantifier itself - exists(State mid, RegExpTerm term | - mid = epsilonSucc+(q) and - term = mid.getRepr() and - term instanceof InfiniteRepetitionQuantifier and - q = epsilonSucc+(mid) and - not mid = q - ) - ) and - stateInsideBacktracking(r1) and - stateInsideBacktracking(r2) -} - -/** - * Gets the state pair `(q1, q2)` or `(q2, q1)`; note that only - * one or the other is defined. - */ -private StatePair mkStatePair(State q1, State q2) { - result = MkStatePair(q1, q2) or result = MkStatePair(q2, q1) -} - -/** - * Holds if there are transitions from the components of `q` to the corresponding - * components of `r` labelled with `s1` and `s2`, respectively. - */ -private predicate step(StatePair q, InputSymbol s1, InputSymbol s2, StatePair r) { - exists(State r1, State r2 | step(q, s1, s2, r1, r2) and r = mkStatePair(r1, r2)) -} - -/** - * Holds if there are transitions from the components of `q` to `r1` and `r2` - * labelled with `s1` and `s2`, respectively. - * - * We only consider transitions where the resulting states `(r1, r2)` are both - * inside a repetition that might backtrack. - */ -pragma[noopt] -private predicate step(StatePair q, InputSymbol s1, InputSymbol s2, State r1, State r2) { - exists(State q1, State q2 | q.getLeft() = q1 and q.getRight() = q2 | - deltaClosed(q1, s1, r1) and - deltaClosed(q2, s2, r2) and - // use noopt to force the join on `intersect` to happen last. - exists(intersect(s1, s2)) - ) and - stateInsideBacktracking(r1) and - stateInsideBacktracking(r2) -} - -private newtype TTrace = - Nil() or - Step(InputSymbol s1, InputSymbol s2, TTrace t) { isReachableFromFork(_, _, s1, s2, t, _) } - -/** - * A list of pairs of input symbols that describe a path in the product automaton - * starting from some fork state. - */ -private class Trace extends TTrace { - /** Gets a textual representation of this element. */ - string toString() { - this = Nil() and result = "Nil()" - or - exists(InputSymbol s1, InputSymbol s2, Trace t | this = Step(s1, s2, t) | - result = "Step(" + s1 + ", " + s2 + ", " + t + ")" - ) - } -} - -/** - * Holds if `r` is reachable from `(fork, fork)` under input `w`, and there is - * a path from `r` back to `(fork, fork)` with `rem` steps. - */ -private predicate isReachableFromFork(State fork, StatePair r, Trace w, int rem) { - exists(InputSymbol s1, InputSymbol s2, Trace v | - isReachableFromFork(fork, r, s1, s2, v, rem) and - w = Step(s1, s2, v) - ) -} - -private predicate isReachableFromFork( - State fork, StatePair r, InputSymbol s1, InputSymbol s2, Trace v, int rem -) { - // base case - exists(State q1, State q2 | - isFork(fork, s1, s2, q1, q2) and - r = MkStatePair(q1, q2) and - v = Nil() and - rem = statePairDistToFork(r, MkStatePair(fork, fork)) - ) - or - // recursive case - exists(StatePair p | - isReachableFromFork(fork, p, v, rem + 1) and - step(p, s1, s2, r) and - rem = statePairDistToFork(r, MkStatePair(fork, fork)) - ) -} - -/** - * Gets a state in the product automaton from which `(fork, fork)` is - * reachable in zero or more epsilon transitions. - */ -private StatePair getAForkPair(State fork) { - isFork(fork, _, _, _, _) and - result = MkStatePair(epsilonPred*(fork), epsilonPred*(fork)) -} - -/** An implementation of a chain containing chars for use by `Concretizer`. */ -private module CharTreeImpl implements CharTree { - class CharNode = Trace; - - CharNode getPrev(CharNode t) { t = Step(_, _, result) } - - /** Holds if `n` is a trace that is used by `concretize` in `isPumpable`. */ - predicate isARelevantEnd(CharNode n) { - exists(State f | isReachableFromFork(f, getAForkPair(f), n, _)) - } - - string getChar(CharNode t) { - exists(InputSymbol s1, InputSymbol s2 | t = Step(s1, s2, _) | result = intersect(s1, s2)) - } -} - -/** - * Holds if `fork` is a pumpable fork with word `w`. - */ -private predicate isPumpable(State fork, string w) { - exists(StatePair q, Trace t | - isReachableFromFork(fork, q, t, _) and - q = getAForkPair(fork) and - w = Concretizer::concretize(t) - ) -} - -/** Holds if `state` has exponential ReDoS */ -predicate hasReDoSResult = ReDoSPruning::hasReDoSResult/4; +private import semmle.code.java.regex.RegexTreeView::RegexTreeView as TreeView +// ExponentialBackTracking should be used directly from the shared pack, and not from this file. +deprecated private import codeql.regex.nfa.ExponentialBackTracking::Make as Dep +import Dep diff --git a/java/ql/lib/semmle/code/java/security/regexp/NfaUtils.qll b/java/ql/lib/semmle/code/java/security/regexp/NfaUtils.qll index 5ff0cb6a39e..3b69ecc7120 100644 --- a/java/ql/lib/semmle/code/java/security/regexp/NfaUtils.qll +++ b/java/ql/lib/semmle/code/java/security/regexp/NfaUtils.qll @@ -7,1332 +7,7 @@ * other queries that benefit from reasoning about NFAs. */ -import NfaUtilsSpecific - -/** - * Gets the char after `c` (from a simplified ASCII table). - */ -private string nextChar(string c) { exists(int code | code = ascii(c) | code + 1 = ascii(result)) } - -/** - * Gets an approximation for the ASCII code for `char`. - * Only the easily printable chars are included (so no newline, tab, null, etc). - */ -private int ascii(string char) { - char = - rank[result](string c | - c = - "! \"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" - .charAt(_) - ) -} - -/** - * Holds if `t` matches at least an epsilon symbol. - * - * That is, this term does not restrict the language of the enclosing regular expression. - * - * This is implemented as an under-approximation, and this predicate does not hold for sub-patterns in particular. - */ -predicate matchesEpsilon(RegExpTerm t) { - t instanceof RegExpStar - or - t instanceof RegExpOpt - or - t.(RegExpRange).getLowerBound() = 0 - or - exists(RegExpTerm child | - child = t.getAChild() and - matchesEpsilon(child) - | - t instanceof RegExpAlt or - t instanceof RegExpGroup or - t instanceof RegExpPlus or - t instanceof RegExpRange - ) - or - matchesEpsilon(t.(RegExpBackRef).getGroup()) - or - forex(RegExpTerm child | child = t.(RegExpSequence).getAChild() | matchesEpsilon(child)) -} - -/** - * A lookahead/lookbehind that matches the empty string. - */ -class EmptyPositiveSubPattern extends RegExpSubPattern { - EmptyPositiveSubPattern() { - ( - this instanceof RegExpPositiveLookahead - or - this instanceof RegExpPositiveLookbehind - ) and - matchesEpsilon(this.getOperand()) - } -} - -/** DEPRECATED: Use `EmptyPositiveSubPattern` instead. */ -deprecated class EmptyPositiveSubPatttern = EmptyPositiveSubPattern; - -/** - * A branch in a disjunction that is the root node in a literal, or a literal - * whose root node is not a disjunction. - */ -class RegExpRoot extends RegExpTerm { - RegExpRoot() { - exists(RegExpParent parent | - exists(RegExpAlt alt | - alt.isRootTerm() and - this = alt.getAChild() and - parent = alt.getParent() - ) - or - this.isRootTerm() and - not this instanceof RegExpAlt and - parent = this.getParent() - ) - } - - /** - * Holds if this root term is relevant to the ReDoS analysis. - */ - predicate isRelevant() { - // is actually used as a RegExp - this.isUsedAsRegExp() and - // not excluded for library specific reasons - not isExcluded(this.getRootTerm().getParent()) - } -} - -/** - * A constant in a regular expression that represents valid Unicode character(s). - */ -private class RegexpCharacterConstant extends RegExpConstant { - RegexpCharacterConstant() { this.isCharacter() } -} - -/** - * A regexp term that is relevant for this ReDoS analysis. - */ -class RelevantRegExpTerm extends RegExpTerm { - RelevantRegExpTerm() { getRoot(this).isRelevant() } -} - -/** - * Holds if `term` is the chosen canonical representative for all terms with string representation `str`. - * The string representation includes which flags are used with the regular expression. - * - * Using canonical representatives gives a huge performance boost when working with tuples containing multiple `InputSymbol`s. - * The number of `InputSymbol`s is decreased by 3 orders of magnitude or more in some larger benchmarks. - */ -private predicate isCanonicalTerm(RelevantRegExpTerm term, string str) { - term = - min(RelevantRegExpTerm t, Location loc, File file | - loc = t.getLocation() and - file = t.getFile() and - str = getCanonicalizationString(t) - | - t order by t.getFile().getRelativePath(), loc.getStartLine(), loc.getStartColumn() - ) -} - -/** - * Gets a string representation of `term` that is used for canonicalization. - */ -private string getCanonicalizationString(RelevantRegExpTerm term) { - exists(string ignoreCase | - (if RegExpFlags::isIgnoreCase(term.getRootTerm()) then ignoreCase = "i" else ignoreCase = "") and - result = term.getRawValue() + "|" + ignoreCase - ) -} - -/** - * An abstract input symbol, representing a set of concrete characters. - */ -private newtype TInputSymbol = - /** An input symbol corresponding to character `c`. */ - Char(string c) { - c = - any(RegexpCharacterConstant cc | - cc instanceof RelevantRegExpTerm and - not RegExpFlags::isIgnoreCase(cc.getRootTerm()) - ).getValue().charAt(_) - or - // normalize everything to lower case if the regexp is case insensitive - c = - any(RegexpCharacterConstant cc, string char | - cc instanceof RelevantRegExpTerm and - RegExpFlags::isIgnoreCase(cc.getRootTerm()) and - char = cc.getValue().charAt(_) - | - char.toLowerCase() - ) - } or - /** - * An input symbol representing all characters matched by - * a (non-universal) character class that has string representation `charClassString`. - */ - CharClass(string charClassString) { - exists(RelevantRegExpTerm recc | isCanonicalTerm(recc, charClassString) | - recc instanceof RegExpCharacterClass and - not recc.(RegExpCharacterClass).isUniversalClass() - or - isEscapeClass(recc, _) - ) - } or - /** An input symbol representing all characters matched by `.`. */ - Dot() or - /** An input symbol representing all characters. */ - Any() or - /** An epsilon transition in the automaton. */ - Epsilon() - -/** - * Gets the the CharClass corresponding to the canonical representative `term`. - */ -private CharClass getCharClassForCanonicalTerm(RegExpTerm term) { - exists(string str | isCanonicalTerm(term, str) | result = CharClass(str)) -} - -/** - * Gets a char class that represents `term`, even when `term` is not the canonical representative. - */ -CharacterClass getCanonicalCharClass(RegExpTerm term) { - exists(string str | str = getCanonicalizationString(term) and result = CharClass(str)) -} - -/** - * Holds if `a` and `b` are input symbols from the same regexp. - */ -private predicate sharesRoot(InputSymbol a, InputSymbol b) { - exists(RegExpRoot root | - belongsTo(a, root) and - belongsTo(b, root) - ) -} - -/** - * Holds if the `a` is an input symbol from a regexp that has root `root`. - */ -private predicate belongsTo(InputSymbol a, RegExpRoot root) { - exists(State s | getRoot(s.getRepr()) = root | - delta(s, a, _) - or - delta(_, a, s) - ) -} - -/** - * An abstract input symbol, representing a set of concrete characters. - */ -class InputSymbol extends TInputSymbol { - InputSymbol() { not this instanceof Epsilon } - - /** - * Gets a string representation of this input symbol. - */ - string toString() { - this = Char(result) - or - this = CharClass(result) - or - this = Dot() and result = "." - or - this = Any() and result = "[^]" - } -} - -/** - * An abstract input symbol that represents a character class. - */ -abstract class CharacterClass extends InputSymbol { - /** - * Gets a character that is relevant for intersection-tests involving this - * character class. - * - * Specifically, this is any of the characters mentioned explicitly in the - * character class, offset by one if it is inverted. For character class escapes, - * the result is as if the class had been written out as a series of intervals. - * - * This set is large enough to ensure that for any two intersecting character - * classes, one contains a relevant character from the other. - */ - abstract string getARelevantChar(); - - /** - * Holds if this character class matches `char`. - */ - bindingset[char] - abstract predicate matches(string char); - - /** - * Gets a character matched by this character class. - */ - string choose() { result = this.getARelevantChar() and this.matches(result) } -} - -/** - * Provides implementations for `CharacterClass`. - */ -private module CharacterClasses { - /** - * Holds if the character class `cc` has a child (constant or range) that matches `char`. - */ - pragma[noinline] - predicate hasChildThatMatches(RegExpCharacterClass cc, string char) { - if RegExpFlags::isIgnoreCase(cc.getRootTerm()) - then - // normalize everything to lower case if the regexp is case insensitive - exists(string c | hasChildThatMatchesIgnoringCasingFlags(cc, c) | char = c.toLowerCase()) - else hasChildThatMatchesIgnoringCasingFlags(cc, char) - } - - /** - * Holds if the character class `cc` has a child (constant or range) that matches `char`. - * Ignores whether the character class is inside a regular expression that has the ignore case flag. - */ - pragma[noinline] - predicate hasChildThatMatchesIgnoringCasingFlags(RegExpCharacterClass cc, string char) { - exists(getCharClassForCanonicalTerm(cc)) and - exists(RegExpTerm child | child = cc.getAChild() | - char = child.(RegexpCharacterConstant).getValue() - or - rangeMatchesOnLetterOrDigits(child, char) - or - not rangeMatchesOnLetterOrDigits(child, _) and - char = getARelevantChar() and - exists(string lo, string hi | child.(RegExpCharacterRange).isRange(lo, hi) | - lo <= char and - char <= hi - ) - or - exists(string charClass | isEscapeClass(child, charClass) | - charClass.toLowerCase() = charClass and - classEscapeMatches(charClass, char) - or - char = getARelevantChar() and - charClass.toUpperCase() = charClass and - not classEscapeMatches(charClass, char) - ) - ) - } - - /** - * Holds if `range` is a range on lower-case, upper-case, or digits, and matches `char`. - * This predicate is used to restrict the searchspace for ranges by only joining `getAnyPossiblyMatchedChar` - * on a few ranges. - */ - private predicate rangeMatchesOnLetterOrDigits(RegExpCharacterRange range, string char) { - exists(string lo, string hi | - range.isRange(lo, hi) and lo = lowercaseLetter() and hi = lowercaseLetter() - | - lo <= char and - char <= hi and - char = lowercaseLetter() - ) - or - exists(string lo, string hi | - range.isRange(lo, hi) and lo = upperCaseLetter() and hi = upperCaseLetter() - | - lo <= char and - char <= hi and - char = upperCaseLetter() - ) - or - exists(string lo, string hi | range.isRange(lo, hi) and lo = digit() and hi = digit() | - lo <= char and - char <= hi and - char = digit() - ) - } - - private string lowercaseLetter() { result = "abcdefghijklmnopqrstuvwxyz".charAt(_) } - - private string upperCaseLetter() { result = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".charAt(_) } - - private string digit() { result = [0 .. 9].toString() } - - /** - * Gets a char that could be matched by a regular expression. - * Includes all printable ascii chars, all constants mentioned in a regexp, and all chars matches by the regexp `/\s|\d|\w/`. - */ - string getARelevantChar() { - exists(ascii(result)) - or - exists(RegexpCharacterConstant c | result = c.getValue().charAt(_)) - or - classEscapeMatches(_, result) - } - - /** - * Gets a char that is mentioned in the character class `c`. - */ - private string getAMentionedChar(RegExpCharacterClass c) { - exists(RegExpTerm child | child = c.getAChild() | - result = child.(RegexpCharacterConstant).getValue() - or - child.(RegExpCharacterRange).isRange(result, _) - or - child.(RegExpCharacterRange).isRange(_, result) - or - exists(string charClass | isEscapeClass(child, charClass) | - result = min(string s | classEscapeMatches(charClass.toLowerCase(), s)) - or - result = max(string s | classEscapeMatches(charClass.toLowerCase(), s)) - ) - ) - } - - bindingset[char, cc] - private string caseNormalize(string char, RegExpTerm cc) { - if RegExpFlags::isIgnoreCase(cc.getRootTerm()) - then result = char.toLowerCase() - else result = char - } - - /** - * An implementation of `CharacterClass` for positive (non inverted) character classes. - */ - private class PositiveCharacterClass extends CharacterClass { - RegExpCharacterClass cc; - - PositiveCharacterClass() { this = getCharClassForCanonicalTerm(cc) and not cc.isInverted() } - - override string getARelevantChar() { result = caseNormalize(getAMentionedChar(cc), cc) } - - override predicate matches(string char) { hasChildThatMatches(cc, char) } - } - - /** - * An implementation of `CharacterClass` for inverted character classes. - */ - private class InvertedCharacterClass extends CharacterClass { - RegExpCharacterClass cc; - - InvertedCharacterClass() { this = getCharClassForCanonicalTerm(cc) and cc.isInverted() } - - override string getARelevantChar() { - result = nextChar(caseNormalize(getAMentionedChar(cc), cc)) or - nextChar(result) = caseNormalize(getAMentionedChar(cc), cc) - } - - bindingset[char] - override predicate matches(string char) { not hasChildThatMatches(cc, char) } - } - - /** - * Holds if the character class escape `clazz` (\d, \s, or \w) matches `char`. - */ - pragma[noinline] - private predicate classEscapeMatches(string clazz, string char) { - clazz = "d" and - char = "0123456789".charAt(_) - or - clazz = "s" and - char = [" ", "\t", "\r", "\n", 11.toUnicode(), 12.toUnicode()] // 11.toUnicode() = \v, 12.toUnicode() = \f - or - clazz = "w" and - char = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_".charAt(_) - } - - /** - * An implementation of `CharacterClass` for \d, \s, and \w. - */ - private class PositiveCharacterClassEscape extends CharacterClass { - string charClass; - RegExpTerm cc; - - PositiveCharacterClassEscape() { - isEscapeClass(cc, charClass) and - this = getCharClassForCanonicalTerm(cc) and - charClass = ["d", "s", "w"] - } - - override string getARelevantChar() { - charClass = "d" and - result = ["0", "9"] - or - charClass = "s" and - result = " " - or - charClass = "w" and - if RegExpFlags::isIgnoreCase(cc.getRootTerm()) - then result = ["a", "z", "_", "0", "9"] - else result = ["a", "Z", "_", "0", "9"] - } - - override predicate matches(string char) { classEscapeMatches(charClass, char) } - - override string choose() { - charClass = "d" and - result = "9" - or - charClass = "s" and - result = " " - or - charClass = "w" and - result = "a" - } - } - - /** - * An implementation of `CharacterClass` for \D, \S, and \W. - */ - private class NegativeCharacterClassEscape extends CharacterClass { - string charClass; - - NegativeCharacterClassEscape() { - exists(RegExpTerm cc | - isEscapeClass(cc, charClass) and - this = getCharClassForCanonicalTerm(cc) and - charClass = ["D", "S", "W"] - ) - } - - override string getARelevantChar() { - charClass = "D" and - result = ["a", "Z", "!"] - or - charClass = "S" and - result = ["a", "9", "!"] - or - charClass = "W" and - result = [" ", "!"] - } - - bindingset[char] - override predicate matches(string char) { - not classEscapeMatches(charClass.toLowerCase(), char) - } - } - - /** Gets a representative for all char classes that match the same chars as `c`. */ - CharacterClass normalize(CharacterClass c) { - exists(string normalization | - normalization = getNormalizationString(c) and - result = - min(CharacterClass cc, string raw | - getNormalizationString(cc) = normalization and cc = CharClass(raw) - | - cc order by raw - ) - ) - } - - /** Gets a string representing all the chars matched by `c` */ - private string getNormalizationString(CharacterClass c) { - (c instanceof PositiveCharacterClass or c instanceof PositiveCharacterClassEscape) and - result = concat(string char | c.matches(char) and char = CharacterClasses::getARelevantChar()) - or - (c instanceof InvertedCharacterClass or c instanceof NegativeCharacterClassEscape) and - // the string produced by the concat can not contain repeated chars - // so by starting the below with "nn" we can guarantee that - // it will not overlap with the above case. - // and a negative char class can never match the same chars as a positive one, so we don't miss any results from this. - result = - "nn:" + - concat(string char | not c.matches(char) and char = CharacterClasses::getARelevantChar()) - } -} - -private class EdgeLabel extends TInputSymbol { - string toString() { - this = Epsilon() and result = "" - or - exists(InputSymbol s | this = s and result = s.toString()) - } -} - -/** - * A RegExp term that acts like a plus. - * Either it's a RegExpPlus, or it is a range {1,X} where X is >= 30. - * 30 has been chosen as a threshold because for exponential blowup 2^30 is enough to get a decent DOS attack. - */ -private class EffectivelyPlus extends RegExpTerm { - EffectivelyPlus() { - this instanceof RegExpPlus - or - exists(RegExpRange range | - range.getLowerBound() = 1 and - (range.getUpperBound() >= 30 or not exists(range.getUpperBound())) - | - this = range - ) - } -} - -/** - * A RegExp term that acts like a star. - * Either it's a RegExpStar, or it is a range {0,X} where X is >= 30. - */ -private class EffectivelyStar extends RegExpTerm { - EffectivelyStar() { - this instanceof RegExpStar - or - exists(RegExpRange range | - range.getLowerBound() = 0 and - (range.getUpperBound() >= 30 or not exists(range.getUpperBound())) - | - this = range - ) - } -} - -/** - * A RegExp term that acts like a question mark. - * Either it's a RegExpQuestion, or it is a range {0,1}. - */ -private class EffectivelyQuestion extends RegExpTerm { - EffectivelyQuestion() { - this instanceof RegExpOpt - or - exists(RegExpRange range | range.getLowerBound() = 0 and range.getUpperBound() = 1 | - this = range - ) - } -} - -/** - * Gets the state before matching `t`. - */ -pragma[inline] -private State before(RegExpTerm t) { result = Match(t, 0) } - -/** - * Gets a state the NFA may be in after matching `t`. - */ -State after(RegExpTerm t) { - exists(RegExpAlt alt | t = alt.getAChild() | result = after(alt)) - or - exists(RegExpSequence seq, int i | t = seq.getChild(i) | - result = before(seq.getChild(i + 1)) - or - i + 1 = seq.getNumChild() and result = after(seq) - ) - or - exists(RegExpGroup grp | t = grp.getAChild() | result = after(grp)) - or - exists(EffectivelyStar star | t = star.getAChild() | - not isPossessive(star) and - result = before(star) - ) - or - exists(EffectivelyPlus plus | t = plus.getAChild() | - not isPossessive(plus) and - result = before(plus) - or - result = after(plus) - ) - or - exists(EffectivelyQuestion opt | t = opt.getAChild() | result = after(opt)) - or - exists(RegExpRoot root | t = root | - if matchesAnySuffix(root) then result = AcceptAnySuffix(root) else result = Accept(root) - ) -} - -/** - * Holds if the NFA has a transition from `q1` to `q2` labelled with `lbl`. - */ -predicate delta(State q1, EdgeLabel lbl, State q2) { - exists(RegexpCharacterConstant s, int i | - q1 = Match(s, i) and - ( - not RegExpFlags::isIgnoreCase(s.getRootTerm()) and - lbl = Char(s.getValue().charAt(i)) - or - // normalize everything to lower case if the regexp is case insensitive - RegExpFlags::isIgnoreCase(s.getRootTerm()) and - exists(string c | c = s.getValue().charAt(i) | lbl = Char(c.toLowerCase())) - ) and - ( - q2 = Match(s, i + 1) - or - s.getValue().length() = i + 1 and - q2 = after(s) - ) - ) - or - exists(RegExpDot dot | q1 = before(dot) and q2 = after(dot) | - if RegExpFlags::isDotAll(dot.getRootTerm()) then lbl = Any() else lbl = Dot() - ) - or - exists(RegExpCharacterClass cc | - cc.isUniversalClass() and q1 = before(cc) and lbl = Any() and q2 = after(cc) - or - q1 = before(cc) and - lbl = CharacterClasses::normalize(CharClass(getCanonicalizationString(cc))) and - q2 = after(cc) - ) - or - exists(RegExpTerm cc | isEscapeClass(cc, _) | - q1 = before(cc) and - lbl = CharacterClasses::normalize(CharClass(getCanonicalizationString(cc))) and - q2 = after(cc) - ) - or - exists(RegExpAlt alt | lbl = Epsilon() | q1 = before(alt) and q2 = before(alt.getAChild())) - or - exists(RegExpSequence seq | lbl = Epsilon() | q1 = before(seq) and q2 = before(seq.getChild(0))) - or - exists(RegExpGroup grp | lbl = Epsilon() | q1 = before(grp) and q2 = before(grp.getChild(0))) - or - exists(EffectivelyStar star | lbl = Epsilon() | - q1 = before(star) and q2 = before(star.getChild(0)) - or - q1 = before(star) and q2 = after(star) - ) - or - exists(EffectivelyPlus plus | lbl = Epsilon() | - q1 = before(plus) and q2 = before(plus.getChild(0)) - ) - or - exists(EffectivelyQuestion opt | lbl = Epsilon() | - q1 = before(opt) and q2 = before(opt.getChild(0)) - or - q1 = before(opt) and q2 = after(opt) - ) - or - exists(RegExpRoot root | q1 = AcceptAnySuffix(root) | - lbl = Any() and q2 = q1 - or - lbl = Epsilon() and q2 = Accept(root) - ) - or - exists(RegExpRoot root | q1 = Match(root, 0) | matchesAnyPrefix(root) and lbl = Any() and q2 = q1) - or - exists(RegExpDollar dollar | q1 = before(dollar) | - lbl = Epsilon() and q2 = Accept(getRoot(dollar)) - ) - or - exists(EmptyPositiveSubPattern empty | q1 = before(empty) | lbl = Epsilon() and q2 = after(empty)) -} - -/** - * Gets a state that `q` has an epsilon transition to. - */ -State epsilonSucc(State q) { delta(q, Epsilon(), result) } - -/** - * Gets a state that has an epsilon transition to `q`. - */ -State epsilonPred(State q) { q = epsilonSucc(result) } - -/** - * Holds if there is a state `q` that can be reached from `q1` - * along epsilon edges, such that there is a transition from - * `q` to `q2` that consumes symbol `s`. - */ -predicate deltaClosed(State q1, InputSymbol s, State q2) { delta(epsilonSucc*(q1), s, q2) } - -/** - * Gets the root containing the given term, that is, the root of the literal, - * or a branch of the root disjunction. - */ -RegExpRoot getRoot(RegExpTerm term) { - result = term or - result = getRoot(term.getParent()) -} - -/** - * A state in the NFA. - */ -newtype TState = - /** - * A state representing that the NFA is about to match a term. - * `i` is used to index into multi-char literals. - */ - Match(RelevantRegExpTerm t, int i) { - i = 0 - or - exists(t.(RegexpCharacterConstant).getValue().charAt(i)) - } or - /** - * An accept state, where exactly the given input string is accepted. - */ - Accept(RegExpRoot l) { l.isRelevant() } or - /** - * An accept state, where the given input string, or any string that has this - * string as a prefix, is accepted. - */ - AcceptAnySuffix(RegExpRoot l) { l.isRelevant() } - -/** - * Gets a state that is about to match the regular expression `t`. - */ -State mkMatch(RegExpTerm t) { result = Match(t, 0) } - -/** - * A state in the NFA corresponding to a regular expression. - * - * Each regular expression literal `l` has one accepting state - * `Accept(l)`, one state that accepts all suffixes `AcceptAnySuffix(l)`, - * and a state `Match(t, i)` for every subterm `t`, - * which represents the state of the NFA before starting to - * match `t`, or the `i`th character in `t` if `t` is a constant. - */ -class State extends TState { - RegExpTerm repr; - - State() { - this = Match(repr, _) or - this = Accept(repr) or - this = AcceptAnySuffix(repr) - } - - /** - * Gets a string representation for this state in a regular expression. - */ - string toString() { - exists(int i | this = Match(repr, i) | result = "Match(" + repr + "," + i + ")") - or - this instanceof Accept and - result = "Accept(" + repr + ")" - or - this instanceof AcceptAnySuffix and - result = "AcceptAny(" + repr + ")" - } - - /** - * Gets the location for this state. - */ - Location getLocation() { result = repr.getLocation() } - - /** - * Gets the term represented by this state. - */ - RegExpTerm getRepr() { result = repr } -} - -/** - * Gets the minimum char that is matched by both the character classes `c` and `d`. - */ -private string getMinOverlapBetweenCharacterClasses(CharacterClass c, CharacterClass d) { - result = min(getAOverlapBetweenCharacterClasses(c, d)) -} - -/** - * Gets a char that is matched by both the character classes `c` and `d`. - * And `c` and `d` is not the same character class. - */ -private string getAOverlapBetweenCharacterClasses(CharacterClass c, CharacterClass d) { - sharesRoot(c, d) and - result = [c.getARelevantChar(), d.getARelevantChar()] and - c.matches(result) and - d.matches(result) and - not c = d -} - -/** - * Gets a character that is represented by both `c` and `d`. - */ -string intersect(InputSymbol c, InputSymbol d) { - (sharesRoot(c, d) or [c, d] = Any()) and - ( - c = Char(result) and - d = getAnInputSymbolMatching(result) - or - result = getMinOverlapBetweenCharacterClasses(c, d) - or - result = c.(CharacterClass).choose() and - ( - d = c - or - d = Dot() and - not (result = "\n" or result = "\r") - or - d = Any() - ) - or - (c = Dot() or c = Any()) and - (d = Dot() or d = Any()) and - result = "a" - ) - or - result = intersect(d, c) -} - -/** - * Gets a symbol that matches `char`. - */ -bindingset[char] -InputSymbol getAnInputSymbolMatching(string char) { - result = Char(char) - or - result.(CharacterClass).matches(char) - or - result = Dot() and - not (char = "\n" or char = "\r") - or - result = Any() -} - -/** - * Holds if `state` is a start state. - */ -predicate isStartState(State state) { - state = mkMatch(any(RegExpRoot r)) - or - exists(RegExpCaret car | state = after(car)) -} - -/** - * Holds if `state` is a candidate for ReDoS with string `pump`. - */ -signature predicate isCandidateSig(State state, string pump); - -/** - * Holds if `state` is a candidate for ReDoS. - */ -signature predicate isCandidateSig(State state); - -/** - * Predicates for constructing a prefix string that leads to a given state. - */ -module PrefixConstruction { - /** - * Holds if `state` is the textually last start state for the regular expression. - */ - private predicate lastStartState(RelevantState state) { - exists(RegExpRoot root | - state = - max(RelevantState s, Location l | - isStartState(s) and - getRoot(s.getRepr()) = root and - l = s.getRepr().getLocation() - | - s - order by - l.getStartLine(), l.getStartColumn(), s.getRepr().toString(), l.getEndColumn(), - l.getEndLine() - ) - ) - } - - /** - * Holds if there exists any transition (Epsilon() or other) from `a` to `b`. - */ - private predicate existsTransition(State a, State b) { delta(a, _, b) } - - /** - * Gets the minimum number of transitions it takes to reach `state` from the `start` state. - */ - int prefixLength(State start, State state) = - shortestDistances(lastStartState/1, existsTransition/2)(start, state, result) - - /** - * Gets the minimum number of transitions it takes to reach `state` from the start state. - */ - private int lengthFromStart(State state) { result = prefixLength(_, state) } - - /** - * Gets a string for which the regular expression will reach `state`. - * - * Has at most one result for any given `state`. - * This predicate will not always have a result even if there is a ReDoS issue in - * the regular expression. - */ - string prefix(State state) { - lastStartState(state) and - result = "" - or - // the search stops past the last redos candidate state. - lengthFromStart(state) <= max(lengthFromStart(any(State s | isCandidate(s)))) and - exists(State prev | - // select a unique predecessor (by an arbitrary measure) - prev = - min(State s, Location loc | - lengthFromStart(s) = lengthFromStart(state) - 1 and - loc = s.getRepr().getLocation() and - delta(s, _, state) - | - s - order by - loc.getStartLine(), loc.getStartColumn(), loc.getEndLine(), loc.getEndColumn(), - s.getRepr().toString() - ) - | - // greedy search for the shortest prefix - result = prefix(prev) and delta(prev, Epsilon(), state) - or - not delta(prev, Epsilon(), state) and - result = prefix(prev) + getCanonicalEdgeChar(prev, state) - ) - } - - /** - * Gets a canonical char for which there exists a transition from `prev` to `next` in the NFA. - */ - private string getCanonicalEdgeChar(State prev, State next) { - result = - min(string c | delta(prev, any(InputSymbol symbol | c = intersect(Any(), symbol)), next)) - } - - /** A state within a regular expression that contains a candidate state. */ - class RelevantState instanceof State { - RelevantState() { - exists(State s | isCandidate(s) | getRoot(s.getRepr()) = getRoot(this.getRepr())) - } - - /** Gets a string representation for this state in a regular expression. */ - string toString() { result = State.super.toString() } - - /** Gets the term represented by this state. */ - RegExpTerm getRepr() { result = State.super.getRepr() } - } -} - -/** - * A module for pruning candidate ReDoS states. - * The candidates are specified by the `isCandidate` signature predicate. - * The candidates are checked for rejecting suffixes and deduplicated, - * and the resulting ReDoS states are read by the `hasReDoSResult` predicate. - */ -module ReDoSPruning { - /** - * Holds if repeating `pump` starting at `state` is a candidate for causing backtracking. - * No check whether a rejected suffix exists has been made. - */ - private predicate isReDoSCandidate(State state, string pump) { - isCandidate(state, pump) and - not state = acceptsAnySuffix() and // pruning early - these can never get stuck in a rejecting state. - ( - not isCandidate(epsilonSucc+(state), _) - or - epsilonSucc+(state) = state and - state = - max(State s, Location l | - s = epsilonSucc+(state) and - l = s.getRepr().getLocation() and - isCandidate(s, _) and - s.getRepr() instanceof InfiniteRepetitionQuantifier - | - s order by l.getStartLine(), l.getStartColumn(), l.getEndColumn(), l.getEndLine() - ) - ) - } - - /** Gets a state that can reach the `accept-any` state using only epsilon steps. */ - private State acceptsAnySuffix() { epsilonSucc*(result) = AcceptAnySuffix(_) } - - predicate isCandidateState(State s) { isReDoSCandidate(s, _) } - - import PrefixConstruction as Prefix - - class RelevantState = Prefix::RelevantState; - - /** - * Predicates for testing the presence of a rejecting suffix. - * - * These predicates are used to ensure that the all states reached from the fork - * by repeating `w` have a rejecting suffix. - * - * For example, a regexp like `/^(a+)+/` will accept any string as long the prefix is - * some number of `"a"`s, and it is therefore not possible to construct a rejecting suffix. - * - * A regexp like `/(a+)+$/` or `/(a+)+b/` trivially has a rejecting suffix, - * as the suffix "X" will cause both the regular expressions to be rejected. - * - * The string `w` is repeated any number of times because it needs to be - * infinitely repeatable for the attack to work. - * For the regular expression `/((ab)+)*abab/` the accepting state is not reachable from the fork - * using epsilon transitions. But any attempt at repeating `w` will end in a state that accepts all suffixes. - */ - private module SuffixConstruction { - /** - * Holds if all states reachable from `fork` by repeating `w` - * are likely rejectable by appending some suffix. - */ - predicate reachesOnlyRejectableSuffixes(State fork, string w) { - isReDoSCandidate(fork, w) and - forex(State next | next = process(fork, w, w.length() - 1) | isLikelyRejectable(next)) and - not getProcessPrevious(fork, _, w) = acceptsAnySuffix() // we stop `process(..)` early if we can, check here if it happened. - } - - /** - * Holds if there likely exists a suffix starting from `s` that leads to the regular expression being rejected. - * This predicate might find impossible suffixes when searching for suffixes of length > 1, which can cause FPs. - */ - pragma[noinline] - private predicate isLikelyRejectable(RelevantState s) { - // exists a reject edge with some char. - hasRejectEdge(s) - or - hasEdgeToLikelyRejectable(s) - or - // stopping here is rejection - isRejectState(s) - } - - /** - * Holds if `s` is not an accept state, and there is no epsilon transition to an accept state. - */ - predicate isRejectState(RelevantState s) { not epsilonSucc*(s) = Accept(_) } - - /** - * Holds if there is likely a non-empty suffix leading to rejection starting in `s`. - */ - pragma[noopt] - predicate hasEdgeToLikelyRejectable(RelevantState s) { - // all edges (at least one) with some char leads to another state that is rejectable. - // the `next` states might not share a common suffix, which can cause FPs. - exists(string char | char = hasEdgeToLikelyRejectableHelper(s) | - // noopt to force `hasEdgeToLikelyRejectableHelper` to be first in the join-order. - exists(State next | deltaClosedChar(s, char, next) | isLikelyRejectable(next)) and - forall(State next | deltaClosedChar(s, char, next) | isLikelyRejectable(next)) - ) - } - - /** - * Gets a char for there exists a transition away from `s`, - * and `s` has not been found to be rejectable by `hasRejectEdge` or `isRejectState`. - */ - pragma[noinline] - private string hasEdgeToLikelyRejectableHelper(RelevantState s) { - not hasRejectEdge(s) and - not isRejectState(s) and - deltaClosedChar(s, result, _) - } - - /** - * Holds if there is a state `next` that can be reached from `prev` - * along epsilon edges, such that there is a transition from - * `prev` to `next` that the character symbol `char`. - */ - predicate deltaClosedChar(RelevantState prev, string char, RelevantState next) { - deltaClosed(prev, getAnInputSymbolMatchingRelevant(char), next) - } - - pragma[noinline] - InputSymbol getAnInputSymbolMatchingRelevant(string char) { - char = relevant(_) and - result = getAnInputSymbolMatching(char) - } - - pragma[noinline] - RegExpRoot relevantRoot() { - exists(RegExpTerm term, State s | - s.getRepr() = term and isCandidateState(s) and result = term.getRootTerm() - ) - } - - /** - * Gets a char used for finding possible suffixes inside `root`. - */ - pragma[noinline] - private string relevant(RegExpRoot root) { - root = relevantRoot() and - ( - exists(ascii(result)) and exists(root) - or - exists(InputSymbol s | belongsTo(s, root) | result = intersect(s, _)) - or - // The characters from `hasSimpleRejectEdge`. Only `\n` is really needed (as `\n` is not in the `ascii` relation). - // The three chars must be kept in sync with `hasSimpleRejectEdge`. - result = ["|", "\n", "Z"] and exists(root) - ) - } - - /** - * Holds if there exists a `char` such that there is no edge from `s` labeled `char` in our NFA. - * The NFA does not model reject states, so the above is the same as saying there is a reject edge. - */ - private predicate hasRejectEdge(State s) { - hasSimpleRejectEdge(s) - or - not hasSimpleRejectEdge(s) and - exists(string char | char = relevant(getRoot(s.getRepr())) | not deltaClosedChar(s, char, _)) - } - - /** - * Holds if there is no edge from `s` labeled with "|", "\n", or "Z" in our NFA. - * This predicate is used as a cheap pre-processing to speed up `hasRejectEdge`. - */ - private predicate hasSimpleRejectEdge(State s) { - // The three chars were chosen arbitrarily. The three chars must be kept in sync with `relevant`. - exists(string char | char = ["|", "\n", "Z"] | not deltaClosedChar(s, char, _)) - } - - /** - * Gets a state that can be reached from pumpable `fork` consuming all - * chars in `w` any number of times followed by the first `i+1` characters of `w`. - */ - pragma[noopt] - private State process(State fork, string w, int i) { - exists(State prev | prev = getProcessPrevious(fork, i, w) | - not prev = acceptsAnySuffix() and // we stop `process(..)` early if we can. If the successor accepts any suffix, then we know it can never be rejected. - exists(string char, InputSymbol sym | - char = w.charAt(i) and - deltaClosed(prev, sym, result) and - // noopt to prevent joining `prev` with all possible `chars` that could transition away from `prev`. - // Instead only join with the set of `chars` where a relevant `InputSymbol` has already been found. - sym = getAProcessInputSymbol(char) - ) - ) - } - - /** - * Gets a state that can be reached from pumpable `fork` consuming all - * chars in `w` any number of times followed by the first `i` characters of `w`. - */ - private State getProcessPrevious(State fork, int i, string w) { - isReDoSCandidate(fork, w) and - ( - i = 0 and result = fork - or - result = process(fork, w, i - 1) - or - // repeat until fixpoint - i = 0 and - result = process(fork, w, w.length() - 1) - ) - } - - /** - * Gets an InputSymbol that matches `char`. - * The predicate is specialized to only have a result for the `char`s that are relevant for the `process` predicate. - */ - private InputSymbol getAProcessInputSymbol(string char) { - char = getAProcessChar() and - result = getAnInputSymbolMatching(char) - } - - /** - * Gets a `char` that occurs in a `pump` string. - */ - private string getAProcessChar() { result = any(string s | isReDoSCandidate(_, s)).charAt(_) } - } - - /** - * Holds if `term` may cause superlinear backtracking on strings containing many repetitions of `pump`. - * Gets the shortest string that causes superlinear backtracking. - */ - private predicate isReDoSAttackable(RegExpTerm term, string pump, State s) { - exists(int i, string c | s = Match(term, i) | - c = - min(string w | - isCandidate(s, w) and - SuffixConstruction::reachesOnlyRejectableSuffixes(s, w) - | - w order by w.length(), w - ) and - pump = escape(rotate(c, i)) - ) - } - - /** - * Holds if the state `s` (represented by the term `t`) can have backtracking with repetitions of `pump`. - * - * `prefixMsg` contains a friendly message for a prefix that reaches `s` (or `prefixMsg` is the empty string if the prefix is empty or if no prefix could be found). - */ - predicate hasReDoSResult(RegExpTerm t, string pump, State s, string prefixMsg) { - isReDoSAttackable(t, pump, s) and - ( - prefixMsg = "starting with '" + escape(Prefix::prefix(s)) + "' and " and - not Prefix::prefix(s) = "" - or - Prefix::prefix(s) = "" and prefixMsg = "" - or - not exists(Prefix::prefix(s)) and prefixMsg = "" - ) - } - - /** - * Gets the result of backslash-escaping newlines, carriage-returns and - * backslashes in `s`. - */ - bindingset[s] - private string escape(string s) { - result = - s.replaceAll("\\", "\\\\") - .replaceAll("\n", "\\n") - .replaceAll("\r", "\\r") - .replaceAll("\t", "\\t") - } - - /** - * Gets `str` with the last `i` characters moved to the front. - * - * We use this to adjust the pump string to match with the beginning of - * a RegExpTerm, so it doesn't start in the middle of a constant. - */ - bindingset[str, i] - private string rotate(string str, int i) { - result = str.suffix(str.length() - i) + str.prefix(str.length() - i) - } -} - -/** - * A module that describes a tree where each node has one or more associated characters, also known as a trie. - * The root node has no associated character. - * This module is a signature used in `Concretizer`. - */ -signature module CharTree { - /** A node in the tree. */ - class CharNode; - - /** Gets the previous node in the tree from `t`. */ - CharNode getPrev(CharNode t); - - /** - * Holds if `n` is at the end of a tree. I.e. a node that should have a result in the `Concretizer` module. - * Such a node can still have children. - */ - predicate isARelevantEnd(CharNode n); - - /** Gets a char associated with `t`. */ - string getChar(CharNode t); -} - -/** - * Implements an algorithm for computing all possible strings - * from following a tree of nodes (as described in `CharTree`). - * - * The string is build using one big concat, where all the chars are computed first. - * See `concretize`. - */ -module Concretizer { - private class Node = Impl::CharNode; - - private predicate getPrev = Impl::getPrev/1; - - private predicate isARelevantEnd = Impl::isARelevantEnd/1; - - private predicate getChar = Impl::getChar/1; - - /** Holds if `n` is on a path from the root to a leaf, and is therefore relevant for the results in `concretize`. */ - private predicate isRelevant(Node n) { - isARelevantEnd(n) - or - exists(Node succ | isRelevant(succ) | n = getPrev(succ)) - } - - /** Holds if `n` is a root with no predecessors. */ - private predicate isRoot(Node n) { not exists(getPrev(n)) } - - /** Gets the distance from a root to `n`. */ - private int nodeDepth(Node n) { - result = 0 and isRoot(n) - or - isRelevant(n) and - exists(Node prev | result = nodeDepth(prev) + 1 | prev = getPrev(n)) - } - - /** Gets an ancestor of `end`, where `end` is a node that should have a result in `concretize`. */ - private Node getAnAncestor(Node end) { isARelevantEnd(end) and result = getPrev*(end) } - - /** Gets the `i`th character on the path from the root to `n`. */ - pragma[noinline] - private string getPrefixChar(Node n, int i) { - exists(Node ancestor | - result = getChar(ancestor) and - ancestor = getAnAncestor(n) and - i = nodeDepth(ancestor) - ) - } - - /** Gets a string corresponding to `node`. */ - language[monotonicAggregates] - string concretize(Node n) { - result = strictconcat(int i | exists(getPrefixChar(n, i)) | getPrefixChar(n, i) order by i) - } -} +private import semmle.code.java.regex.RegexTreeView::RegexTreeView as TreeView +// NfaUtils should be used directly from the shared pack, and not from this file. +deprecated private import codeql.regex.nfa.NfaUtils::Make as Dep +import Dep diff --git a/java/ql/lib/semmle/code/java/security/regexp/NfaUtilsSpecific.qll b/java/ql/lib/semmle/code/java/security/regexp/NfaUtilsSpecific.qll deleted file mode 100644 index 742229eacca..00000000000 --- a/java/ql/lib/semmle/code/java/security/regexp/NfaUtilsSpecific.qll +++ /dev/null @@ -1,76 +0,0 @@ -/** - * This module should provide a class hierarchy corresponding to a parse tree of regular expressions. - * This is the interface to the shared ReDoS library. - */ - -private import java -import semmle.code.FileSystem -import semmle.code.java.regex.RegexTreeView - -/** - * Holds if `term` is an escape class representing e.g. `\d`. - * `clazz` is which character class it represents, e.g. "d" for `\d`. - */ -predicate isEscapeClass(RegExpTerm term, string clazz) { - term.(RegExpCharacterClassEscape).getValue() = clazz - or - term.(RegExpNamedProperty).getBackslashEquivalent() = clazz -} - -/** - * Holds if `term` is a possessive quantifier, e.g. `a*+`. - */ -predicate isPossessive(RegExpQuantifier term) { term.isPossessive() } - -/** - * Holds if the regex that `term` is part of is used in a way that ignores any leading prefix of the input it's matched against. - */ -predicate matchesAnyPrefix(RegExpTerm term) { not term.getRegex().matchesFullString() } - -/** - * Holds if the regex that `term` is part of is used in a way that ignores any trailing suffix of the input it's matched against. - */ -predicate matchesAnySuffix(RegExpTerm term) { not term.getRegex().matchesFullString() } - -/** - * Holds if the regular expression should not be considered. - * - * We make the pragmatic performance optimization to ignore regular expressions in files - * that do not belong to the project code (such as installed dependencies). - */ -predicate isExcluded(RegExpParent parent) { - not exists(parent.getRegex().getLocation().getFile().getRelativePath()) - or - // Regexes with many occurrences of ".*" may cause the polynomial ReDoS computation to explode, so - // we explicitly exclude these. - strictcount(int i | exists(parent.getRegex().getText().regexpFind("\\.\\*", i, _)) | i) > 10 -} - -/** - * A module containing predicates for determining which flags a regular expression have. - */ -module RegExpFlags { - /** - * Holds if `root` has the `i` flag for case-insensitive matching. - */ - predicate isIgnoreCase(RegExpTerm root) { - root.isRootTerm() and - root.getLiteral().isIgnoreCase() - } - - /** - * Gets the flags for `root`, or the empty string if `root` has no flags. - */ - deprecated string getFlags(RegExpTerm root) { - root.isRootTerm() and - result = root.getLiteral().getFlags() - } - - /** - * Holds if `root` has the `s` flag for multi-line matching. - */ - predicate isDotAll(RegExpTerm root) { - root.isRootTerm() and - root.getLiteral().isDotAll() - } -} diff --git a/java/ql/lib/semmle/code/java/security/regexp/PolynomialReDoSQuery.qll b/java/ql/lib/semmle/code/java/security/regexp/PolynomialReDoSQuery.qll index b0a8ff1a3c5..2a822ac69de 100644 --- a/java/ql/lib/semmle/code/java/security/regexp/PolynomialReDoSQuery.qll +++ b/java/ql/lib/semmle/code/java/security/regexp/PolynomialReDoSQuery.qll @@ -1,19 +1,19 @@ /** Definitions and configurations for the Polynomial ReDoS query */ -import semmle.code.java.security.regexp.SuperlinearBackTracking +private import semmle.code.java.regex.RegexTreeView::RegexTreeView as TreeView +import codeql.regex.nfa.SuperlinearBackTracking::Make as SuperlinearBackTracking import semmle.code.java.dataflow.DataFlow -import semmle.code.java.regex.RegexTreeView import semmle.code.java.regex.RegexFlowConfigs import semmle.code.java.dataflow.FlowSources /** A sink for polynomial redos queries, where a regex is matched. */ class PolynomialRedosSink extends DataFlow::Node { - RegExpLiteral reg; + TreeView::RegExpLiteral reg; PolynomialRedosSink() { regexMatchedAgainst(reg.getRegex(), this.asExpr()) } /** Gets the regex that is matched against this node. */ - RegExpTerm getRegExp() { result.getParent() = reg } + TreeView::RegExpTerm getRegExp() { result.getParent() = reg } } /** @@ -49,7 +49,8 @@ class PolynomialRedosConfig extends TaintTracking::Configuration { /** Holds if there is flow from `source` to `sink` that is matched against the regexp term `regexp` that is vulnerable to Polynomial ReDoS. */ predicate hasPolynomialReDoSResult( - DataFlow::PathNode source, DataFlow::PathNode sink, PolynomialBackTrackingTerm regexp + DataFlow::PathNode source, DataFlow::PathNode sink, + SuperlinearBackTracking::PolynomialBackTrackingTerm regexp ) { any(PolynomialRedosConfig config).hasFlowPath(source, sink) and regexp.getRootTerm() = sink.getNode().(PolynomialRedosSink).getRegExp() diff --git a/java/ql/lib/semmle/code/java/security/regexp/SuperlinearBackTracking.qll b/java/ql/lib/semmle/code/java/security/regexp/SuperlinearBackTracking.qll index 14a69dc0644..623b1540ef1 100644 --- a/java/ql/lib/semmle/code/java/security/regexp/SuperlinearBackTracking.qll +++ b/java/ql/lib/semmle/code/java/security/regexp/SuperlinearBackTracking.qll @@ -1,11 +1,4 @@ /** - * Provides classes for working with regular expressions that can - * perform backtracking in superlinear time. - */ - -import NfaUtils - -/* * This module implements the analysis described in the paper: * Valentin Wustholz, Oswaldo Olivo, Marijn J. H. Heule, and Isil Dillig: * Static Detection of DoS Vulnerabilities in @@ -42,377 +35,7 @@ import NfaUtils * It also doesn't find all transitions in the product automaton, which can cause false negatives. */ -/** - * Gets any root (start) state of a regular expression. - */ -private State getRootState() { result = mkMatch(any(RegExpRoot r)) } - -private newtype TStateTuple = - MkStateTuple(State q1, State q2, State q3) { - // starts at (pivot, pivot, succ) - isStartLoops(q1, q3) and q1 = q2 - or - step(_, _, _, _, q1, q2, q3) and FeasibleTuple::isFeasibleTuple(q1, q2, q3) - } - -/** - * A state in the product automaton. - * The product automaton contains 3-tuples of states. - * - * We lazily only construct those states that we are actually - * going to need. - * Either a start state `(pivot, pivot, succ)`, or a state - * where there exists a transition from an already existing state. - * - * The exponential variant of this query (`js/redos`) uses an optimization - * trick where `q1 <= q2`. This trick cannot be used here as the order - * of the elements matter. - */ -class StateTuple extends TStateTuple { - State q1; - State q2; - State q3; - - StateTuple() { this = MkStateTuple(q1, q2, q3) } - - /** - * Gest a string representation of this tuple. - */ - string toString() { result = "(" + q1 + ", " + q2 + ", " + q3 + ")" } - - /** - * Holds if this tuple is `(r1, r2, r3)`. - */ - pragma[noinline] - predicate isTuple(State r1, State r2, State r3) { r1 = q1 and r2 = q2 and r3 = q3 } -} - -/** - * A module for determining feasible tuples for the product automaton. - * - * The implementation is split into many predicates for performance reasons. - */ -private module FeasibleTuple { - /** - * Holds if the tuple `(r1, r2, r3)` might be on path from a start-state to an end-state in the product automaton. - */ - pragma[inline] - predicate isFeasibleTuple(State r1, State r2, State r3) { - // The first element is either inside a repetition (or the start state itself) - isRepetitionOrStart(r1) and - // The last element is inside a repetition - stateInsideRepetition(r3) and - // The states are reachable in the NFA in the order r1 -> r2 -> r3 - delta+(r1) = r2 and - delta+(r2) = r3 and - // The first element can reach a beginning (the "pivot" state in a `(pivot, succ)` pair). - canReachABeginning(r1) and - // The last element can reach a target (the "succ" state in a `(pivot, succ)` pair). - canReachATarget(r3) - } - - /** - * Holds if `s` is either inside a repetition, or is the start state (which is a repetition). - */ - pragma[noinline] - private predicate isRepetitionOrStart(State s) { stateInsideRepetition(s) or s = getRootState() } - - /** - * Holds if state `s` might be inside a backtracking repetition. - */ - pragma[noinline] - private predicate stateInsideRepetition(State s) { - s.getRepr().getParent*() instanceof InfiniteRepetitionQuantifier - } - - /** - * Holds if there exists a path in the NFA from `s` to a "pivot" state - * (from a `(pivot, succ)` pair that starts the search). - */ - pragma[noinline] - private predicate canReachABeginning(State s) { - delta+(s) = any(State pivot | isStartLoops(pivot, _)) - } - - /** - * Holds if there exists a path in the NFA from `s` to a "succ" state - * (from a `(pivot, succ)` pair that starts the search). - */ - pragma[noinline] - private predicate canReachATarget(State s) { delta+(s) = any(State succ | isStartLoops(_, succ)) } -} - -/** - * Holds if `pivot` and `succ` are a pair of loops that could be the beginning of a quadratic blowup. - * - * There is a slight implementation difference compared to the paper: this predicate requires that `pivot != succ`. - * The case where `pivot = succ` causes exponential backtracking and is handled by the `js/redos` query. - */ -predicate isStartLoops(State pivot, State succ) { - pivot != succ and - succ.getRepr() instanceof InfiniteRepetitionQuantifier and - delta+(pivot) = succ and - ( - pivot.getRepr() instanceof InfiniteRepetitionQuantifier - or - pivot = mkMatch(any(RegExpRoot root)) - ) -} - -/** - * Gets a state for which there exists a transition in the NFA from `s'. - */ -State delta(State s) { delta(s, _, result) } - -/** - * Holds if there are transitions from the components of `q` to the corresponding - * components of `r` labelled with `s1`, `s2`, and `s3`, respectively. - */ -pragma[noinline] -predicate step(StateTuple q, InputSymbol s1, InputSymbol s2, InputSymbol s3, StateTuple r) { - exists(State r1, State r2, State r3 | - step(q, s1, s2, s3, r1, r2, r3) and r = MkStateTuple(r1, r2, r3) - ) -} - -/** - * Holds if there are transitions from the components of `q` to `r1`, `r2`, and `r3 - * labelled with `s1`, `s2`, and `s3`, respectively. - */ -pragma[noopt] -predicate step( - StateTuple q, InputSymbol s1, InputSymbol s2, InputSymbol s3, State r1, State r2, State r3 -) { - exists(State q1, State q2, State q3 | q.isTuple(q1, q2, q3) | - deltaClosed(q1, s1, r1) and - deltaClosed(q2, s2, r2) and - deltaClosed(q3, s3, r3) and - // use noopt to force the join on `getAThreewayIntersect` to happen last. - exists(getAThreewayIntersect(s1, s2, s3)) - ) -} - -/** - * Gets a char that is matched by all the edges `s1`, `s2`, and `s3`. - * - * The result is not complete, and might miss some combination of edges that share some character. - */ -pragma[noinline] -string getAThreewayIntersect(InputSymbol s1, InputSymbol s2, InputSymbol s3) { - result = minAndMaxIntersect(s1, s2) and result = [intersect(s2, s3), intersect(s1, s3)] - or - result = minAndMaxIntersect(s1, s3) and result = [intersect(s2, s3), intersect(s1, s2)] - or - result = minAndMaxIntersect(s2, s3) and result = [intersect(s1, s2), intersect(s1, s3)] -} - -/** - * Gets the minimum and maximum characters that intersect between `a` and `b`. - * This predicate is used to limit the size of `getAThreewayIntersect`. - */ -pragma[noinline] -string minAndMaxIntersect(InputSymbol a, InputSymbol b) { - result = [min(intersect(a, b)), max(intersect(a, b))] -} - -private newtype TTrace = - Nil() or - Step(InputSymbol s1, InputSymbol s2, InputSymbol s3, TTrace t) { - isReachableFromStartTuple(_, _, t, s1, s2, s3, _, _) - } - -/** - * A list of tuples of input symbols that describe a path in the product automaton - * starting from some start state. - */ -class Trace extends TTrace { - /** - * Gets a string representation of this Trace that can be used for debug purposes. - */ - string toString() { - this = Nil() and result = "Nil()" - or - exists(InputSymbol s1, InputSymbol s2, InputSymbol s3, Trace t | this = Step(s1, s2, s3, t) | - result = "Step(" + s1 + ", " + s2 + ", " + s3 + ", " + t + ")" - ) - } -} - -/** - * Holds if there exists a transition from `r` to `q` in the product automaton. - * Notice that the arguments are flipped, and thus the direction is backwards. - */ -pragma[noinline] -predicate tupleDeltaBackwards(StateTuple q, StateTuple r) { step(r, _, _, _, q) } - -/** - * Holds if `tuple` is an end state in our search. - * That means there exists a pair of loops `(pivot, succ)` such that `tuple = (pivot, succ, succ)`. - */ -predicate isEndTuple(StateTuple tuple) { tuple = getAnEndTuple(_, _) } - -/** - * Gets the minimum length of a path from `r` to some an end state `end`. - * - * The implementation searches backwards from the end-tuple. - * This approach was chosen because it is way more efficient if the first predicate given to `shortestDistances` is small. - * The `end` argument must always be an end state. - */ -int distBackFromEnd(StateTuple r, StateTuple end) = - shortestDistances(isEndTuple/1, tupleDeltaBackwards/2)(end, r, result) - -/** - * Holds if there exists a pair of repetitions `(pivot, succ)` in the regular expression such that: - * `tuple` is reachable from `(pivot, pivot, succ)` in the product automaton, - * and there is a distance of `dist` from `tuple` to the nearest end-tuple `(pivot, succ, succ)`, - * and a path from a start-state to `tuple` follows the transitions in `trace`. - */ -private predicate isReachableFromStartTuple( - State pivot, State succ, StateTuple tuple, Trace trace, int dist -) { - exists(InputSymbol s1, InputSymbol s2, InputSymbol s3, Trace v | - isReachableFromStartTuple(pivot, succ, v, s1, s2, s3, tuple, dist) and - trace = Step(s1, s2, s3, v) - ) -} - -private predicate isReachableFromStartTuple( - State pivot, State succ, Trace trace, InputSymbol s1, InputSymbol s2, InputSymbol s3, - StateTuple tuple, int dist -) { - // base case. - exists(State q1, State q2, State q3 | - isStartLoops(pivot, succ) and - step(MkStateTuple(pivot, pivot, succ), s1, s2, s3, tuple) and - tuple = MkStateTuple(q1, q2, q3) and - trace = Nil() and - dist = distBackFromEnd(tuple, MkStateTuple(pivot, succ, succ)) - ) - or - // recursive case - exists(StateTuple p | - isReachableFromStartTuple(pivot, succ, p, trace, dist + 1) and - dist = distBackFromEnd(tuple, MkStateTuple(pivot, succ, succ)) and - step(p, s1, s2, s3, tuple) - ) -} - -/** - * Gets the tuple `(pivot, succ, succ)` from the product automaton. - */ -StateTuple getAnEndTuple(State pivot, State succ) { - isStartLoops(pivot, succ) and - result = MkStateTuple(pivot, succ, succ) -} - -/** An implementation of a chain containing chars for use by `Concretizer`. */ -private module CharTreeImpl implements CharTree { - class CharNode = Trace; - - CharNode getPrev(CharNode t) { t = Step(_, _, _, result) } - - /** Holds if `n` is used in `isPumpable`. */ - predicate isARelevantEnd(CharNode n) { - exists(State pivot, State succ | - isReachableFromStartTuple(pivot, succ, getAnEndTuple(pivot, succ), n, _) - ) - } - - string getChar(CharNode t) { - exists(InputSymbol s1, InputSymbol s2, InputSymbol s3 | t = Step(s1, s2, s3, _) | - result = getAThreewayIntersect(s1, s2, s3) - ) - } -} - -/** - * Holds if matching repetitions of `pump` can: - * 1) Transition from `pivot` back to `pivot`. - * 2) Transition from `pivot` to `succ`. - * 3) Transition from `succ` to `succ`. - * - * From theorem 3 in the paper linked in the top of this file we can therefore conclude that - * the regular expression has polynomial backtracking - if a rejecting suffix exists. - * - * This predicate is used by `SuperLinearReDoSConfiguration`, and the final results are - * available in the `hasReDoSResult` predicate. - */ -predicate isPumpable(State pivot, State succ, string pump) { - exists(StateTuple q, Trace t | - isReachableFromStartTuple(pivot, succ, q, t, _) and - q = getAnEndTuple(pivot, succ) and - pump = Concretizer::concretize(t) - ) -} - -/** - * Holds if states starting in `state` can have polynomial backtracking with the string `pump`. - */ -predicate isReDoSCandidate(State state, string pump) { isPumpable(_, state, pump) } - -/** - * Holds if repetitions of `pump` at `t` will cause polynomial backtracking. - */ -predicate polynomialReDoS(RegExpTerm t, string pump, string prefixMsg, RegExpTerm prev) { - exists(State s, State pivot | - ReDoSPruning::hasReDoSResult(t, pump, s, prefixMsg) and - isPumpable(pivot, s, _) and - prev = pivot.getRepr() - ) -} - -/** - * Gets a message for why `term` can cause polynomial backtracking. - */ -string getReasonString(RegExpTerm term, string pump, string prefixMsg, RegExpTerm prev) { - polynomialReDoS(term, pump, prefixMsg, prev) and - result = - "Strings " + prefixMsg + "with many repetitions of '" + pump + - "' can start matching anywhere after the start of the preceeding " + prev -} - -/** - * A term that may cause a regular expression engine to perform a - * polynomial number of match attempts, relative to the input length. - */ -class PolynomialBackTrackingTerm extends InfiniteRepetitionQuantifier { - string reason; - string pump; - string prefixMsg; - RegExpTerm prev; - - PolynomialBackTrackingTerm() { - reason = getReasonString(this, pump, prefixMsg, prev) and - // there might be many reasons for this term to have polynomial backtracking - we pick the shortest one. - reason = min(string msg | msg = getReasonString(this, _, _, _) | msg order by msg.length(), msg) - } - - /** - * Holds if all non-empty successors to the polynomial backtracking term matches the end of the line. - */ - predicate isAtEndLine() { - forall(RegExpTerm succ | this.getSuccessor+() = succ and not matchesEpsilon(succ) | - succ instanceof RegExpDollar - ) - } - - /** - * Gets the string that should be repeated to cause this regular expression to perform polynomially. - */ - string getPumpString() { result = pump } - - /** - * Gets a message for which prefix a matching string must start with for this term to cause polynomial backtracking. - */ - string getPrefixMessage() { result = prefixMsg } - - /** - * Gets a predecessor to `this`, which also loops on the pump string, and thereby causes polynomial backtracking. - */ - RegExpTerm getPreviousLoop() { result = prev } - - /** - * Gets the reason for the number of match attempts. - */ - string getReason() { result = reason } -} +private import semmle.code.java.regex.RegexTreeView::RegexTreeView as TreeView +// SuperlinearBackTracking should be used directly from the shared pack, and not from this file. +deprecated private import codeql.regex.nfa.SuperlinearBackTracking::Make as Dep +import Dep diff --git a/java/ql/src/Security/CWE/CWE-020/OverlyLargeRange.ql b/java/ql/src/Security/CWE/CWE-020/OverlyLargeRange.ql index d054659892c..b8ea3e52dbd 100644 --- a/java/ql/src/Security/CWE/CWE-020/OverlyLargeRange.ql +++ b/java/ql/src/Security/CWE/CWE-020/OverlyLargeRange.ql @@ -12,14 +12,15 @@ * external/cwe/cwe-020 */ -import semmle.code.java.security.OverlyLargeRangeQuery +private import semmle.code.java.regex.RegexTreeView::RegexTreeView as TreeView +import codeql.regex.OverlyLargeRangeQuery::Make -RegExpCharacterClass potentialMisparsedCharClass() { +TreeView::RegExpCharacterClass potentialMisparsedCharClass() { // nested char classes are currently misparsed - result.getAChild().(RegExpNormalChar).getValue() = "[" + result.getAChild().(TreeView::RegExpNormalChar).getValue() = "[" } -from RegExpCharacterRange range, string reason +from TreeView::RegExpCharacterRange range, string reason where problem(range, reason) and not range.getParent() = potentialMisparsedCharClass() diff --git a/java/ql/src/Security/CWE/CWE-730/PolynomialReDoS.ql b/java/ql/src/Security/CWE/CWE-730/PolynomialReDoS.ql index 75cd8335fac..a84f1c5213e 100644 --- a/java/ql/src/Security/CWE/CWE-730/PolynomialReDoS.ql +++ b/java/ql/src/Security/CWE/CWE-730/PolynomialReDoS.ql @@ -17,7 +17,9 @@ import java import semmle.code.java.security.regexp.PolynomialReDoSQuery import DataFlow::PathGraph -from DataFlow::PathNode source, DataFlow::PathNode sink, PolynomialBackTrackingTerm regexp +from + DataFlow::PathNode source, DataFlow::PathNode sink, + SuperlinearBackTracking::PolynomialBackTrackingTerm regexp where hasPolynomialReDoSResult(source, sink, regexp) select sink, source, sink, "This $@ that depends on a $@ may run slow on strings " + regexp.getPrefixMessage() + diff --git a/java/ql/src/Security/CWE/CWE-730/ReDoS.ql b/java/ql/src/Security/CWE/CWE-730/ReDoS.ql index 23e258e8915..ca4750fc858 100644 --- a/java/ql/src/Security/CWE/CWE-730/ReDoS.ql +++ b/java/ql/src/Security/CWE/CWE-730/ReDoS.ql @@ -14,12 +14,12 @@ * external/cwe/cwe-400 */ -import java -import semmle.code.java.security.regexp.ExponentialBackTracking +private import semmle.code.java.regex.RegexTreeView::RegexTreeView as TreeView +import codeql.regex.nfa.ExponentialBackTracking::Make as ExponentialBackTracking -from RegExpTerm t, string pump, State s, string prefixMsg +from TreeView::RegExpTerm t, string pump, ExponentialBackTracking::State s, string prefixMsg where - hasReDoSResult(t, pump, s, prefixMsg) and + ExponentialBackTracking::hasReDoSResult(t, pump, s, prefixMsg) and // exclude verbose mode regexes for now not t.getRegex().getAMode() = "VERBOSE" select t, diff --git a/java/ql/test/library-tests/regex/parser/RegexParseTests.ql b/java/ql/test/library-tests/regex/parser/RegexParseTests.ql index 345031a3b2d..4c8d7519f14 100644 --- a/java/ql/test/library-tests/regex/parser/RegexParseTests.ql +++ b/java/ql/test/library-tests/regex/parser/RegexParseTests.ql @@ -1,10 +1,12 @@ import java -import semmle.code.java.regex.RegexTreeView -import semmle.code.java.regex.regex +import semmle.code.java.regex.RegexTreeView as RegexTreeView +import semmle.code.java.regex.regex as Regex -string getQLClases(RegExpTerm t) { result = "[" + strictconcat(t.getPrimaryQLClass(), ",") + "]" } +string getQLClases(RegexTreeView::RegExpTerm t) { + result = "[" + strictconcat(t.getPrimaryQLClass(), ",") + "]" +} -query predicate parseFailures(Regex r, int i) { r.failedToParse(i) } +query predicate parseFailures(Regex::Regex r, int i) { r.failedToParse(i) } -from RegExpTerm t +from RegexTreeView::RegExpTerm t select t, getQLClases(t) diff --git a/java/ql/test/query-tests/security/CWE-730/PolynomialReDoS.ql b/java/ql/test/query-tests/security/CWE-730/PolynomialReDoS.ql index bd600a6d8af..c8c1566a7a4 100644 --- a/java/ql/test/query-tests/security/CWE-730/PolynomialReDoS.ql +++ b/java/ql/test/query-tests/security/CWE-730/PolynomialReDoS.ql @@ -1,4 +1,3 @@ -import java import TestUtilities.InlineExpectationsTest import semmle.code.java.security.regexp.PolynomialReDoSQuery @@ -9,7 +8,10 @@ class HasPolyRedos extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { tag = "hasPolyRedos" and - exists(DataFlow::PathNode source, DataFlow::PathNode sink, PolynomialBackTrackingTerm regexp | + exists( + DataFlow::PathNode source, DataFlow::PathNode sink, + SuperlinearBackTracking::PolynomialBackTrackingTerm regexp + | hasPolynomialReDoSResult(source, sink, regexp) and location = sink.getNode().getLocation() and element = sink.getNode().toString() and diff --git a/java/ql/test/query-tests/security/CWE-730/ReDoS.ql b/java/ql/test/query-tests/security/CWE-730/ReDoS.ql index 288ca57f2e2..7226541bcb2 100644 --- a/java/ql/test/query-tests/security/CWE-730/ReDoS.ql +++ b/java/ql/test/query-tests/security/CWE-730/ReDoS.ql @@ -1,6 +1,7 @@ import java import TestUtilities.InlineExpectationsTest -import semmle.code.java.security.regexp.ExponentialBackTracking +private import semmle.code.java.regex.RegexTreeView::RegexTreeView as TreeView +import codeql.regex.nfa.ExponentialBackTracking::Make as ExponentialBackTracking import semmle.code.java.regex.regex class HasExpRedos extends InlineExpectationsTest { @@ -10,8 +11,8 @@ class HasExpRedos extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { tag = "hasExpRedos" and - exists(RegExpTerm t, string pump, State s, string prefixMsg | - hasReDoSResult(t, pump, s, prefixMsg) and + exists(TreeView::RegExpTerm t, string pump, ExponentialBackTracking::State s, string prefixMsg | + ExponentialBackTracking::hasReDoSResult(t, pump, s, prefixMsg) and not t.getRegex().getAMode() = "VERBOSE" and value = "" and location = t.getLocation() and From a4acea9adf9463cba31368a017aaf646fff16272 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Tue, 1 Nov 2022 12:20:15 +0100 Subject: [PATCH 0250/1420] add change-note --- java/ql/lib/change-notes/2022-10-31-shared-redos-pack.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 java/ql/lib/change-notes/2022-10-31-shared-redos-pack.md diff --git a/java/ql/lib/change-notes/2022-10-31-shared-redos-pack.md b/java/ql/lib/change-notes/2022-10-31-shared-redos-pack.md new file mode 100644 index 00000000000..405ddd1108c --- /dev/null +++ b/java/ql/lib/change-notes/2022-10-31-shared-redos-pack.md @@ -0,0 +1,4 @@ +--- + category: minorAnalysis +--- + * The ReDoS libraries in `semmle.code.java.security.regexp` has been moved to a shared pack inside the `shared/` folder, and the previous location has been deprecated. \ No newline at end of file From b59a9bc95cf7a2a81256248f406d642c0cc93238 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Mon, 7 Nov 2022 14:29:51 +0100 Subject: [PATCH 0251/1420] use instead of a fixed version number --- java/ql/lib/qlpack.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index 32f1ef40ae8..4b103c629a2 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -6,4 +6,4 @@ extractor: java library: true upgrades: upgrades dependencies: - codeql/regex: 0.0.1 \ No newline at end of file + codeql/regex: ${workspace} \ No newline at end of file From 5bbdaad0e54c3706e50ac7c4c08d8d45f1bc2065 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 14 Nov 2022 16:50:39 -0500 Subject: [PATCH 0252/1420] C++: deprecate AST-based GVN --- cpp/ql/lib/change-notes/2022-11-14-deprecate-ast-gvn.md | 6 ++++++ .../code/cpp/valuenumbering/GlobalValueNumberingImpl.qll | 8 ++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 cpp/ql/lib/change-notes/2022-11-14-deprecate-ast-gvn.md diff --git a/cpp/ql/lib/change-notes/2022-11-14-deprecate-ast-gvn.md b/cpp/ql/lib/change-notes/2022-11-14-deprecate-ast-gvn.md new file mode 100644 index 00000000000..eb6bd755c2b --- /dev/null +++ b/cpp/ql/lib/change-notes/2022-11-14-deprecate-ast-gvn.md @@ -0,0 +1,6 @@ +--- +category: deprecated +--- + + +* Deprecated `semmle.code.cpp.valuenumbering.GlobalValueNumberingImpl`. Use `semmle.code.cpp.valuenumbering.GlobalValueNumbering`, which exposes the same API. \ No newline at end of file diff --git a/cpp/ql/lib/semmle/code/cpp/valuenumbering/GlobalValueNumberingImpl.qll b/cpp/ql/lib/semmle/code/cpp/valuenumbering/GlobalValueNumberingImpl.qll index 7dd55dbfde3..10e3d3ba1c2 100644 --- a/cpp/ql/lib/semmle/code/cpp/valuenumbering/GlobalValueNumberingImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/valuenumbering/GlobalValueNumberingImpl.qll @@ -1,4 +1,8 @@ /** + * DEPRECATED: This library has been replaced with a newer version which + * provides better performance and precision. Use + * `semmle.code.cpp.valuenumbering.GlobalValueNumbering` instead. + * * Provides an implementation of Global Value Numbering. * See https://en.wikipedia.org/wiki/Global_value_numbering * @@ -221,7 +225,7 @@ private newtype GvnBase = * expression with this `GVN` and using its `toString` and `getLocation` * methods. */ -class GVN extends GvnBase { +deprecated class GVN extends GvnBase { GVN() { this instanceof GvnBase } /** Gets an expression that has this GVN. */ @@ -503,7 +507,7 @@ private predicate mk_Deref(GVN p, ControlFlowNode dominator, PointerDereferenceE /** Gets the global value number of expression `e`. */ cached -GVN globalValueNumber(Expr e) { +deprecated GVN globalValueNumber(Expr e) { exists(int val, Type t | mk_IntConst(val, t, e) and result = GVN_IntConst(val, t) From 5ea03b1ded4380816c8d3396a652c36841e8a856 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Mon, 14 Nov 2022 22:56:06 +0100 Subject: [PATCH 0253/1420] Update Hapi.qll Add `server` definitions in plugin registration and plugin dependency declaration --- .../ql/lib/semmle/javascript/frameworks/Hapi.qll | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll b/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll index 4c7dd3e5a49..0d04aac34c6 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll @@ -14,16 +14,24 @@ module Hapi { // `server = new Hapi.Server()` this = DataFlow::moduleMember("hapi", "Server").getAnInstantiation() or - // server = Glue.compose(manifest, composeOptions) + // `server = Glue.compose(manifest, composeOptions)` this = DataFlow::moduleMember("@hapi/glue", "compose").getAnInvocation() or - // server inside a plugin - // TODO match `function (server, options)` + // `register (server, options)` exists(Function f | this.(DataFlow::ParameterNode).getParameter() = f.getParameter(0) and + f.getName() = "register" and f.getParameter(0).getName() = "server" and f.getParameter(1).getName() = "options" ) + or + // `const after = function (server) {...};` + // `server.dependency('name', after);` + exists(ServerDefinition server, DataFlow::MethodCallNode call | + call = server.ref().getAMethodCall() and + call.getMethodName() = "dependency" and + this = call.getArgument(1).(DataFlow::FunctionNode).getParameter(0) + ) } } @@ -261,7 +269,7 @@ module Hapi { RouteHandlerCandidate() { exists(string request, string responseToolkit | (request = "request" or request = "req") and - responseToolkit = "h" and + responseToolkit = ["h", "hapi"] and // heuristic: parameter names match the Hapi documentation astNode.getNumParameter() = 2 and astNode.getParameter(0).getName() = request and From 9d7e7735d566a1aa27e8f4f8be3f6cd56b5afb78 Mon Sep 17 00:00:00 2001 From: tiferet Date: Mon, 14 Nov 2022 14:33:08 -0800 Subject: [PATCH 0254/1420] Extract training data: Implement the new query that selects data for training. For now we include clauses that implement logic that is identical to the old queries. Include a temporary wrapper query that converts the resulting data into the format expected by the endpoint pipeline. Move the small pieces of `ExtractEndpointData` that are still needed into `ExtractEndpointDataTraining.qll`. --- .../EndpointCharacteristics.qll | 3 + .../extraction/ExtractEndpointDataTraining.ql | 21 +- .../ExtractEndpointDataTraining.qll | 239 ++++++++++++++++++ 3 files changed, 245 insertions(+), 18 deletions(-) create mode 100644 javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointDataTraining.qll diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll index 33cd559503c..b20031bf228 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll @@ -45,6 +45,9 @@ abstract class EndpointCharacteristic extends string { EndpointType endpointClass, boolean isPositiveIndicator, float confidence ); + /** Indicators with confidence at or above this threshold are considered to be high-confidence indicators. */ + final float getHighConfidenceThreshold() { result = 0.8 } + // The following are some confidence values that are used in practice by the subclasses. They are defined as named // constants here to make it easier to change them in the future. final float maximalConfidence() { result = 1.0 } diff --git a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointDataTraining.ql b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointDataTraining.ql index 20ece497585..3b0bb468ffd 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointDataTraining.ql +++ b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointDataTraining.ql @@ -4,23 +4,8 @@ * Extracts training data we can use to train ML models for ML-powered queries. */ -import javascript -import ExtractEndpointData as ExtractEndpointData +private import ExtractEndpointDataTraining as ExtractEndpointDataTraining -query predicate endpoints( - DataFlow::Node endpoint, string queryName, string key, string value, string valueType -) { - ExtractEndpointData::endpoints(endpoint, queryName, key, value, valueType) and - // only select endpoints that are either Sink or NotASink - ExtractEndpointData::endpoints(endpoint, queryName, "sinkLabel", ["Sink", "NotASink"], "string") and - // do not select endpoints filtered out by end-to-end evaluation - ExtractEndpointData::endpoints(endpoint, queryName, "isExcludedFromEndToEndEvaluation", "false", - "boolean") and - // only select endpoints that can be part of a tainted flow - ExtractEndpointData::endpoints(endpoint, queryName, "isConstantExpression", "false", "boolean") -} +query predicate endpoints = ExtractEndpointDataTraining::reformattedTrainingEndpoints/5; -query predicate tokenFeatures(DataFlow::Node endpoint, string featureName, string featureValue) { - endpoints(endpoint, _, _, _, _) and - ExtractEndpointData::tokenFeatures(endpoint, featureName, featureValue) -} +query predicate tokenFeatures = ExtractEndpointDataTraining::tokenFeatures/3; diff --git a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointDataTraining.qll b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointDataTraining.qll new file mode 100644 index 00000000000..00733c8abae --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointDataTraining.qll @@ -0,0 +1,239 @@ +/* + * For internal use only. + * + * Extracts training data we can use to train ML models for ML-powered queries. + */ + +import javascript +import experimental.adaptivethreatmodeling.EndpointCharacteristics +import experimental.adaptivethreatmodeling.EndpointFeatures as EndpointFeatures +import NoFeaturizationRestrictionsConfig +private import Exclusions as Exclusions +private import Queries +private import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm +private import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm +private import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm +private import experimental.adaptivethreatmodeling.XssATM as XssAtm + +/** + * Gets the set of featureName-featureValue pairs for each endpoint in the training set. + * + * `EndpointFeatures::tokenFeatures` has no results when `featureName` is absent for the endpoint + * `endpoint`. To preserve compatibility with the data pipeline, this relation will instead set + * `featureValue` to the empty string in this case. + */ +query predicate tokenFeatures(DataFlow::Node endpoint, string featureName, string featureValue) { + trainingEndpoints(endpoint, _, _) and + ( + EndpointFeatures::tokenFeatures(endpoint, featureName, featureValue) + or + // Performance note: this creates a Cartesian product between `endpoint` and `featureName`. + featureName = EndpointFeatures::getASupportedFeatureName() and + not exists(string value | EndpointFeatures::tokenFeatures(endpoint, featureName, value)) and + featureValue = "" + ) +} + +/** + * Holds if the given endpoint should be included in the training set as a sample belonging to endpointClass, and has + * the given characteristic. This query uses the endpoint characteristics to select and label endpoints for the training + * set, and provides a list of characteristics for each endpoint in the training set, which is used in the modeling + * code. + * + * Params: + * endpoint: The endpoint to include / exclude. + * endpointClass: The sink type. Each EndpointType has a predicate getEncoding, which specifies the classifier class + * for this sink type. Class 0 is the negative class (non-sink). Each positive int corresponds to a single sink type. + * This gives us the label for the endpoint in the training data. + * characteristic: Provides the list of characteristics that apply to the endpoint, which the modeling code currently + * uses for type balancing. + * + * Note: This predicate will produce multiple tuples for endpoints that have multiple characteristics, which we must + * then group together into a list of characteristics. + */ +query predicate trainingEndpoints( + DataFlow::Node endpoint, EndpointType endpointClass, EndpointCharacteristic characteristic +) { + characteristic.getEndpoints(endpoint) and + // Only consider the source code for the project being analyzed. + exists(endpoint.getFile().getRelativePath()) and + // Only select endpoints that can be part of a tainted flow: Constant expressions always evaluate to a constant + // primitive value. Therefore they can't ever appear in an alert, making them less interesting training examples. + // TODO: Experiment with removing this requirement. + not endpoint.asExpr() instanceof ConstantExpr and + // Do not select endpoints filtered out by end-to-end evaluation. + // TODO: Experiment with removing this requirement. + not Exclusions::isFileExcluded(endpoint.getFile()) and + // Filter out negative examples that also have a LikelyNotASinkReason, because this is currently done here + // https://github.com/github/codeql/blob/387e57546bf7352f7c1cfe781daa1a3799b7063e/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointData.qll#L77 + // TODO: Experiment with removing this requirement. + not ( + endpointClass instanceof NegativeType and + exists(EndpointCharacteristic c | + c.getEndpoints(endpoint) and + c instanceof LikelyNotASinkCharacteristic + ) + ) and + ( + // If the list of characteristics includes positive indicators with high confidence for this class, select this as a + // training sample belonging to the class. + exists(EndpointCharacteristic characteristic2, float confidence | + characteristic2.getEndpoints(endpoint) and + characteristic2.getImplications(endpointClass, true, confidence) and + confidence >= characteristic2.getHighConfidenceThreshold() + ) and + ( + // Temporarily limit this only to positive classes. For negative classes, additionally select only endpoints that + // have no high confidence indicators that they are sinks, because this is what was previously done. + // TODO: Experiment with removing this requirement, and instead ensuring that an endpoint never has both a high + // confidence indicator that it _is_ a sink and a high confidence indicator that it is _not_ a sink. + not endpointClass instanceof NegativeType + or + not exists(EndpointCharacteristic characteristic3, float confidence3, EndpointType posClass | + characteristic3.getEndpoints(endpoint) and + characteristic3.getImplications(posClass, true, confidence3) and + confidence3 >= characteristic3.getHighConfidenceThreshold() and + not posClass instanceof NegativeType + ) + ) + or + // If the list of characteristics includes negative indicators with high confidence for all classes other than 0, + // select this as a training sample of class 0 (this means we had query-specific characteristics to decide this + // endpoint isn’t a sink for each of our sink types). + endpointClass instanceof NegativeType and + forall(EndpointType otherClass | not otherClass instanceof NegativeType | + exists(EndpointCharacteristic characteristic2, float confidence | + characteristic2.getEndpoints(endpoint) and + characteristic2.getImplications(otherClass, false, confidence) and + confidence >= characteristic2.getHighConfidenceThreshold() + ) + ) + ) +} + +/** + * Temporary: + * Reformat the training data that was extracted with the new logic to match the format produced by the old predicate. + * This is the format expected by the endpoint pipeline. + */ +query predicate reformattedTrainingEndpoints( + DataFlow::Node endpoint, string queryName, string key, string value, string valueType +) { + trainingEndpoints(endpoint, _, _) and + exists(Query query | + queryName = query.getName() and + // For sinks, only list that sink type, but for non-sinks, list all sink types. + ( + exists(EndpointType endpointClass | + endpointClass.getDescription().matches(queryName + "%") and + not endpointClass instanceof NegativeType and + trainingEndpoints(endpoint, endpointClass, _) + ) + or + exists(EndpointType endpointClass | + endpointClass instanceof NegativeType and + trainingEndpoints(endpoint, endpointClass, _) + ) + ) and + ( + // NOTE: We don't use hasFlowFromSource in training, so we could just hardcode it to be false. + key = "hasFlowFromSource" and + ( + if FlowFromSource::hasFlowFromSource(endpoint, query) + then value = "true" + else value = "false" + ) and + valueType = "boolean" + or + // Constant expressions always evaluate to a constant primitive value. Therefore they can't ever + // appear in an alert, making them less interesting training examples. + key = "isConstantExpression" and + (if endpoint.asExpr() instanceof ConstantExpr then value = "true" else value = "false") and + valueType = "boolean" + or + // Holds if alerts involving the endpoint are excluded from the end-to-end evaluation. + key = "isExcludedFromEndToEndEvaluation" and + (if Exclusions::isFileExcluded(endpoint.getFile()) then value = "true" else value = "false") and + valueType = "boolean" + or + // The label for this query, considering the endpoint as a sink. + key = "sinkLabel" and + valueType = "string" and + value = "Sink" and + exists(EndpointType endpointClass | + endpointClass.getDescription().matches(queryName + "%") and + not endpointClass instanceof NegativeType and + trainingEndpoints(endpoint, endpointClass, _) + ) + or + key = "sinkLabel" and + valueType = "string" and + value = "NotASink" and + exists(EndpointType endpointClass | + endpointClass instanceof NegativeType and + trainingEndpoints(endpoint, endpointClass, _) + ) + or + // The reason, or reasons, why the endpoint was labeled NotASink for this query, only for negative examples. + key = "notASinkReason" and + exists(EndpointCharacteristic characteristic, EndpointType endpointClass | + characteristic.getEndpoints(endpoint) and + characteristic.getImplications(endpointClass, true, _) and + endpointClass instanceof NegativeType and + value = characteristic + ) and + // Don't include a notASinkReason for endpoints that are also known sinks. + not exists(EndpointCharacteristic characteristic3, float confidence3, EndpointType posClass | + characteristic3.getEndpoints(endpoint) and + characteristic3.getImplications(posClass, true, confidence3) and + confidence3 >= characteristic3.getHighConfidenceThreshold() and + not posClass instanceof NegativeType + ) and + valueType = "string" + ) + ) +} + +/** + * Gets the ATM data flow configuration for the specified query. + * TODO: Delete this once we are no longer surfacing `hasFlowFromSource`. + */ +DataFlow::Configuration getDataFlowCfg(Query query) { + query instanceof NosqlInjectionQuery and result instanceof NosqlInjectionAtm::Configuration + or + query instanceof SqlInjectionQuery and result instanceof SqlInjectionAtm::Configuration + or + query instanceof TaintedPathQuery and result instanceof TaintedPathAtm::Configuration + or + query instanceof XssQuery and result instanceof XssAtm::Configuration +} + +// TODO: Delete this once we are no longer surfacing `hasFlowFromSource`. +module FlowFromSource { + predicate hasFlowFromSource(DataFlow::Node endpoint, Query q) { + exists(Configuration cfg | cfg.getQuery() = q | cfg.hasFlow(_, endpoint)) + } + + /** + * A data flow configuration that replicates the data flow configuration for a specific query, but + * replaces the set of sinks with the set of endpoints we're extracting. + * + * We use this to find out when there is flow to a particular endpoint from a known source. + * + * This configuration behaves in a very similar way to the `ForwardExploringConfiguration` class + * from the CodeQL standard libraries for JavaScript. + */ + private class Configuration extends DataFlow::Configuration { + Query q; + + Configuration() { this = getDataFlowCfg(q) } + + Query getQuery() { result = q } + + /** Holds if `sink` is an endpoint we're extracting. */ + override predicate isSink(DataFlow::Node sink) { any() } + + /** Holds if `sink` is an endpoint we're extracting. */ + override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel lbl) { exists(lbl) } + } +} From b47723d6070abab1a8231e0517aba38ffd081c8a Mon Sep 17 00:00:00 2001 From: tiferet Date: Mon, 14 Nov 2022 14:57:59 -0800 Subject: [PATCH 0255/1420] Delete `ExtractEndpointData`. Also remove the associated test files. --- .../modelbuilding/DebugResultInclusion.ql | 4 +- .../extraction/ExtractEndpointData.ql | 11 - .../extraction/ExtractEndpointData.qll | 215 ------------------ .../ExtractEndpointData.qlref | 1 - .../ExtractEndpointData.qlref | 1 - 5 files changed, 2 insertions(+), 230 deletions(-) delete mode 100644 javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointData.ql delete mode 100644 javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointData.qll delete mode 100644 javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/ExtractEndpointData.qlref delete mode 100644 javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_unit_tests/ExtractEndpointData.qlref diff --git a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/DebugResultInclusion.ql b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/DebugResultInclusion.ql index 28a95268a57..c5654e86a12 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/DebugResultInclusion.ql +++ b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/DebugResultInclusion.ql @@ -11,7 +11,7 @@ import javascript import experimental.adaptivethreatmodeling.ATMConfig -import extraction.ExtractEndpointData +import extraction.ExtractEndpointDataTraining string getAReasonSinkExcluded(DataFlow::Node sinkCandidate, Query query) { query instanceof NosqlInjectionQuery and @@ -33,7 +33,7 @@ string getDescriptionForAlertCandidate( ) { result = "excluded[reason=" + getAReasonSinkExcluded(sinkCandidate, query) + "]" or - getAtmCfg(query).isKnownSink(sinkCandidate) and + getDataFlowCfg(query).(AtmConfig).isKnownSink(sinkCandidate) and result = "excluded[reason=known-sink]" or not exists(getAReasonSinkExcluded(sinkCandidate, query)) and diff --git a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointData.ql b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointData.ql deleted file mode 100644 index 73dab4af324..00000000000 --- a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointData.ql +++ /dev/null @@ -1,11 +0,0 @@ -/* - * For internal use only. - * - * Extracts training and evaluation data we can use to train ML models for ML-powered queries. - */ - -import ExtractEndpointData as ExtractEndpointData - -query predicate endpoints = ExtractEndpointData::endpoints/5; - -query predicate tokenFeatures = ExtractEndpointData::tokenFeatures/3; diff --git a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointData.qll b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointData.qll deleted file mode 100644 index af91933b7a4..00000000000 --- a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointData.qll +++ /dev/null @@ -1,215 +0,0 @@ -/* - * For internal use only. - * - * Library code for training and evaluation data we can use to train ML models for ML-powered - * queries. - */ - -import javascript -import Exclusions as Exclusions -import evaluation.EndToEndEvaluation as EndToEndEvaluation -import experimental.adaptivethreatmodeling.ATMConfig -import experimental.adaptivethreatmodeling.CoreKnowledge as CoreKnowledge -import experimental.adaptivethreatmodeling.EndpointFeatures as EndpointFeatures -import experimental.adaptivethreatmodeling.EndpointScoring as EndpointScoring -import experimental.adaptivethreatmodeling.EndpointTypes -import experimental.adaptivethreatmodeling.FilteringReasons -import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm - -/** DEPRECATED: Alias for NosqlInjectionAtm */ -deprecated module NosqlInjectionATM = NosqlInjectionAtm; - -import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm - -/** DEPRECATED: Alias for SqlInjectionAtm */ -deprecated module SqlInjectionATM = SqlInjectionAtm; - -import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm - -/** DEPRECATED: Alias for TaintedPathAtm */ -deprecated module TaintedPathATM = TaintedPathAtm; - -import experimental.adaptivethreatmodeling.XssATM as XssAtm - -/** DEPRECATED: Alias for XssAtm */ -deprecated module XssATM = XssAtm; - -import Labels -import NoFeaturizationRestrictionsConfig -import Queries - -/** Gets the ATM configuration object for the specified query. */ -AtmConfig getAtmCfg(Query query) { - query instanceof NosqlInjectionQuery and - result instanceof NosqlInjectionAtm::NosqlInjectionAtmConfig - or - query instanceof SqlInjectionQuery and result instanceof SqlInjectionAtm::SqlInjectionAtmConfig - or - query instanceof TaintedPathQuery and result instanceof TaintedPathAtm::TaintedPathAtmConfig - or - query instanceof XssQuery and result instanceof XssAtm::DomBasedXssAtmConfig -} - -/** DEPRECATED: Alias for getAtmCfg */ -deprecated ATMConfig getATMCfg(Query query) { result = getAtmCfg(query) } - -/** Gets the ATM data flow configuration for the specified query. */ -DataFlow::Configuration getDataFlowCfg(Query query) { - query instanceof NosqlInjectionQuery and result instanceof NosqlInjectionAtm::Configuration - or - query instanceof SqlInjectionQuery and result instanceof SqlInjectionAtm::Configuration - or - query instanceof TaintedPathQuery and result instanceof TaintedPathAtm::Configuration - or - query instanceof XssQuery and result instanceof XssAtm::Configuration -} - -/** Gets a known sink for the specified query. */ -private DataFlow::Node getASink(Query query) { - getAtmCfg(query).isKnownSink(result) and - // Only consider the source code for the project being analyzed. - exists(result.getFile().getRelativePath()) -} - -/** Gets a data flow node that is known not to be a sink for the specified query. */ -private DataFlow::Node getANotASink(NotASinkReason reason) { - CoreKnowledge::isOtherModeledArgument(result, reason) and - // Some endpoints can be assigned both a `NotASinkReason` and a `LikelyNotASinkReason`. We - // consider these endpoints to be `LikelyNotASink`, therefore this line excludes them from the - // definition of `NotASink`. - not CoreKnowledge::isOtherModeledArgument(result, any(LikelyNotASinkReason t)) and - not result = getASink(_) and - // Only consider the source code for the project being analyzed. - exists(result.getFile().getRelativePath()) -} - -/** - * Gets a data flow node whose label is unknown for the specified query. - * - * In other words, this is an endpoint that is not `Sink`, `NotASink`, or `LikelyNotASink` for the - * specified query. - */ -private DataFlow::Node getAnUnknown(Query query) { - getAtmCfg(query).isEffectiveSink(result) and - // Effective sinks should exclude sinks but this is a defensive requirement - not result = getASink(query) and - // Effective sinks should exclude NotASink but for some queries (e.g. Xss) this is currently not always the case and - // so this is a defensive requirement - not result = getANotASink(_) and - // Only consider the source code for the project being analyzed. - exists(result.getFile().getRelativePath()) -} - -/** Gets the query-specific sink label for the given endpoint, if such a label exists. */ -private EndpointLabel getSinkLabelForEndpoint(DataFlow::Node endpoint, Query query) { - endpoint = getASink(query) and result instanceof SinkLabel - or - endpoint = getANotASink(_) and result instanceof NotASinkLabel - or - endpoint = getAnUnknown(query) and result instanceof UnknownLabel -} - -/** Gets an endpoint that should be extracted. */ -DataFlow::Node getAnEndpoint(Query query) { exists(getSinkLabelForEndpoint(result, query)) } - -/** - * Endpoints and associated metadata. - * - * Note that we draw a distinction between _features_, that are provided to the model at training - * and query time, and _metadata_, that is only provided to the model at training time. - * - * Internal: See the design document for - * [extensible extraction queries](https://docs.google.com/document/d/1g3ci2Nf1hGMG6ZUP0Y4PqCy_8elcoC_dhBvgTxdAWpg) - * for technical information about the design of this predicate. - */ -predicate endpoints( - DataFlow::Node endpoint, string queryName, string key, string value, string valueType -) { - exists(Query query | - // Only provide metadata for labelled endpoints, since we do not extract all endpoints. - endpoint = getAnEndpoint(query) and - queryName = query.getName() and - ( - // Holds if there is a taint flow path from a known source to the endpoint - key = "hasFlowFromSource" and - ( - if FlowFromSource::hasFlowFromSource(endpoint, query) - then value = "true" - else value = "false" - ) and - valueType = "boolean" - or - // Constant expressions always evaluate to a constant primitive value. Therefore they can't ever - // appear in an alert, making them less interesting training examples. - key = "isConstantExpression" and - (if endpoint.asExpr() instanceof ConstantExpr then value = "true" else value = "false") and - valueType = "boolean" - or - // Holds if alerts involving the endpoint are excluded from the end-to-end evaluation. - key = "isExcludedFromEndToEndEvaluation" and - (if Exclusions::isFileExcluded(endpoint.getFile()) then value = "true" else value = "false") and - valueType = "boolean" - or - // The label for this query, considering the endpoint as a sink. - key = "sinkLabel" and - value = getSinkLabelForEndpoint(endpoint, query).getEncoding() and - valueType = "string" - or - // The reason, or reasons, why the endpoint was labeled NotASink for this query. - key = "notASinkReason" and - exists(FilteringReason reason | - endpoint = getANotASink(reason) and - value = reason.getDescription() - ) and - valueType = "string" - ) - ) -} - -/** - * `EndpointFeatures::tokenFeatures` has no results when `featureName` is absent for the endpoint - * `endpoint`. To preserve compatibility with the data pipeline, this relation will instead set - * `featureValue` to the empty string in this case. - */ -predicate tokenFeatures(DataFlow::Node endpoint, string featureName, string featureValue) { - endpoints(endpoint, _, _, _, _) and - ( - EndpointFeatures::tokenFeatures(endpoint, featureName, featureValue) - or - // Performance note: this creates a Cartesian product between `endpoint` and `featureName`. - featureName = EndpointFeatures::getASupportedFeatureName() and - not exists(string value | EndpointFeatures::tokenFeatures(endpoint, featureName, value)) and - featureValue = "" - ) -} - -module FlowFromSource { - predicate hasFlowFromSource(DataFlow::Node endpoint, Query q) { - exists(Configuration cfg | cfg.getQuery() = q | cfg.hasFlow(_, endpoint)) - } - - /** - * A data flow configuration that replicates the data flow configuration for a specific query, but - * replaces the set of sinks with the set of endpoints we're extracting. - * - * We use this to find out when there is flow to a particular endpoint from a known source. - * - * This configuration behaves in a very similar way to the `ForwardExploringConfiguration` class - * from the CodeQL standard libraries for JavaScript. - */ - private class Configuration extends DataFlow::Configuration { - Query q; - - Configuration() { this = getDataFlowCfg(q) } - - Query getQuery() { result = q } - - /** Holds if `sink` is an endpoint we're extracting. */ - override predicate isSink(DataFlow::Node sink) { sink = getAnEndpoint(q) } - - /** Holds if `sink` is an endpoint we're extracting. */ - override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel lbl) { - sink = getAnEndpoint(q) and exists(lbl) - } - } -} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/ExtractEndpointData.qlref b/javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/ExtractEndpointData.qlref deleted file mode 100644 index b1e84d268d9..00000000000 --- a/javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/ExtractEndpointData.qlref +++ /dev/null @@ -1 +0,0 @@ -extraction/ExtractEndpointData.ql diff --git a/javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_unit_tests/ExtractEndpointData.qlref b/javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_unit_tests/ExtractEndpointData.qlref deleted file mode 100644 index b1e84d268d9..00000000000 --- a/javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_unit_tests/ExtractEndpointData.qlref +++ /dev/null @@ -1 +0,0 @@ -extraction/ExtractEndpointData.ql From 6b7612fed712f2b2f506113382e03af94bfe64f2 Mon Sep 17 00:00:00 2001 From: tiferet Date: Mon, 14 Nov 2022 15:33:46 -0800 Subject: [PATCH 0256/1420] Fix import errors in `DebugResultInclusion.ql` --- .../extraction/ExtractEndpointDataTraining.qll | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointDataTraining.qll b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointDataTraining.qll index 00733c8abae..0eff964038d 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointDataTraining.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointDataTraining.qll @@ -9,11 +9,11 @@ import experimental.adaptivethreatmodeling.EndpointCharacteristics import experimental.adaptivethreatmodeling.EndpointFeatures as EndpointFeatures import NoFeaturizationRestrictionsConfig private import Exclusions as Exclusions -private import Queries -private import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm -private import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm -private import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm -private import experimental.adaptivethreatmodeling.XssATM as XssAtm +import Queries +import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm +import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm +import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm +import experimental.adaptivethreatmodeling.XssATM as XssAtm /** * Gets the set of featureName-featureValue pairs for each endpoint in the training set. From 9ecff0723cbd15b0198345015ef67b17ee091b1b Mon Sep 17 00:00:00 2001 From: tiferet Date: Mon, 14 Nov 2022 16:34:24 -0800 Subject: [PATCH 0257/1420] Fix non-ascii character in docs --- .../modelbuilding/extraction/ExtractEndpointDataTraining.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointDataTraining.qll b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointDataTraining.qll index 0eff964038d..533f7bb2d9d 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointDataTraining.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointDataTraining.qll @@ -99,7 +99,7 @@ query predicate trainingEndpoints( or // If the list of characteristics includes negative indicators with high confidence for all classes other than 0, // select this as a training sample of class 0 (this means we had query-specific characteristics to decide this - // endpoint isn’t a sink for each of our sink types). + // endpoint isn't a sink for each of our sink types). endpointClass instanceof NegativeType and forall(EndpointType otherClass | not otherClass instanceof NegativeType | exists(EndpointCharacteristic characteristic2, float confidence | From eda028721e29eaefe31cd1c16fd678159e280315 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 15 Nov 2022 09:17:29 +0100 Subject: [PATCH 0258/1420] C#: Update all nuget packages --- .../Semmle.Autobuild.CSharp.Tests.csproj | 14 +++++++------- .../Semmle.Autobuild.CSharp.csproj | 12 ++++++------ .../Semmle.Autobuild.Shared.csproj | 6 +++--- .../Semmle.Extraction.CIL.csproj | 4 ++-- .../Semmle.Extraction.CSharp.Standalone.csproj | 16 ++++++++-------- .../Semmle.Extraction.CSharp.csproj | 12 ++++++------ .../Semmle.Extraction.Tests.csproj | 18 +++++++++--------- .../Semmle.Extraction/Semmle.Extraction.csproj | 4 ++-- .../Semmle.Util.Tests/Semmle.Util.Tests.csproj | 8 ++++---- .../posix-only/dotnet_test/dotnet_test.csproj | 8 ++++---- 10 files changed, 51 insertions(+), 51 deletions(-) diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/Semmle.Autobuild.CSharp.Tests.csproj b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/Semmle.Autobuild.CSharp.Tests.csproj index a91868fbd80..1d5ac39a96f 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/Semmle.Autobuild.CSharp.Tests.csproj +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/Semmle.Autobuild.CSharp.Tests.csproj @@ -6,17 +6,17 @@ enable - - - - + + + + all runtime; build; native; contentfiles; analyzers - + - - + + \ No newline at end of file diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj b/csharp/autobuilder/Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj index 4f712a651d4..d05daec1de0 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj @@ -11,15 +11,15 @@ enable - + - - + + - - - + + + \ No newline at end of file diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj b/csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj index d3e24dd6ad5..f76a28f6a0b 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj @@ -8,12 +8,12 @@ enable - + - + - + \ No newline at end of file diff --git a/csharp/extractor/Semmle.Extraction.CIL/Semmle.Extraction.CIL.csproj b/csharp/extractor/Semmle.Extraction.CIL/Semmle.Extraction.CIL.csproj index 8dc6d8a9f76..2d9263eb24e 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Semmle.Extraction.CIL.csproj +++ b/csharp/extractor/Semmle.Extraction.CIL/Semmle.Extraction.CIL.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -24,7 +24,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Semmle.Extraction.CSharp.Standalone.csproj b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Semmle.Extraction.CSharp.Standalone.csproj index 5546278e93c..c02999c9acd 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Semmle.Extraction.CSharp.Standalone.csproj +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Semmle.Extraction.CSharp.Standalone.csproj @@ -12,17 +12,17 @@ enable - - + + - + - - - - - + + + + + \ No newline at end of file diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Semmle.Extraction.CSharp.csproj b/csharp/extractor/Semmle.Extraction.CSharp/Semmle.Extraction.CSharp.csproj index 77e204997b3..02d1e96558d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Semmle.Extraction.CSharp.csproj +++ b/csharp/extractor/Semmle.Extraction.CSharp/Semmle.Extraction.CSharp.csproj @@ -10,15 +10,15 @@ enable - - - + + + - + - - + + \ No newline at end of file diff --git a/csharp/extractor/Semmle.Extraction.Tests/Semmle.Extraction.Tests.csproj b/csharp/extractor/Semmle.Extraction.Tests/Semmle.Extraction.Tests.csproj index c4fa93f9697..e75d4c779ae 100644 --- a/csharp/extractor/Semmle.Extraction.Tests/Semmle.Extraction.Tests.csproj +++ b/csharp/extractor/Semmle.Extraction.Tests/Semmle.Extraction.Tests.csproj @@ -6,19 +6,19 @@ enable - - - - + + + + all runtime; build; native; contentfiles; analyzers - + - - - - + + + + \ No newline at end of file diff --git a/csharp/extractor/Semmle.Extraction/Semmle.Extraction.csproj b/csharp/extractor/Semmle.Extraction/Semmle.Extraction.csproj index f647003ebf0..b93f3a40043 100644 --- a/csharp/extractor/Semmle.Extraction/Semmle.Extraction.csproj +++ b/csharp/extractor/Semmle.Extraction/Semmle.Extraction.csproj @@ -12,13 +12,13 @@ TRACE;DEBUG;DEBUG_LABELS - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + \ No newline at end of file diff --git a/csharp/extractor/Semmle.Util.Tests/Semmle.Util.Tests.csproj b/csharp/extractor/Semmle.Util.Tests/Semmle.Util.Tests.csproj index 7447a7e82c1..9edca915bd0 100644 --- a/csharp/extractor/Semmle.Util.Tests/Semmle.Util.Tests.csproj +++ b/csharp/extractor/Semmle.Util.Tests/Semmle.Util.Tests.csproj @@ -6,14 +6,14 @@ enable - - + + all runtime; build; native; contentfiles; analyzers - + - + \ No newline at end of file diff --git a/csharp/ql/integration-tests/posix-only/dotnet_test/dotnet_test.csproj b/csharp/ql/integration-tests/posix-only/dotnet_test/dotnet_test.csproj index 50852359bb8..a2fd6979adf 100644 --- a/csharp/ql/integration-tests/posix-only/dotnet_test/dotnet_test.csproj +++ b/csharp/ql/integration-tests/posix-only/dotnet_test/dotnet_test.csproj @@ -8,10 +8,10 @@ - - - - + + + + From 279ba60eb1cd9c928a45d204dcf8387223a47779 Mon Sep 17 00:00:00 2001 From: Gustav Munkby Date: Tue, 15 Nov 2022 10:00:53 +0100 Subject: [PATCH 0259/1420] Refactor package path extraction In preparation for pulling all package information at once. --- go/extractor/extractor.go | 34 ++++++++++++++-------------------- go/extractor/util/util.go | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/go/extractor/extractor.go b/go/extractor/extractor.go index 1be2bfef224..82ed3be6f69 100644 --- a/go/extractor/extractor.go +++ b/go/extractor/extractor.go @@ -103,10 +103,8 @@ func ExtractWithFlags(buildFlags []string, patterns []string) error { extractUniverseScope() log.Println("Done extracting universe scope.") - // a map of package path to package root directory (currently the module root or the source directory) - pkgRoots := make(map[string]string) - // a map of package path to source code directory - pkgDirs := make(map[string]string) + // a map of package path to source directory and module root directory + pkgInfos := make(map[string]util.PkgInfo) // root directories of packages that we want to extract wantedRoots := make(map[string]bool) @@ -116,16 +114,8 @@ func ExtractWithFlags(buildFlags []string, patterns []string) error { }, func(pkg *packages.Package) { log.Printf("Processing package %s.", pkg.PkgPath) - if _, ok := pkgRoots[pkg.PkgPath]; !ok { - mdir := util.GetModDir(pkg.PkgPath, modFlags...) - pdir := util.GetPkgDir(pkg.PkgPath, modFlags...) - // GetModDir returns the empty string if the module directory cannot be determined, e.g. if the package - // is not using modules. If this is the case, fall back to the package directory - if mdir == "" { - mdir = pdir - } - pkgRoots[pkg.PkgPath] = mdir - pkgDirs[pkg.PkgPath] = pdir + if _, ok := pkgInfos[pkg.PkgPath]; !ok { + pkgInfos[pkg.PkgPath] = util.GetPkgInfo(pkg.PkgPath, modFlags...) } log.Printf("Extracting types for package %s.", pkg.PkgPath) @@ -152,11 +142,14 @@ func ExtractWithFlags(buildFlags []string, patterns []string) error { }) for _, pkg := range pkgs { - if pkgRoots[pkg.PkgPath] == "" { + pkgInfo, ok := pkgInfos[pkg.PkgPath] + if !ok || pkgInfo.PkgDir == "" { log.Fatalf("Unable to get a source directory for input package %s.", pkg.PkgPath) } - wantedRoots[pkgRoots[pkg.PkgPath]] = true - wantedRoots[pkgDirs[pkg.PkgPath]] = true + wantedRoots[pkgInfo.PkgDir] = true + if pkgInfo.ModDir != "" { + wantedRoots[pkgInfo.ModDir] = true + } } log.Println("Done processing dependencies.") @@ -174,7 +167,8 @@ func ExtractWithFlags(buildFlags []string, patterns []string) error { return true }, func(pkg *packages.Package) { for root, _ := range wantedRoots { - relDir, err := filepath.Rel(root, pkgDirs[pkg.PkgPath]) + pkgInfo := pkgInfos[pkg.PkgPath] + relDir, err := filepath.Rel(root, pkgInfo.PkgDir) if err != nil || noExtractRe.MatchString(relDir) { // if the path can't be made relative or matches the noExtract regexp skip it continue @@ -182,8 +176,8 @@ func ExtractWithFlags(buildFlags []string, patterns []string) error { extraction.extractPackage(pkg) - if pkgRoots[pkg.PkgPath] != "" { - modPath := filepath.Join(pkgRoots[pkg.PkgPath], "go.mod") + if pkgInfo.ModDir != "" { + modPath := filepath.Join(pkgInfo.ModDir, "go.mod") if util.FileExists(modPath) { log.Printf("Extracting %s", modPath) start := time.Now() diff --git a/go/extractor/util/util.go b/go/extractor/util/util.go index 5725c03d5b6..31c202678f4 100644 --- a/go/extractor/util/util.go +++ b/go/extractor/util/util.go @@ -54,6 +54,21 @@ func runGoListWithEnv(format string, pkgpath string, additionalEnv []string, fla return strings.TrimSpace(string(out)), nil } +// PkgInfo holds package directory and module directory (if any) for a package +type PkgInfo struct { + PkgDir string // the directory directly containing source code of this package + ModDir string // the module directory containing this package, empty if not a module +} + +// GetPkgInfo fills the package info structure for the specified package path. +// It passes the `go list` the flags specified by `flags`. +func GetPkgInfo(pkgpath string, flags ...string) PkgInfo { + return PkgInfo{ + PkgDir: GetModDir(pkgpath, flags...), + ModDir: GetPkgDir(pkgpath, flags...), + } +} + // GetModDir gets the absolute directory of the module containing the package with path // `pkgpath`. It passes the `go list` the flags specified by `flags`. func GetModDir(pkgpath string, flags ...string) string { From ec3578364ebc92ff01a3b22baa43147b5fb037dd Mon Sep 17 00:00:00 2001 From: Stephan Brandauer Date: Tue, 15 Nov 2022 10:17:38 +0100 Subject: [PATCH 0260/1420] remove superfluous class in EndpointCharacteristics hierarchy --- .../EndpointCharacteristics.qll | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll index 33cd559503c..e1539a504ec 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll @@ -135,20 +135,11 @@ private class NosqlInjectionSinkCharacteristic extends EndpointCharacteristic { * Characteristics that are indicative of not being a sink of any type. */ -/** - * A characteristic that is an indicator of not being a sink of any type, because it's an argument that has a manual - * model. - */ -abstract private class OtherModeledArgumentCharacteristic extends EndpointCharacteristic { - bindingset[this] - OtherModeledArgumentCharacteristic() { any() } -} - /** * A characteristic that is an indicator of not being a sink of any type, because it's an argument to a function of a * builtin object. */ -abstract private class ArgumentToBuiltinFunctionCharacteristic extends OtherModeledArgumentCharacteristic { +abstract private class ArgumentToBuiltinFunctionCharacteristic extends EndpointCharacteristic { bindingset[this] ArgumentToBuiltinFunctionCharacteristic() { any() } } @@ -156,7 +147,7 @@ abstract private class ArgumentToBuiltinFunctionCharacteristic extends OtherMode /** * A high-confidence characteristic that indicates that an endpoint is not a sink of any type. */ -abstract private class NotASinkCharacteristic extends OtherModeledArgumentCharacteristic { +abstract private class NotASinkCharacteristic extends EndpointCharacteristic { bindingset[this] NotASinkCharacteristic() { any() } @@ -175,7 +166,7 @@ abstract private class NotASinkCharacteristic extends OtherModeledArgumentCharac * TODO: This class is currently not private, because the current extraction logic explicitly avoids including these * endpoints in the training data. We might want to change this in the future. */ -abstract class LikelyNotASinkCharacteristic extends OtherModeledArgumentCharacteristic { +abstract class LikelyNotASinkCharacteristic extends EndpointCharacteristic { bindingset[this] LikelyNotASinkCharacteristic() { any() } From a293239bd5324ffd0892b53518e9d82c816fafee Mon Sep 17 00:00:00 2001 From: Gustav Munkby Date: Tue, 15 Nov 2022 10:30:56 +0100 Subject: [PATCH 0261/1420] Accelerating go-extractor by using 'go list -deps' Resurrect https://github.com/github/codeql-go/pull/554, but behind an environment variable as to avoid the broken builds noted in https://github.com/github/codeql/issues/9304, but still allowing some people to opt in to the much faster approach. --- go/extractor/extractor.go | 10 ++++++ go/extractor/util/util.go | 75 ++++++++++++++++++++++++++++++++++----- 2 files changed, 77 insertions(+), 8 deletions(-) diff --git a/go/extractor/extractor.go b/go/extractor/extractor.go index 82ed3be6f69..4d479f6384b 100644 --- a/go/extractor/extractor.go +++ b/go/extractor/extractor.go @@ -108,6 +108,16 @@ func ExtractWithFlags(buildFlags []string, patterns []string) error { // root directories of packages that we want to extract wantedRoots := make(map[string]bool) + if os.Getenv("CODEQL_EXTRACTOR_GO_FAST_PACKAGE_INFO") != "" { + log.Printf("Running go list to resolve package and module directories.") + // get all packages information + pkgInfos, err = util.GetPkgsInfo(patterns, true, modFlags...) + if err != nil { + log.Fatalf("Error getting dependency package or module directories: %v.", err) + } + log.Printf("Done running go list deps: resolved %d packages.", len(pkgInfos)) + } + // Do a post-order traversal and extract the package scope of each package packages.Visit(pkgs, func(pkg *packages.Package) bool { return true diff --git a/go/extractor/util/util.go b/go/extractor/util/util.go index 31c202678f4..71ca932f366 100644 --- a/go/extractor/util/util.go +++ b/go/extractor/util/util.go @@ -1,7 +1,9 @@ package util import ( + "encoding/json" "errors" + "io" "log" "os" "os/exec" @@ -31,13 +33,13 @@ func Getenv(key string, aliases ...string) string { // runGoList is a helper function for running go list with format `format` and flags `flags` on // package `pkgpath`. -func runGoList(format string, pkgpath string, flags ...string) (string, error) { - return runGoListWithEnv(format, pkgpath, nil, flags...) +func runGoList(format string, patterns []string, flags ...string) (string, error) { + return runGoListWithEnv(format, patterns, nil, flags...) } -func runGoListWithEnv(format string, pkgpath string, additionalEnv []string, flags ...string) (string, error) { +func runGoListWithEnv(format string, patterns []string, additionalEnv []string, flags ...string) (string, error) { args := append([]string{"list", "-e", "-f", format}, flags...) - args = append(args, pkgpath) + args = append(args, patterns...) cmd := exec.Command("go", args...) cmd.Env = append(os.Environ(), additionalEnv...) out, err := cmd.Output() @@ -60,6 +62,63 @@ type PkgInfo struct { ModDir string // the module directory containing this package, empty if not a module } +// GetPkgsInfo gets the absolute module and package root directories for the packages matched by the +// patterns `patterns`. It passes to `go list` the flags specified by `flags`. If `includingDeps` +// is true, all dependencies will also be included. +func GetPkgsInfo(patterns []string, includingDeps bool, flags ...string) (map[string]PkgInfo, error) { + // enable module mode so that we can find a module root if it exists, even if go module support is + // disabled by a build + if includingDeps { + // the flag `-deps` causes all dependencies to be retrieved + flags = append(flags, "-deps") + } + + // using -json overrides -f format + output, err := runGoList("", patterns, append(flags, "-json")...) + if err != nil { + return nil, err + } + + // the output of `go list -json` is a stream of json object + type goListPkgInfo struct { + ImportPath string + Dir string + Module *struct { + Dir string + } + } + pkgInfoMapping := make(map[string]PkgInfo) + streamDecoder := json.NewDecoder(strings.NewReader(output)) + for { + var pkgInfo goListPkgInfo + decErr := streamDecoder.Decode(&pkgInfo) + if decErr == io.EOF { + break + } + if decErr != nil { + log.Printf("Error decoding output of go list -json: %s", err.Error()) + return nil, decErr + } + pkgAbsDir, err := filepath.Abs(pkgInfo.Dir) + if err != nil { + log.Printf("Unable to make package dir %s absolute: %s", pkgInfo.Dir, err.Error()) + } + var modAbsDir string + if pkgInfo.Module != nil { + modAbsDir, err = filepath.Abs(pkgInfo.Module.Dir) + if err != nil { + log.Printf("Unable to make module dir %s absolute: %s", pkgInfo.Module.Dir, err.Error()) + } + } + pkgInfoMapping[pkgInfo.ImportPath] = PkgInfo{ + PkgDir: pkgAbsDir, + ModDir: modAbsDir, + } + } + return pkgInfoMapping, nil +} + + // GetPkgInfo fills the package info structure for the specified package path. // It passes the `go list` the flags specified by `flags`. func GetPkgInfo(pkgpath string, flags ...string) PkgInfo { @@ -74,13 +133,13 @@ func GetPkgInfo(pkgpath string, flags ...string) PkgInfo { func GetModDir(pkgpath string, flags ...string) string { // enable module mode so that we can find a module root if it exists, even if go module support is // disabled by a build - mod, err := runGoListWithEnv("{{.Module}}", pkgpath, []string{"GO111MODULE=on"}, flags...) + mod, err := runGoListWithEnv("{{.Module}}", []string{pkgpath}, []string{"GO111MODULE=on"}, flags...) if err != nil || mod == "" { // if the command errors or modules aren't being used, return the empty string return "" } - modDir, err := runGoListWithEnv("{{.Module.Dir}}", pkgpath, []string{"GO111MODULE=on"}, flags...) + modDir, err := runGoListWithEnv("{{.Module.Dir}}", []string{pkgpath}, []string{"GO111MODULE=on"}, flags...) if err != nil { return "" } @@ -96,7 +155,7 @@ func GetModDir(pkgpath string, flags ...string) string { // GetPkgDir gets the absolute directory containing the package with path `pkgpath`. It passes the // `go list` command the flags specified by `flags`. func GetPkgDir(pkgpath string, flags ...string) string { - pkgDir, err := runGoList("{{.Dir}}", pkgpath, flags...) + pkgDir, err := runGoList("{{.Dir}}", []string{pkgpath}, flags...) if err != nil { return "" } @@ -112,7 +171,7 @@ func GetPkgDir(pkgpath string, flags ...string) string { // DepErrors checks there are any errors resolving dependencies for `pkgpath`. It passes the `go // list` command the flags specified by `flags`. func DepErrors(pkgpath string, flags ...string) bool { - out, err := runGoList("{{if .DepsErrors}}{{else}}error{{end}}", pkgpath, flags...) + out, err := runGoList("{{if .DepsErrors}}{{else}}error{{end}}", []string{pkgpath}, flags...) if err != nil { // if go list failed, assume dependencies are broken return false From 65c9d8cb78a284fbf6869ccaa764a765bb45a247 Mon Sep 17 00:00:00 2001 From: Gustav Munkby Date: Tue, 15 Nov 2022 10:38:06 +0100 Subject: [PATCH 0262/1420] Run go linux tests with fast package extraction To ensure this code path is actively tested. --- .github/workflows/go-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/go-tests.yml b/.github/workflows/go-tests.yml index 3cc61eafa5c..5ce27627e8a 100644 --- a/.github/workflows/go-tests.yml +++ b/.github/workflows/go-tests.yml @@ -51,7 +51,7 @@ jobs: - name: Test run: | cd go - make test + env CODEQL_EXTRACTOR_GO_FAST_PACKAGE_INFO=1 make test test-mac: name: Test MacOS From 7ca32ee2b57faec856bc5f9be44fbc1edaa78294 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 15 Nov 2022 11:11:36 +0100 Subject: [PATCH 0263/1420] Python: Fieldflow: merge assignment tests --- .../experimental/dataflow/fieldflow/test.py | 72 ++++++++----------- 1 file changed, 30 insertions(+), 42 deletions(-) diff --git a/python/ql/test/experimental/dataflow/fieldflow/test.py b/python/ql/test/experimental/dataflow/fieldflow/test.py index 70db8554241..f5b20daff96 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/test.py +++ b/python/ql/test/experimental/dataflow/fieldflow/test.py @@ -39,33 +39,55 @@ class MyObj(object): self.foo = foo def setFoo(obj, x): - SINK_F(obj.foo) obj.foo = x -@expects(2) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) + +@expects(3) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) def test_indirect_assign(): - myobj = MyObj("OK") + myobj = MyObj(NONSOURCE) + SINK_F(myobj.foo) setFoo(myobj, SOURCE) SINK(myobj.foo) # $ flow="SOURCE, l:-1 -> myobj.foo" + setFoo(myobj, NONSOURCE) + SINK_F(myobj.foo) # $ SPURIOUS: flow="SOURCE, l:-4 -> myobj.foo" + +@expects(3) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) def test_indirect_assign_method(): - myobj = MyObj("OK") + myobj = MyObj(NONSOURCE) + SINK_F(myobj.foo) myobj.setFoo(SOURCE) SINK(myobj.foo) # $ flow="SOURCE, l:-1 -> myobj.foo" + myobj.setFoo(NONSOURCE) + SINK_F(myobj.foo) # $ SPURIOUS: flow="SOURCE, l:-4 -> myobj.foo" + +@expects(3) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) +def test_indirect_assign_bound_method(): + myobj = MyObj(NONSOURCE) + SINK_F(myobj.foo) + + sf = myobj.setFoo + + sf(SOURCE) + SINK(myobj.foo) # $ MISSING: flow="SOURCE, l:-1 -> myobj.foo" + + sf(NONSOURCE) + SINK_F(myobj.foo) + + +@expects(3) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) def test_direct_assign(): myobj = MyObj(NONSOURCE) + SINK_F(myobj.foo) + myobj.foo = SOURCE SINK(myobj.foo) # $ flow="SOURCE, l:-1 -> myobj.foo" - -def test_direct_assign_overwrite(): - myobj = MyObj(NONSOURCE) - myobj.foo = SOURCE myobj.foo = NONSOURCE SINK_F(myobj.foo) @@ -160,40 +182,6 @@ def test_nested_obj_method(): a.getObj().foo = x SINK(a.obj.foo) # $ flow="SOURCE, l:-3 -> a.obj.foo" -# ------------------------------------------------------------------------------ -# Bound Method calls -# ------------------------------------------------------------------------------ - -class Foo: - def __init__(self, x): - self.x = x - - def update_x(self, x): - self.x = x - -@expects(7) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) -def test_bound_method_call(): - # direct assignment - foo = Foo(None) - SINK_F(foo.x) - foo.x = SOURCE - SINK(foo.x) # $ flow="SOURCE, l:-1 -> foo.x" - foo.x = None - SINK_F(foo.x) - - # assignment through function - foo = Foo(SOURCE) - SINK(foo.x) # $ flow="SOURCE, l:-1 -> foo.x" - foo.update_x(None) - SINK_F(foo.x) # $ flow="SOURCE, l:-3 -> foo.x" - - # assignment through bound-method calls - foo = Foo(SOURCE) - ux = foo.update_x - SINK(foo.x) # $ flow="SOURCE, l:-2 -> foo.x" - ux(None) - SINK_F(foo.x) # $ SPURIOUS: flow="SOURCE, l:-4 -> foo.x" - # ------------------------------------------------------------------------------ # Crosstalk test -- using different function based on conditional From 98bf3adc7245d094a9d848276098f6cad6f53241 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Sun, 13 Nov 2022 20:35:23 +0100 Subject: [PATCH 0264/1420] Python: Add enclosing-callable test --- .../EnclosingCallable.expected | 24 +++++++++++++++++++ .../enclosing-callable/EnclosingCallable.ql | 6 +++++ .../enclosing-callable/class_example.py | 7 ++++++ .../dataflow/enclosing-callable/generator.py | 2 ++ 4 files changed, 39 insertions(+) create mode 100644 python/ql/test/experimental/dataflow/enclosing-callable/EnclosingCallable.expected create mode 100644 python/ql/test/experimental/dataflow/enclosing-callable/EnclosingCallable.ql create mode 100644 python/ql/test/experimental/dataflow/enclosing-callable/class_example.py create mode 100644 python/ql/test/experimental/dataflow/enclosing-callable/generator.py diff --git a/python/ql/test/experimental/dataflow/enclosing-callable/EnclosingCallable.expected b/python/ql/test/experimental/dataflow/enclosing-callable/EnclosingCallable.expected new file mode 100644 index 00000000000..a1e3de562f5 --- /dev/null +++ b/python/ql/test/experimental/dataflow/enclosing-callable/EnclosingCallable.expected @@ -0,0 +1,24 @@ +| file://:0:0:0:0 | Function generator_func | generator.py:1:20:1:21 | ControlFlowNode for xs | +| file://:0:0:0:0 | Function generator_func | generator.py:2:12:2:26 | ControlFlowNode for .0 | +| file://:0:0:0:0 | Function generator_func | generator.py:2:12:2:26 | ControlFlowNode for .0 | +| file://:0:0:0:0 | Function generator_func | generator.py:2:12:2:26 | ControlFlowNode for ListComp | +| file://:0:0:0:0 | Function generator_func | generator.py:2:13:2:13 | ControlFlowNode for Yield | +| file://:0:0:0:0 | Function generator_func | generator.py:2:13:2:13 | ControlFlowNode for x | +| file://:0:0:0:0 | Function generator_func | generator.py:2:19:2:19 | ControlFlowNode for x | +| file://:0:0:0:0 | Function generator_func | generator.py:2:24:2:25 | ControlFlowNode for xs | +| file://:0:0:0:0 | Module class_example | class_example.py:1:1:1:3 | ControlFlowNode for wat | +| file://:0:0:0:0 | Module class_example | class_example.py:1:7:1:7 | ControlFlowNode for IntegerLiteral | +| file://:0:0:0:0 | Module class_example | class_example.py:3:1:3:10 | ControlFlowNode for ClassExpr | +| file://:0:0:0:0 | Module class_example | class_example.py:3:7:3:9 | ControlFlowNode for Wat | +| file://:0:0:0:0 | Module class_example | class_example.py:4:5:4:7 | ControlFlowNode for wat | +| file://:0:0:0:0 | Module class_example | class_example.py:4:11:4:11 | ControlFlowNode for IntegerLiteral | +| file://:0:0:0:0 | Module class_example | class_example.py:5:5:5:9 | ControlFlowNode for print | +| file://:0:0:0:0 | Module class_example | class_example.py:5:5:5:26 | ControlFlowNode for print() | +| file://:0:0:0:0 | Module class_example | class_example.py:5:11:5:20 | ControlFlowNode for Str | +| file://:0:0:0:0 | Module class_example | class_example.py:5:23:5:25 | ControlFlowNode for wat | +| file://:0:0:0:0 | Module class_example | class_example.py:7:1:7:5 | ControlFlowNode for print | +| file://:0:0:0:0 | Module class_example | class_example.py:7:1:7:23 | ControlFlowNode for print() | +| file://:0:0:0:0 | Module class_example | class_example.py:7:7:7:17 | ControlFlowNode for Str | +| file://:0:0:0:0 | Module class_example | class_example.py:7:20:7:22 | ControlFlowNode for wat | +| file://:0:0:0:0 | Module generator | generator.py:1:1:1:23 | ControlFlowNode for FunctionExpr | +| file://:0:0:0:0 | Module generator | generator.py:1:5:1:18 | ControlFlowNode for generator_func | diff --git a/python/ql/test/experimental/dataflow/enclosing-callable/EnclosingCallable.ql b/python/ql/test/experimental/dataflow/enclosing-callable/EnclosingCallable.ql new file mode 100644 index 00000000000..1fa6a8ffc54 --- /dev/null +++ b/python/ql/test/experimental/dataflow/enclosing-callable/EnclosingCallable.ql @@ -0,0 +1,6 @@ +import python +import semmle.python.dataflow.new.DataFlow + +from DataFlow::CfgNode node +where exists(node.getLocation().getFile().getRelativePath()) +select node.getEnclosingCallable() as enclosingCallable, node diff --git a/python/ql/test/experimental/dataflow/enclosing-callable/class_example.py b/python/ql/test/experimental/dataflow/enclosing-callable/class_example.py new file mode 100644 index 00000000000..389b293d2bd --- /dev/null +++ b/python/ql/test/experimental/dataflow/enclosing-callable/class_example.py @@ -0,0 +1,7 @@ +wat = 1 + +class Wat: + wat = 2 + print("in class", wat) # prints 2 + +print("in module", wat) # prints 1 diff --git a/python/ql/test/experimental/dataflow/enclosing-callable/generator.py b/python/ql/test/experimental/dataflow/enclosing-callable/generator.py new file mode 100644 index 00000000000..56b6202abd7 --- /dev/null +++ b/python/ql/test/experimental/dataflow/enclosing-callable/generator.py @@ -0,0 +1,2 @@ +def generator_func(xs): + return [x for x in xs] From e886b53a947ddce06437dfa1ac06e3ba1250c2f2 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 15 Nov 2022 11:16:10 +0100 Subject: [PATCH 0265/1420] Python: CallGraph tests: remove rest of old annotations --- .../CallGraph/InlineCallGraphTest.expected | 10 +++++----- .../library-tests/CallGraph/code/class_simple.py | 8 -------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.expected b/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.expected index 975ac22dd2d..02d08ac4c81 100644 --- a/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.expected +++ b/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.expected @@ -3,11 +3,11 @@ debug_callableNotUnique | code/class_advanced.py:18:5:18:18 | Function arg | Qualified function name 'B.arg' is not unique. Please fix. | | code/class_advanced.py:23:5:23:25 | Function arg | Qualified function name 'B.arg' is not unique. Please fix. | pointsTo_found_typeTracker_notFound -| code/class_simple.py:28:1:28:15 | ControlFlowNode for Attribute() | A.some_method | -| code/class_simple.py:30:1:30:21 | ControlFlowNode for Attribute() | A.some_staticmethod | -| code/class_simple.py:32:1:32:20 | ControlFlowNode for Attribute() | A.some_classmethod | -| code/class_simple.py:35:1:35:21 | ControlFlowNode for Attribute() | A.some_staticmethod | -| code/class_simple.py:37:1:37:20 | ControlFlowNode for Attribute() | A.some_classmethod | +| code/class_simple.py:24:1:24:15 | ControlFlowNode for Attribute() | A.some_method | +| code/class_simple.py:25:1:25:21 | ControlFlowNode for Attribute() | A.some_staticmethod | +| code/class_simple.py:26:1:26:20 | ControlFlowNode for Attribute() | A.some_classmethod | +| code/class_simple.py:28:1:28:21 | ControlFlowNode for Attribute() | A.some_staticmethod | +| code/class_simple.py:29:1:29:20 | ControlFlowNode for Attribute() | A.some_classmethod | | code/runtime_decision.py:18:1:18:6 | ControlFlowNode for func() | rd_bar | | code/runtime_decision.py:18:1:18:6 | ControlFlowNode for func() | rd_foo | | code/runtime_decision.py:26:1:26:7 | ControlFlowNode for func2() | rd_bar | diff --git a/python/ql/test/experimental/library-tests/CallGraph/code/class_simple.py b/python/ql/test/experimental/library-tests/CallGraph/code/class_simple.py index a679cc5e25c..f201e648e3a 100644 --- a/python/ql/test/experimental/library-tests/CallGraph/code/class_simple.py +++ b/python/ql/test/experimental/library-tests/CallGraph/code/class_simple.py @@ -4,17 +4,14 @@ class A(object): print('A.__init__', arg) self.arg = arg - # name:A.some_method def some_method(self): print('A.some_method', self) @staticmethod - # name:A.some_staticmethod def some_staticmethod(): print('A.some_staticmethod') @classmethod - # name:A.some_classmethod def some_classmethod(cls): print('A.some_classmethod', cls) @@ -24,14 +21,9 @@ class A(object): # However, current test setup uses "callable" for naming, and expects things to be Function. a = A(42) -# calls:A.some_method a.some_method() # $ pt=A.some_method -# calls:A.some_staticmethod a.some_staticmethod() # $ pt=A.some_staticmethod -# calls:A.some_classmethod a.some_classmethod() # $ pt=A.some_classmethod -# calls:A.some_staticmethod A.some_staticmethod() # $ pt=A.some_staticmethod -# calls:A.some_classmethod A.some_classmethod() # $ pt=A.some_classmethod From a7492127075ec86a3ed3a2901eb13e95a9ab24a7 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 15 Nov 2022 11:22:19 +0100 Subject: [PATCH 0266/1420] C#: Handle `op_Checked*` operators in the extractor --- .../Semmle.Extraction.CSharp/Entities/UserOperator.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs index 11e54ddff35..9f0d3c8f4c5 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs @@ -2,6 +2,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using System.IO; using System.Linq; +using System.Text.RegularExpressions; namespace Semmle.Extraction.CSharp.Entities { @@ -161,6 +162,9 @@ namespace Semmle.Extraction.CSharp.Entities case "op_False": operatorName = "false"; break; + case var @checked when Regex.IsMatch(@checked, "^op_Checked.*"): + operatorName = @checked; + break; default: operatorName = methodName; success = false; From dc2cd994d4f2dfa2ad5bdd54a93cda7f2a6e9b10 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 15 Nov 2022 11:22:48 +0100 Subject: [PATCH 0267/1420] C#: Update expected test output --- .../library-tests/csharp9/PrintAst.expected | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/csharp/ql/test/library-tests/csharp9/PrintAst.expected b/csharp/ql/test/library-tests/csharp9/PrintAst.expected index 3c708935143..aeaea302fa3 100644 --- a/csharp/ql/test/library-tests/csharp9/PrintAst.expected +++ b/csharp/ql/test/library-tests/csharp9/PrintAst.expected @@ -5,7 +5,8 @@ AnonymousObjectCreation.cs: # 7| 1: [TypeMention] AnonObj # 7| 1: [AssignExpr] ... = ... # 7| 0: [FieldAccess] access to field l -# 7| 1: [ObjectCreation] object creation of type List +# 7| 1: [CastExpr] (...) ... +# 7| 1: [ObjectCreation] object creation of type List # 9| 6: [Property] Prop1 # 9| -1: [TypeMention] int # 9| 3: [Getter] get_Prop1 @@ -21,13 +22,15 @@ AnonymousObjectCreation.cs: # 13| 0: [ExprStmt] ...; # 13| 0: [MethodCall] call to method M1 # 13| -1: [ThisAccess] this access -# 13| 0: [ObjectCreation] object creation of type AnonObj -# 13| -1: [ObjectInitializer] { ..., ... } -# 13| 0: [MemberInitializer] ... = ... -# 13| 0: [PropertyCall] access to property Prop1 -# 13| 1: [IntLiteral] 1 +# 13| 0: [CastExpr] (...) ... +# 13| 1: [ObjectCreation] object creation of type AnonObj +# 13| -1: [ObjectInitializer] { ..., ... } +# 13| 0: [MemberInitializer] ... = ... +# 13| 0: [PropertyCall] access to property Prop1 +# 13| 1: [IntLiteral] 1 # 14| 1: [ReturnStmt] return ...; -# 14| 0: [ObjectCreation] object creation of type AnonObj +# 14| 0: [CastExpr] (...) ... +# 14| 1: [ObjectCreation] object creation of type AnonObj # 17| 8: [DelegateType] D #-----| 2: (Parameters) # 17| 0: [Parameter] x @@ -42,9 +45,10 @@ AnonymousObjectCreation.cs: # 21| -1: [TypeMention] D # 21| 4: [BlockStmt] {...} # 21| 0: [ReturnStmt] return ...; -# 21| 0: [ExplicitDelegateCreation] delegate creation of type D -# 21| 0: [ImplicitDelegateCreation] delegate creation of type D -# 21| 0: [MethodAccess] access to method M2 +# 21| 0: [CastExpr] (...) ... +# 21| 1: [ExplicitDelegateCreation] delegate creation of type D +# 21| 0: [ImplicitDelegateCreation] delegate creation of type D +# 21| 0: [MethodAccess] access to method M2 # 23| 11: [Method] MethodAdd # 23| -1: [TypeMention] Void # 24| 4: [BlockStmt] {...} @@ -53,7 +57,8 @@ AnonymousObjectCreation.cs: # 25| -1: [TypeMention] List # 25| 1: [TypeMention] int # 25| 0: [LocalVariableAccess] access to local variable list -# 25| 1: [ObjectCreation] object creation of type List +# 25| 1: [CastExpr] (...) ... +# 25| 1: [ObjectCreation] object creation of type List BinaryPattern.cs: # 3| [Class] BinaryPattern # 5| 5: [Property] P1 From 32f60fd11281fcbf943e920e40991aad43447127 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 14 Nov 2022 14:07:02 +0100 Subject: [PATCH 0268/1420] Ruby: Add more local flow tests for use-use flow --- .../dataflow/local/DataflowStep.expected | 109 ++++++++++++++++- .../dataflow/local/Nodes.expected | 60 +++++++++ .../dataflow/local/TaintStep.expected | 115 +++++++++++++++++- .../dataflow/local/local_dataflow.rb | 26 ++++ 4 files changed, 308 insertions(+), 2 deletions(-) diff --git a/ruby/ql/test/library-tests/dataflow/local/DataflowStep.expected b/ruby/ql/test/library-tests/dataflow/local/DataflowStep.expected index c6023a039d3..db3bc7b7296 100644 --- a/ruby/ql/test/library-tests/dataflow/local/DataflowStep.expected +++ b/ruby/ql/test/library-tests/dataflow/local/DataflowStep.expected @@ -1,6 +1,6 @@ | local_dataflow.rb:1:1:7:3 | self (foo) | local_dataflow.rb:3:8:3:10 | self | | local_dataflow.rb:1:1:7:3 | self in foo | local_dataflow.rb:1:1:7:3 | self (foo) | -| local_dataflow.rb:1:1:124:4 | self (local_dataflow.rb) | local_dataflow.rb:49:1:53:3 | self | +| local_dataflow.rb:1:1:150:3 | self (local_dataflow.rb) | local_dataflow.rb:49:1:53:3 | self | | local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:1:9:1:9 | a | | local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:2:7:2:7 | a | | local_dataflow.rb:2:3:2:7 | ... = ... | local_dataflow.rb:3:13:3:13 | b | @@ -279,3 +279,110 @@ | local_dataflow.rb:123:8:123:20 | call to dup | local_dataflow.rb:123:8:123:45 | call to tap | | local_dataflow.rb:123:8:123:45 | call to tap | local_dataflow.rb:123:8:123:49 | call to dup | | local_dataflow.rb:123:26:123:45 | self | local_dataflow.rb:123:32:123:43 | self | +| local_dataflow.rb:126:1:128:3 | self (use) | local_dataflow.rb:127:3:127:8 | self | +| local_dataflow.rb:126:1:128:3 | self in use | local_dataflow.rb:126:1:128:3 | self (use) | +| local_dataflow.rb:130:1:150:3 | self (use_use_madness) | local_dataflow.rb:132:6:132:11 | self | +| local_dataflow.rb:130:1:150:3 | self in use_use_madness | local_dataflow.rb:130:1:150:3 | self (use_use_madness) | +| local_dataflow.rb:131:3:131:8 | ... = ... | local_dataflow.rb:132:10:132:10 | x | +| local_dataflow.rb:131:7:131:8 | "" | local_dataflow.rb:131:3:131:8 | ... = ... | +| local_dataflow.rb:131:7:131:8 | "" | local_dataflow.rb:131:3:131:8 | ... = ... | +| local_dataflow.rb:132:6:132:11 | [post] self | local_dataflow.rb:133:8:133:13 | self | +| local_dataflow.rb:132:6:132:11 | self | local_dataflow.rb:133:8:133:13 | self | +| local_dataflow.rb:132:10:132:10 | x | local_dataflow.rb:133:12:133:12 | x | +| local_dataflow.rb:132:12:148:10 | then ... | local_dataflow.rb:132:3:149:5 | if ... | +| local_dataflow.rb:133:8:133:13 | [post] self | local_dataflow.rb:133:18:133:23 | self | +| local_dataflow.rb:133:8:133:13 | [post] self | local_dataflow.rb:134:7:134:12 | self | +| local_dataflow.rb:133:8:133:13 | call to use | local_dataflow.rb:133:8:133:23 | [false] ... \|\| ... | +| local_dataflow.rb:133:8:133:13 | call to use | local_dataflow.rb:133:8:133:23 | [true] ... \|\| ... | +| local_dataflow.rb:133:8:133:13 | self | local_dataflow.rb:133:18:133:23 | self | +| local_dataflow.rb:133:8:133:13 | self | local_dataflow.rb:134:7:134:12 | self | +| local_dataflow.rb:133:12:133:12 | x | local_dataflow.rb:133:22:133:22 | x | +| local_dataflow.rb:133:12:133:12 | x | local_dataflow.rb:134:11:134:11 | x | +| local_dataflow.rb:133:18:133:23 | [post] self | local_dataflow.rb:134:7:134:12 | self | +| local_dataflow.rb:133:18:133:23 | [post] self | local_dataflow.rb:136:7:136:12 | self | +| local_dataflow.rb:133:18:133:23 | call to use | local_dataflow.rb:133:8:133:23 | [false] ... \|\| ... | +| local_dataflow.rb:133:18:133:23 | call to use | local_dataflow.rb:133:8:133:23 | [true] ... \|\| ... | +| local_dataflow.rb:133:18:133:23 | self | local_dataflow.rb:134:7:134:12 | self | +| local_dataflow.rb:133:18:133:23 | self | local_dataflow.rb:136:7:136:12 | self | +| local_dataflow.rb:133:22:133:22 | x | local_dataflow.rb:134:11:134:11 | x | +| local_dataflow.rb:133:22:133:22 | x | local_dataflow.rb:136:11:136:11 | x | +| local_dataflow.rb:133:24:134:12 | then ... | local_dataflow.rb:133:5:139:7 | if ... | +| local_dataflow.rb:134:7:134:12 | [post] self | local_dataflow.rb:141:9:141:14 | self | +| local_dataflow.rb:134:7:134:12 | call to use | local_dataflow.rb:133:24:134:12 | then ... | +| local_dataflow.rb:134:7:134:12 | self | local_dataflow.rb:141:9:141:14 | self | +| local_dataflow.rb:134:11:134:11 | x | local_dataflow.rb:141:13:141:13 | x | +| local_dataflow.rb:135:5:138:9 | else ... | local_dataflow.rb:133:5:139:7 | if ... | +| local_dataflow.rb:136:7:136:12 | [post] self | local_dataflow.rb:137:10:137:15 | self | +| local_dataflow.rb:136:7:136:12 | self | local_dataflow.rb:137:10:137:15 | self | +| local_dataflow.rb:136:11:136:11 | x | local_dataflow.rb:137:14:137:14 | x | +| local_dataflow.rb:137:7:138:9 | if ... | local_dataflow.rb:135:5:138:9 | else ... | +| local_dataflow.rb:137:10:137:15 | [post] self | local_dataflow.rb:137:21:137:26 | self | +| local_dataflow.rb:137:10:137:15 | [post] self | local_dataflow.rb:141:9:141:14 | self | +| local_dataflow.rb:137:10:137:15 | call to use | local_dataflow.rb:137:10:137:26 | [false] ... && ... | +| local_dataflow.rb:137:10:137:15 | call to use | local_dataflow.rb:137:10:137:26 | [true] ... && ... | +| local_dataflow.rb:137:10:137:15 | self | local_dataflow.rb:137:21:137:26 | self | +| local_dataflow.rb:137:10:137:15 | self | local_dataflow.rb:141:9:141:14 | self | +| local_dataflow.rb:137:14:137:14 | x | local_dataflow.rb:137:25:137:25 | x | +| local_dataflow.rb:137:14:137:14 | x | local_dataflow.rb:141:13:141:13 | x | +| local_dataflow.rb:137:20:137:26 | [false] ! ... | local_dataflow.rb:137:10:137:26 | [false] ... && ... | +| local_dataflow.rb:137:20:137:26 | [true] ! ... | local_dataflow.rb:137:10:137:26 | [true] ... && ... | +| local_dataflow.rb:137:21:137:26 | [post] self | local_dataflow.rb:141:9:141:14 | self | +| local_dataflow.rb:137:21:137:26 | self | local_dataflow.rb:141:9:141:14 | self | +| local_dataflow.rb:137:25:137:25 | x | local_dataflow.rb:141:13:141:13 | x | +| local_dataflow.rb:141:8:141:14 | [false] ! ... | local_dataflow.rb:141:8:141:37 | [false] ... \|\| ... | +| local_dataflow.rb:141:8:141:14 | [false] ! ... | local_dataflow.rb:141:8:141:37 | [true] ... \|\| ... | +| local_dataflow.rb:141:8:141:14 | [true] ! ... | local_dataflow.rb:141:8:141:37 | [true] ... \|\| ... | +| local_dataflow.rb:141:9:141:14 | [post] self | local_dataflow.rb:141:20:141:25 | self | +| local_dataflow.rb:141:9:141:14 | [post] self | local_dataflow.rb:147:5:147:10 | self | +| local_dataflow.rb:141:9:141:14 | self | local_dataflow.rb:141:20:141:25 | self | +| local_dataflow.rb:141:9:141:14 | self | local_dataflow.rb:147:5:147:10 | self | +| local_dataflow.rb:141:13:141:13 | x | local_dataflow.rb:141:24:141:24 | x | +| local_dataflow.rb:141:13:141:13 | x | local_dataflow.rb:147:9:147:9 | x | +| local_dataflow.rb:141:19:141:37 | [false] ( ... ) | local_dataflow.rb:141:8:141:37 | [false] ... \|\| ... | +| local_dataflow.rb:141:19:141:37 | [true] ( ... ) | local_dataflow.rb:141:8:141:37 | [true] ... \|\| ... | +| local_dataflow.rb:141:20:141:25 | [post] self | local_dataflow.rb:141:31:141:36 | self | +| local_dataflow.rb:141:20:141:25 | [post] self | local_dataflow.rb:143:11:143:16 | self | +| local_dataflow.rb:141:20:141:25 | call to use | local_dataflow.rb:141:20:141:36 | [false] ... && ... | +| local_dataflow.rb:141:20:141:25 | call to use | local_dataflow.rb:141:20:141:36 | [true] ... && ... | +| local_dataflow.rb:141:20:141:25 | self | local_dataflow.rb:141:31:141:36 | self | +| local_dataflow.rb:141:20:141:25 | self | local_dataflow.rb:143:11:143:16 | self | +| local_dataflow.rb:141:20:141:36 | [false] ... && ... | local_dataflow.rb:141:19:141:37 | [false] ( ... ) | +| local_dataflow.rb:141:20:141:36 | [true] ... && ... | local_dataflow.rb:141:19:141:37 | [true] ( ... ) | +| local_dataflow.rb:141:24:141:24 | x | local_dataflow.rb:141:35:141:35 | x | +| local_dataflow.rb:141:24:141:24 | x | local_dataflow.rb:143:15:143:15 | x | +| local_dataflow.rb:141:30:141:36 | [false] ! ... | local_dataflow.rb:141:20:141:36 | [false] ... && ... | +| local_dataflow.rb:141:30:141:36 | [true] ! ... | local_dataflow.rb:141:20:141:36 | [true] ... && ... | +| local_dataflow.rb:141:31:141:36 | [post] self | local_dataflow.rb:143:11:143:16 | self | +| local_dataflow.rb:141:31:141:36 | [post] self | local_dataflow.rb:147:5:147:10 | self | +| local_dataflow.rb:141:31:141:36 | self | local_dataflow.rb:143:11:143:16 | self | +| local_dataflow.rb:141:31:141:36 | self | local_dataflow.rb:147:5:147:10 | self | +| local_dataflow.rb:141:35:141:35 | x | local_dataflow.rb:143:15:143:15 | x | +| local_dataflow.rb:141:35:141:35 | x | local_dataflow.rb:147:9:147:9 | x | +| local_dataflow.rb:141:38:142:9 | then ... | local_dataflow.rb:141:5:145:7 | if ... | +| local_dataflow.rb:142:7:142:9 | nil | local_dataflow.rb:141:38:142:9 | then ... | +| local_dataflow.rb:143:5:144:16 | elsif ... | local_dataflow.rb:141:5:145:7 | if ... | +| local_dataflow.rb:143:11:143:16 | [post] self | local_dataflow.rb:143:21:143:26 | self | +| local_dataflow.rb:143:11:143:16 | [post] self | local_dataflow.rb:144:11:144:16 | self | +| local_dataflow.rb:143:11:143:16 | call to use | local_dataflow.rb:143:11:143:26 | [false] ... \|\| ... | +| local_dataflow.rb:143:11:143:16 | call to use | local_dataflow.rb:143:11:143:26 | [true] ... \|\| ... | +| local_dataflow.rb:143:11:143:16 | self | local_dataflow.rb:143:21:143:26 | self | +| local_dataflow.rb:143:11:143:16 | self | local_dataflow.rb:144:11:144:16 | self | +| local_dataflow.rb:143:15:143:15 | x | local_dataflow.rb:143:25:143:25 | x | +| local_dataflow.rb:143:15:143:15 | x | local_dataflow.rb:144:15:144:15 | x | +| local_dataflow.rb:143:21:143:26 | [post] self | local_dataflow.rb:144:11:144:16 | self | +| local_dataflow.rb:143:21:143:26 | [post] self | local_dataflow.rb:147:5:147:10 | self | +| local_dataflow.rb:143:21:143:26 | call to use | local_dataflow.rb:143:11:143:26 | [false] ... \|\| ... | +| local_dataflow.rb:143:21:143:26 | call to use | local_dataflow.rb:143:11:143:26 | [true] ... \|\| ... | +| local_dataflow.rb:143:21:143:26 | self | local_dataflow.rb:144:11:144:16 | self | +| local_dataflow.rb:143:21:143:26 | self | local_dataflow.rb:147:5:147:10 | self | +| local_dataflow.rb:143:25:143:25 | x | local_dataflow.rb:144:15:144:15 | x | +| local_dataflow.rb:143:25:143:25 | x | local_dataflow.rb:147:9:147:9 | x | +| local_dataflow.rb:143:27:144:16 | then ... | local_dataflow.rb:143:5:144:16 | elsif ... | +| local_dataflow.rb:144:11:144:16 | [post] self | local_dataflow.rb:147:5:147:10 | self | +| local_dataflow.rb:144:11:144:16 | call to use | local_dataflow.rb:143:27:144:16 | then ... | +| local_dataflow.rb:144:11:144:16 | self | local_dataflow.rb:147:5:147:10 | self | +| local_dataflow.rb:144:15:144:15 | x | local_dataflow.rb:147:9:147:9 | x | +| local_dataflow.rb:147:5:147:10 | [post] self | local_dataflow.rb:148:5:148:10 | self | +| local_dataflow.rb:147:5:147:10 | self | local_dataflow.rb:148:5:148:10 | self | +| local_dataflow.rb:147:9:147:9 | x | local_dataflow.rb:148:9:148:9 | x | +| local_dataflow.rb:148:5:148:10 | call to use | local_dataflow.rb:132:12:148:10 | then ... | diff --git a/ruby/ql/test/library-tests/dataflow/local/Nodes.expected b/ruby/ql/test/library-tests/dataflow/local/Nodes.expected index 353d1f0d2d6..fcb55f6ada7 100644 --- a/ruby/ql/test/library-tests/dataflow/local/Nodes.expected +++ b/ruby/ql/test/library-tests/dataflow/local/Nodes.expected @@ -19,6 +19,8 @@ ret | local_dataflow.rb:119:3:119:31 | call to sink | | local_dataflow.rb:123:3:123:50 | call to sink | | local_dataflow.rb:123:32:123:43 | call to puts | +| local_dataflow.rb:127:3:127:8 | call to rand | +| local_dataflow.rb:132:3:149:5 | if ... | arg | local_dataflow.rb:3:8:3:10 | self | local_dataflow.rb:3:8:3:10 | call to p | self | | local_dataflow.rb:3:10:3:10 | a | local_dataflow.rb:3:8:3:10 | call to p | position 0 | @@ -170,3 +172,61 @@ arg | local_dataflow.rb:123:26:123:45 | { ... } | local_dataflow.rb:123:8:123:45 | call to tap | block | | local_dataflow.rb:123:32:123:43 | self | local_dataflow.rb:123:32:123:43 | call to puts | self | | local_dataflow.rb:123:37:123:43 | "hello" | local_dataflow.rb:123:32:123:43 | call to puts | position 0 | +| local_dataflow.rb:127:3:127:8 | self | local_dataflow.rb:127:3:127:8 | call to rand | self | +| local_dataflow.rb:132:6:132:11 | self | local_dataflow.rb:132:6:132:11 | call to use | self | +| local_dataflow.rb:132:10:132:10 | x | local_dataflow.rb:132:6:132:11 | call to use | position 0 | +| local_dataflow.rb:133:8:133:13 | call to use | local_dataflow.rb:133:8:133:23 | [false] ... \|\| ... | self | +| local_dataflow.rb:133:8:133:13 | call to use | local_dataflow.rb:133:8:133:23 | [true] ... \|\| ... | self | +| local_dataflow.rb:133:8:133:13 | self | local_dataflow.rb:133:8:133:13 | call to use | self | +| local_dataflow.rb:133:12:133:12 | x | local_dataflow.rb:133:8:133:13 | call to use | position 0 | +| local_dataflow.rb:133:18:133:23 | call to use | local_dataflow.rb:133:8:133:23 | [false] ... \|\| ... | position 0 | +| local_dataflow.rb:133:18:133:23 | call to use | local_dataflow.rb:133:8:133:23 | [true] ... \|\| ... | position 0 | +| local_dataflow.rb:133:18:133:23 | self | local_dataflow.rb:133:18:133:23 | call to use | self | +| local_dataflow.rb:133:22:133:22 | x | local_dataflow.rb:133:18:133:23 | call to use | position 0 | +| local_dataflow.rb:134:7:134:12 | self | local_dataflow.rb:134:7:134:12 | call to use | self | +| local_dataflow.rb:134:11:134:11 | x | local_dataflow.rb:134:7:134:12 | call to use | position 0 | +| local_dataflow.rb:136:7:136:12 | self | local_dataflow.rb:136:7:136:12 | call to use | self | +| local_dataflow.rb:136:11:136:11 | x | local_dataflow.rb:136:7:136:12 | call to use | position 0 | +| local_dataflow.rb:137:10:137:15 | call to use | local_dataflow.rb:137:10:137:26 | [false] ... && ... | self | +| local_dataflow.rb:137:10:137:15 | call to use | local_dataflow.rb:137:10:137:26 | [true] ... && ... | self | +| local_dataflow.rb:137:10:137:15 | self | local_dataflow.rb:137:10:137:15 | call to use | self | +| local_dataflow.rb:137:14:137:14 | x | local_dataflow.rb:137:10:137:15 | call to use | position 0 | +| local_dataflow.rb:137:20:137:26 | [false] ! ... | local_dataflow.rb:137:10:137:26 | [false] ... && ... | position 0 | +| local_dataflow.rb:137:20:137:26 | [true] ! ... | local_dataflow.rb:137:10:137:26 | [true] ... && ... | position 0 | +| local_dataflow.rb:137:21:137:26 | call to use | local_dataflow.rb:137:20:137:26 | [false] ! ... | self | +| local_dataflow.rb:137:21:137:26 | call to use | local_dataflow.rb:137:20:137:26 | [true] ! ... | self | +| local_dataflow.rb:137:21:137:26 | self | local_dataflow.rb:137:21:137:26 | call to use | self | +| local_dataflow.rb:137:25:137:25 | x | local_dataflow.rb:137:21:137:26 | call to use | position 0 | +| local_dataflow.rb:141:8:141:14 | [false] ! ... | local_dataflow.rb:141:8:141:37 | [false] ... \|\| ... | self | +| local_dataflow.rb:141:8:141:14 | [false] ! ... | local_dataflow.rb:141:8:141:37 | [true] ... \|\| ... | self | +| local_dataflow.rb:141:8:141:14 | [true] ! ... | local_dataflow.rb:141:8:141:37 | [true] ... \|\| ... | self | +| local_dataflow.rb:141:9:141:14 | call to use | local_dataflow.rb:141:8:141:14 | [false] ! ... | self | +| local_dataflow.rb:141:9:141:14 | call to use | local_dataflow.rb:141:8:141:14 | [true] ! ... | self | +| local_dataflow.rb:141:9:141:14 | self | local_dataflow.rb:141:9:141:14 | call to use | self | +| local_dataflow.rb:141:13:141:13 | x | local_dataflow.rb:141:9:141:14 | call to use | position 0 | +| local_dataflow.rb:141:19:141:37 | [false] ( ... ) | local_dataflow.rb:141:8:141:37 | [false] ... \|\| ... | position 0 | +| local_dataflow.rb:141:19:141:37 | [true] ( ... ) | local_dataflow.rb:141:8:141:37 | [true] ... \|\| ... | position 0 | +| local_dataflow.rb:141:20:141:25 | call to use | local_dataflow.rb:141:20:141:36 | [false] ... && ... | self | +| local_dataflow.rb:141:20:141:25 | call to use | local_dataflow.rb:141:20:141:36 | [true] ... && ... | self | +| local_dataflow.rb:141:20:141:25 | self | local_dataflow.rb:141:20:141:25 | call to use | self | +| local_dataflow.rb:141:24:141:24 | x | local_dataflow.rb:141:20:141:25 | call to use | position 0 | +| local_dataflow.rb:141:30:141:36 | [false] ! ... | local_dataflow.rb:141:20:141:36 | [false] ... && ... | position 0 | +| local_dataflow.rb:141:30:141:36 | [true] ! ... | local_dataflow.rb:141:20:141:36 | [true] ... && ... | position 0 | +| local_dataflow.rb:141:31:141:36 | call to use | local_dataflow.rb:141:30:141:36 | [false] ! ... | self | +| local_dataflow.rb:141:31:141:36 | call to use | local_dataflow.rb:141:30:141:36 | [true] ! ... | self | +| local_dataflow.rb:141:31:141:36 | self | local_dataflow.rb:141:31:141:36 | call to use | self | +| local_dataflow.rb:141:35:141:35 | x | local_dataflow.rb:141:31:141:36 | call to use | position 0 | +| local_dataflow.rb:143:11:143:16 | call to use | local_dataflow.rb:143:11:143:26 | [false] ... \|\| ... | self | +| local_dataflow.rb:143:11:143:16 | call to use | local_dataflow.rb:143:11:143:26 | [true] ... \|\| ... | self | +| local_dataflow.rb:143:11:143:16 | self | local_dataflow.rb:143:11:143:16 | call to use | self | +| local_dataflow.rb:143:15:143:15 | x | local_dataflow.rb:143:11:143:16 | call to use | position 0 | +| local_dataflow.rb:143:21:143:26 | call to use | local_dataflow.rb:143:11:143:26 | [false] ... \|\| ... | position 0 | +| local_dataflow.rb:143:21:143:26 | call to use | local_dataflow.rb:143:11:143:26 | [true] ... \|\| ... | position 0 | +| local_dataflow.rb:143:21:143:26 | self | local_dataflow.rb:143:21:143:26 | call to use | self | +| local_dataflow.rb:143:25:143:25 | x | local_dataflow.rb:143:21:143:26 | call to use | position 0 | +| local_dataflow.rb:144:11:144:16 | self | local_dataflow.rb:144:11:144:16 | call to use | self | +| local_dataflow.rb:144:15:144:15 | x | local_dataflow.rb:144:11:144:16 | call to use | position 0 | +| local_dataflow.rb:147:5:147:10 | self | local_dataflow.rb:147:5:147:10 | call to use | self | +| local_dataflow.rb:147:9:147:9 | x | local_dataflow.rb:147:5:147:10 | call to use | position 0 | +| local_dataflow.rb:148:5:148:10 | self | local_dataflow.rb:148:5:148:10 | call to use | self | +| local_dataflow.rb:148:9:148:9 | x | local_dataflow.rb:148:5:148:10 | call to use | position 0 | diff --git a/ruby/ql/test/library-tests/dataflow/local/TaintStep.expected b/ruby/ql/test/library-tests/dataflow/local/TaintStep.expected index 85473aea3dd..f1b8eb2da26 100644 --- a/ruby/ql/test/library-tests/dataflow/local/TaintStep.expected +++ b/ruby/ql/test/library-tests/dataflow/local/TaintStep.expected @@ -50,7 +50,7 @@ | file://:0:0:0:0 | parameter self of each(0) | file://:0:0:0:0 | [summary] read: argument self.any element in each(0) | | local_dataflow.rb:1:1:7:3 | self (foo) | local_dataflow.rb:3:8:3:10 | self | | local_dataflow.rb:1:1:7:3 | self in foo | local_dataflow.rb:1:1:7:3 | self (foo) | -| local_dataflow.rb:1:1:124:4 | self (local_dataflow.rb) | local_dataflow.rb:49:1:53:3 | self | +| local_dataflow.rb:1:1:150:3 | self (local_dataflow.rb) | local_dataflow.rb:49:1:53:3 | self | | local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:1:9:1:9 | a | | local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:2:7:2:7 | a | | local_dataflow.rb:2:3:2:7 | ... = ... | local_dataflow.rb:3:13:3:13 | b | @@ -354,3 +354,116 @@ | local_dataflow.rb:123:8:123:20 | call to dup | local_dataflow.rb:123:8:123:45 | call to tap | | local_dataflow.rb:123:8:123:45 | call to tap | local_dataflow.rb:123:8:123:49 | call to dup | | local_dataflow.rb:123:26:123:45 | self | local_dataflow.rb:123:32:123:43 | self | +| local_dataflow.rb:126:1:128:3 | self (use) | local_dataflow.rb:127:3:127:8 | self | +| local_dataflow.rb:126:1:128:3 | self in use | local_dataflow.rb:126:1:128:3 | self (use) | +| local_dataflow.rb:130:1:150:3 | self (use_use_madness) | local_dataflow.rb:132:6:132:11 | self | +| local_dataflow.rb:130:1:150:3 | self in use_use_madness | local_dataflow.rb:130:1:150:3 | self (use_use_madness) | +| local_dataflow.rb:131:3:131:8 | ... = ... | local_dataflow.rb:132:10:132:10 | x | +| local_dataflow.rb:131:7:131:8 | "" | local_dataflow.rb:131:3:131:8 | ... = ... | +| local_dataflow.rb:131:7:131:8 | "" | local_dataflow.rb:131:3:131:8 | ... = ... | +| local_dataflow.rb:132:6:132:11 | [post] self | local_dataflow.rb:133:8:133:13 | self | +| local_dataflow.rb:132:6:132:11 | self | local_dataflow.rb:133:8:133:13 | self | +| local_dataflow.rb:132:10:132:10 | x | local_dataflow.rb:133:12:133:12 | x | +| local_dataflow.rb:132:12:148:10 | then ... | local_dataflow.rb:132:3:149:5 | if ... | +| local_dataflow.rb:133:8:133:13 | [post] self | local_dataflow.rb:133:18:133:23 | self | +| local_dataflow.rb:133:8:133:13 | [post] self | local_dataflow.rb:134:7:134:12 | self | +| local_dataflow.rb:133:8:133:13 | call to use | local_dataflow.rb:133:8:133:23 | [false] ... \|\| ... | +| local_dataflow.rb:133:8:133:13 | call to use | local_dataflow.rb:133:8:133:23 | [true] ... \|\| ... | +| local_dataflow.rb:133:8:133:13 | self | local_dataflow.rb:133:18:133:23 | self | +| local_dataflow.rb:133:8:133:13 | self | local_dataflow.rb:134:7:134:12 | self | +| local_dataflow.rb:133:12:133:12 | x | local_dataflow.rb:133:22:133:22 | x | +| local_dataflow.rb:133:12:133:12 | x | local_dataflow.rb:134:11:134:11 | x | +| local_dataflow.rb:133:18:133:23 | [post] self | local_dataflow.rb:134:7:134:12 | self | +| local_dataflow.rb:133:18:133:23 | [post] self | local_dataflow.rb:136:7:136:12 | self | +| local_dataflow.rb:133:18:133:23 | call to use | local_dataflow.rb:133:8:133:23 | [false] ... \|\| ... | +| local_dataflow.rb:133:18:133:23 | call to use | local_dataflow.rb:133:8:133:23 | [true] ... \|\| ... | +| local_dataflow.rb:133:18:133:23 | self | local_dataflow.rb:134:7:134:12 | self | +| local_dataflow.rb:133:18:133:23 | self | local_dataflow.rb:136:7:136:12 | self | +| local_dataflow.rb:133:22:133:22 | x | local_dataflow.rb:134:11:134:11 | x | +| local_dataflow.rb:133:22:133:22 | x | local_dataflow.rb:136:11:136:11 | x | +| local_dataflow.rb:133:24:134:12 | then ... | local_dataflow.rb:133:5:139:7 | if ... | +| local_dataflow.rb:134:7:134:12 | [post] self | local_dataflow.rb:141:9:141:14 | self | +| local_dataflow.rb:134:7:134:12 | call to use | local_dataflow.rb:133:24:134:12 | then ... | +| local_dataflow.rb:134:7:134:12 | self | local_dataflow.rb:141:9:141:14 | self | +| local_dataflow.rb:134:11:134:11 | x | local_dataflow.rb:141:13:141:13 | x | +| local_dataflow.rb:135:5:138:9 | else ... | local_dataflow.rb:133:5:139:7 | if ... | +| local_dataflow.rb:136:7:136:12 | [post] self | local_dataflow.rb:137:10:137:15 | self | +| local_dataflow.rb:136:7:136:12 | self | local_dataflow.rb:137:10:137:15 | self | +| local_dataflow.rb:136:11:136:11 | x | local_dataflow.rb:137:14:137:14 | x | +| local_dataflow.rb:137:7:138:9 | if ... | local_dataflow.rb:135:5:138:9 | else ... | +| local_dataflow.rb:137:10:137:15 | [post] self | local_dataflow.rb:137:21:137:26 | self | +| local_dataflow.rb:137:10:137:15 | [post] self | local_dataflow.rb:141:9:141:14 | self | +| local_dataflow.rb:137:10:137:15 | call to use | local_dataflow.rb:137:10:137:26 | [false] ... && ... | +| local_dataflow.rb:137:10:137:15 | call to use | local_dataflow.rb:137:10:137:26 | [true] ... && ... | +| local_dataflow.rb:137:10:137:15 | self | local_dataflow.rb:137:21:137:26 | self | +| local_dataflow.rb:137:10:137:15 | self | local_dataflow.rb:141:9:141:14 | self | +| local_dataflow.rb:137:14:137:14 | x | local_dataflow.rb:137:25:137:25 | x | +| local_dataflow.rb:137:14:137:14 | x | local_dataflow.rb:141:13:141:13 | x | +| local_dataflow.rb:137:20:137:26 | [false] ! ... | local_dataflow.rb:137:10:137:26 | [false] ... && ... | +| local_dataflow.rb:137:20:137:26 | [true] ! ... | local_dataflow.rb:137:10:137:26 | [true] ... && ... | +| local_dataflow.rb:137:21:137:26 | [post] self | local_dataflow.rb:141:9:141:14 | self | +| local_dataflow.rb:137:21:137:26 | call to use | local_dataflow.rb:137:20:137:26 | [false] ! ... | +| local_dataflow.rb:137:21:137:26 | call to use | local_dataflow.rb:137:20:137:26 | [true] ! ... | +| local_dataflow.rb:137:21:137:26 | self | local_dataflow.rb:141:9:141:14 | self | +| local_dataflow.rb:137:25:137:25 | x | local_dataflow.rb:141:13:141:13 | x | +| local_dataflow.rb:141:8:141:14 | [false] ! ... | local_dataflow.rb:141:8:141:37 | [false] ... \|\| ... | +| local_dataflow.rb:141:8:141:14 | [false] ! ... | local_dataflow.rb:141:8:141:37 | [true] ... \|\| ... | +| local_dataflow.rb:141:8:141:14 | [true] ! ... | local_dataflow.rb:141:8:141:37 | [true] ... \|\| ... | +| local_dataflow.rb:141:9:141:14 | [post] self | local_dataflow.rb:141:20:141:25 | self | +| local_dataflow.rb:141:9:141:14 | [post] self | local_dataflow.rb:147:5:147:10 | self | +| local_dataflow.rb:141:9:141:14 | call to use | local_dataflow.rb:141:8:141:14 | [false] ! ... | +| local_dataflow.rb:141:9:141:14 | call to use | local_dataflow.rb:141:8:141:14 | [true] ! ... | +| local_dataflow.rb:141:9:141:14 | self | local_dataflow.rb:141:20:141:25 | self | +| local_dataflow.rb:141:9:141:14 | self | local_dataflow.rb:147:5:147:10 | self | +| local_dataflow.rb:141:13:141:13 | x | local_dataflow.rb:141:24:141:24 | x | +| local_dataflow.rb:141:13:141:13 | x | local_dataflow.rb:147:9:147:9 | x | +| local_dataflow.rb:141:19:141:37 | [false] ( ... ) | local_dataflow.rb:141:8:141:37 | [false] ... \|\| ... | +| local_dataflow.rb:141:19:141:37 | [true] ( ... ) | local_dataflow.rb:141:8:141:37 | [true] ... \|\| ... | +| local_dataflow.rb:141:20:141:25 | [post] self | local_dataflow.rb:141:31:141:36 | self | +| local_dataflow.rb:141:20:141:25 | [post] self | local_dataflow.rb:143:11:143:16 | self | +| local_dataflow.rb:141:20:141:25 | call to use | local_dataflow.rb:141:20:141:36 | [false] ... && ... | +| local_dataflow.rb:141:20:141:25 | call to use | local_dataflow.rb:141:20:141:36 | [true] ... && ... | +| local_dataflow.rb:141:20:141:25 | self | local_dataflow.rb:141:31:141:36 | self | +| local_dataflow.rb:141:20:141:25 | self | local_dataflow.rb:143:11:143:16 | self | +| local_dataflow.rb:141:20:141:36 | [false] ... && ... | local_dataflow.rb:141:19:141:37 | [false] ( ... ) | +| local_dataflow.rb:141:20:141:36 | [true] ... && ... | local_dataflow.rb:141:19:141:37 | [true] ( ... ) | +| local_dataflow.rb:141:24:141:24 | x | local_dataflow.rb:141:35:141:35 | x | +| local_dataflow.rb:141:24:141:24 | x | local_dataflow.rb:143:15:143:15 | x | +| local_dataflow.rb:141:30:141:36 | [false] ! ... | local_dataflow.rb:141:20:141:36 | [false] ... && ... | +| local_dataflow.rb:141:30:141:36 | [true] ! ... | local_dataflow.rb:141:20:141:36 | [true] ... && ... | +| local_dataflow.rb:141:31:141:36 | [post] self | local_dataflow.rb:143:11:143:16 | self | +| local_dataflow.rb:141:31:141:36 | [post] self | local_dataflow.rb:147:5:147:10 | self | +| local_dataflow.rb:141:31:141:36 | call to use | local_dataflow.rb:141:30:141:36 | [false] ! ... | +| local_dataflow.rb:141:31:141:36 | call to use | local_dataflow.rb:141:30:141:36 | [true] ! ... | +| local_dataflow.rb:141:31:141:36 | self | local_dataflow.rb:143:11:143:16 | self | +| local_dataflow.rb:141:31:141:36 | self | local_dataflow.rb:147:5:147:10 | self | +| local_dataflow.rb:141:35:141:35 | x | local_dataflow.rb:143:15:143:15 | x | +| local_dataflow.rb:141:35:141:35 | x | local_dataflow.rb:147:9:147:9 | x | +| local_dataflow.rb:141:38:142:9 | then ... | local_dataflow.rb:141:5:145:7 | if ... | +| local_dataflow.rb:142:7:142:9 | nil | local_dataflow.rb:141:38:142:9 | then ... | +| local_dataflow.rb:143:5:144:16 | elsif ... | local_dataflow.rb:141:5:145:7 | if ... | +| local_dataflow.rb:143:11:143:16 | [post] self | local_dataflow.rb:143:21:143:26 | self | +| local_dataflow.rb:143:11:143:16 | [post] self | local_dataflow.rb:144:11:144:16 | self | +| local_dataflow.rb:143:11:143:16 | call to use | local_dataflow.rb:143:11:143:26 | [false] ... \|\| ... | +| local_dataflow.rb:143:11:143:16 | call to use | local_dataflow.rb:143:11:143:26 | [true] ... \|\| ... | +| local_dataflow.rb:143:11:143:16 | self | local_dataflow.rb:143:21:143:26 | self | +| local_dataflow.rb:143:11:143:16 | self | local_dataflow.rb:144:11:144:16 | self | +| local_dataflow.rb:143:15:143:15 | x | local_dataflow.rb:143:25:143:25 | x | +| local_dataflow.rb:143:15:143:15 | x | local_dataflow.rb:144:15:144:15 | x | +| local_dataflow.rb:143:21:143:26 | [post] self | local_dataflow.rb:144:11:144:16 | self | +| local_dataflow.rb:143:21:143:26 | [post] self | local_dataflow.rb:147:5:147:10 | self | +| local_dataflow.rb:143:21:143:26 | call to use | local_dataflow.rb:143:11:143:26 | [false] ... \|\| ... | +| local_dataflow.rb:143:21:143:26 | call to use | local_dataflow.rb:143:11:143:26 | [true] ... \|\| ... | +| local_dataflow.rb:143:21:143:26 | self | local_dataflow.rb:144:11:144:16 | self | +| local_dataflow.rb:143:21:143:26 | self | local_dataflow.rb:147:5:147:10 | self | +| local_dataflow.rb:143:25:143:25 | x | local_dataflow.rb:144:15:144:15 | x | +| local_dataflow.rb:143:25:143:25 | x | local_dataflow.rb:147:9:147:9 | x | +| local_dataflow.rb:143:27:144:16 | then ... | local_dataflow.rb:143:5:144:16 | elsif ... | +| local_dataflow.rb:144:11:144:16 | [post] self | local_dataflow.rb:147:5:147:10 | self | +| local_dataflow.rb:144:11:144:16 | call to use | local_dataflow.rb:143:27:144:16 | then ... | +| local_dataflow.rb:144:11:144:16 | self | local_dataflow.rb:147:5:147:10 | self | +| local_dataflow.rb:144:15:144:15 | x | local_dataflow.rb:147:9:147:9 | x | +| local_dataflow.rb:147:5:147:10 | [post] self | local_dataflow.rb:148:5:148:10 | self | +| local_dataflow.rb:147:5:147:10 | self | local_dataflow.rb:148:5:148:10 | self | +| local_dataflow.rb:147:9:147:9 | x | local_dataflow.rb:148:9:148:9 | x | +| local_dataflow.rb:148:5:148:10 | call to use | local_dataflow.rb:132:12:148:10 | then ... | diff --git a/ruby/ql/test/library-tests/dataflow/local/local_dataflow.rb b/ruby/ql/test/library-tests/dataflow/local/local_dataflow.rb index f3c6a097273..d72ed8ac5d4 100644 --- a/ruby/ql/test/library-tests/dataflow/local/local_dataflow.rb +++ b/ruby/ql/test/library-tests/dataflow/local/local_dataflow.rb @@ -122,3 +122,29 @@ end def dup_tap sink(source(1).dup.tap { |x| puts "hello" }.dup) # $ hasValueFlow=1 end + +def use x + rand() +end + +def use_use_madness + x = "" + if use(x) + if use(x) || use(x) + use(x) + else + use(x) + if use(x) && !use(x) + end + end + + if !use(x) || (use(x) && !use(x)) + nil + elsif use(x) || use(x) + use(x) + end + + use(x) + use(x) + end +end \ No newline at end of file From 81a1fa167ab6569bb0956ede441af3e1b31bf63b Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 10 Nov 2022 16:40:58 +0100 Subject: [PATCH 0269/1420] SSA: Expose phi-reads --- shared/ssa/codeql/ssa/Ssa.qll | 591 +++++++++++++++++++++++++--------- 1 file changed, 431 insertions(+), 160 deletions(-) diff --git a/shared/ssa/codeql/ssa/Ssa.qll b/shared/ssa/codeql/ssa/Ssa.qll index 19f31f7c8bb..f7f7683d386 100644 --- a/shared/ssa/codeql/ssa/Ssa.qll +++ b/shared/ssa/codeql/ssa/Ssa.qll @@ -264,8 +264,22 @@ module Make { ) } + /** + * Holds if `bb` is in the dominance frontier of a block containing a + * read of `v`. + */ + pragma[nomagic] + private predicate inReadDominanceFrontier(BasicBlock bb, SourceVariable v) { + exists(BasicBlock readbb | inDominanceFrontier(readbb, bb) | + lastRefIsRead(readbb, v) + or + exists(TPhiReadNode(v, readbb)) and + not ref(readbb, _, v, _) + ) + } + cached - newtype TDefinition = + private newtype TDefinitionExt = TWriteDef(SourceVariable v, BasicBlock bb, int i) { variableWrite(bb, i, v, _) and liveAfterWrite(bb, i, v) @@ -273,8 +287,16 @@ module Make { TPhiNode(SourceVariable v, BasicBlock bb) { inDefDominanceFrontier(bb, v) and liveAtEntry(bb, v) + } or + TPhiReadNode(SourceVariable v, BasicBlock bb) { + inReadDominanceFrontier(bb, v) and + liveAtEntry(bb, v) and + // no need to create a phi-read if there is already a normal phi + not any(PhiNode def).definesAt(v, bb, _) } + private class TDefinition = TWriteDef or TPhiNode; + private module SsaDefReaches { newtype TSsaRefKind = SsaActualRead() or @@ -283,6 +305,10 @@ module Make { class SsaRead = SsaActualRead or SsaPhiRead; + class SsaDefExt = SsaDef or SsaPhiRead; + + SsaDefExt ssaDefExt() { any() } + /** * A classification of SSA variable references into reads and definitions. */ @@ -307,98 +333,27 @@ module Make { } } - /** - * Holds if `bb` is in the dominance frontier of a block containing a - * read of `v`. - */ - pragma[nomagic] - private predicate inReadDominanceFrontier(BasicBlock bb, SourceVariable v) { - exists(BasicBlock readbb | inDominanceFrontier(readbb, bb) | - lastRefIsRead(readbb, v) - or - phiRead(readbb, v) - ) - } - - /** - * Holds if a phi-read node should be inserted for variable `v` at the beginning - * of basic block `bb`. - * - * Phi-read nodes are like normal phi nodes, but they are inserted based on reads - * instead of writes, and only if the dominance-frontier block does not already - * contain a reference (read or write) to `v`. Unlike normal phi nodes, this is - * an internal implementation detail that is not exposed. - * - * The motivation for adding phi-reads is to improve performance of the use-use - * calculation in cases where there is a large number of reads that can reach the - * same join-point, and from there reach a large number of basic blocks. Example: - * - * ```cs - * if (a) - * use(x); - * else if (b) - * use(x); - * else if (c) - * use(x); - * else if (d) - * use(x); - * // many more ifs ... - * - * // phi-read for `x` inserted here - * - * // program not mentioning `x`, with large basic block graph - * - * use(x); - * ``` - * - * Without phi-reads, the analysis has to replicate reachability for each of - * the guarded uses of `x`. However, with phi-reads, the analysis will limit - * each conditional use of `x` to reach the basic block containing the phi-read - * node for `x`, and only that basic block will have to compute reachability - * through the remainder of the large program. - * - * Like normal reads, each phi-read node `phi-read` can be reached from exactly - * one SSA definition (without passing through another definition): Assume, for - * the sake of contradiction, that there are two reaching definitions `def1` and - * `def2`. Now, if both `def1` and `def2` dominate `phi-read`, then the nearest - * dominating definition will prevent the other from reaching `phi-read`. So, at - * least one of `def1` and `def2` cannot dominate `phi-read`; assume it is `def1`. - * Then `def1` must go through one of its dominance-frontier blocks in order to - * reach `phi-read`. However, such a block will always start with a (normal) phi - * node, which contradicts reachability. - * - * Also, like normal reads, the unique SSA definition `def` that reaches `phi-read`, - * will dominate `phi-read`. Assuming it doesn't means that the path from `def` - * to `phi-read` goes through a dominance-frontier block, and hence a phi node, - * which contradicts reachability. - */ - pragma[nomagic] - predicate phiRead(BasicBlock bb, SourceVariable v) { - inReadDominanceFrontier(bb, v) and - liveAtEntry(bb, v) and - // only if there are no other references to `v` inside `bb` - not ref(bb, _, v, _) and - not exists(Definition def | def.definesAt(v, bb, _)) - } - /** * Holds if the `i`th node of basic block `bb` is a reference to `v`, - * either a read (when `k` is `SsaRead()`) or an SSA definition (when `k` - * is `SsaDef()`). + * either a read (when `k` is `SsaActualRead()`), an SSA definition (when `k` + * is `SsaDef()`), or a phi-read (when `k` is `SsaPhiRead()`). * - * Unlike `Liveness::ref`, this includes `phi` nodes. + * Unlike `Liveness::ref`, this includes `phi` (read) nodes. */ pragma[nomagic] predicate ssaRef(BasicBlock bb, int i, SourceVariable v, SsaRefKind k) { variableRead(bb, i, v, _) and k = SsaActualRead() or - phiRead(bb, v) and - i = -1 and - k = SsaPhiRead() - or - any(Definition def).definesAt(v, bb, i) and - k = SsaDef() + any(DefinitionExt def).definesAt(v, bb, i, k) + } + + /** + * Holds if the `i`th node of basic block `bb` is a reference to `v`, and + * this reference is not a phi-read. + */ + predicate ssaRefNonPhiRead(BasicBlock bb, int i, SourceVariable v) { + ssaRef(bb, i, v, [SsaActualRead().(TSsaRefKind), SsaDef()]) } private newtype OrderedSsaRefIndex = @@ -445,37 +400,36 @@ module Make { * Holds if the SSA definition `def` reaches rank index `rnk` in its own * basic block `bb`. */ - predicate ssaDefReachesRank(BasicBlock bb, Definition def, int rnk, SourceVariable v) { + predicate ssaDefReachesRank(BasicBlock bb, DefinitionExt def, int rnk, SourceVariable v) { exists(int i | - rnk = ssaRefRank(bb, i, v, SsaDef()) and - def.definesAt(v, bb, i) + rnk = ssaRefRank(bb, i, v, ssaDefExt()) and + def.definesAt(v, bb, i, _) ) or ssaDefReachesRank(bb, def, rnk - 1, v) and - rnk = ssaRefRank(bb, _, v, any(SsaRead k)) + rnk = ssaRefRank(bb, _, v, SsaActualRead()) } /** * Holds if the SSA definition of `v` at `def` reaches index `i` in the same * basic block `bb`, without crossing another SSA definition of `v`. */ - predicate ssaDefReachesReadWithinBlock(SourceVariable v, Definition def, BasicBlock bb, int i) { + predicate ssaDefReachesReadWithinBlock(SourceVariable v, DefinitionExt def, BasicBlock bb, int i) { exists(int rnk | ssaDefReachesRank(bb, def, rnk, v) and - rnk = ssaRefRank(bb, i, v, any(SsaRead k)) + rnk = ssaRefRank(bb, i, v, SsaActualRead()) ) } /** * Same as `ssaRefRank()`, but restricted to a particular SSA definition `def`. */ - int ssaDefRank(Definition def, SourceVariable v, BasicBlock bb, int i, SsaRefKind k) { - v = def.getSourceVariable() and + int ssaDefRank(DefinitionExt def, SourceVariable v, BasicBlock bb, int i, SsaRefKind k) { result = ssaRefRank(bb, i, v, k) and ( - ssaDefReachesRead(_, def, bb, i) + ssaDefReachesReadExt(v, def, bb, i) or - def.definesAt(_, bb, i) + def.definesAt(v, bb, i, k) ) } @@ -484,18 +438,38 @@ module Make { * last reference to `v` inside `bb`. */ pragma[noinline] - predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) { + predicate lastSsaRefExt(DefinitionExt def, SourceVariable v, BasicBlock bb, int i) { ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) } - predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v, SsaRefKind k) { + /** Gets a phi-read node into which `inp` is an input, if any. */ + pragma[nomagic] + private DefinitionExt getAPhiReadOutput(DefinitionExt inp) { + phiHasInputFromBlockExt(result.(PhiReadNode), inp, _) + } + + pragma[nomagic] + DefinitionExt getAnUltimateOutput(Definition def) { result = getAPhiReadOutput*(def) } + + /** + * Same as `lastSsaRefExt`, but ignores phi-reads. + */ + pragma[noinline] + predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) { + lastSsaRefExt(getAnUltimateOutput(def), v, bb, i) and + ssaRefNonPhiRead(bb, i, v) + } + + predicate defOccursInBlock(DefinitionExt def, BasicBlock bb, SourceVariable v, SsaRefKind k) { exists(ssaDefRank(def, v, bb, _, k)) } pragma[noinline] - private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) { - ssaDefReachesEndOfBlock(bb, def, _) and - not defOccursInBlock(_, bb, def.getSourceVariable(), _) + private predicate ssaDefReachesThroughBlock(DefinitionExt def, BasicBlock bb) { + exists(SourceVariable v | + ssaDefReachesEndOfBlockExt(bb, def, v) and + not defOccursInBlock(_, bb, v, _) + ) } /** @@ -503,85 +477,114 @@ module Make { * `bb2` is a transitive successor of `bb1`, `def` is live at the end of _some_ * predecessor of `bb2`, and the underlying variable for `def` is neither read * nor written in any block on the path between `bb1` and `bb2`. - * - * Phi reads are considered as normal reads for this predicate. */ pragma[nomagic] - private predicate varBlockReachesInclPhiRead(Definition def, BasicBlock bb1, BasicBlock bb2) { - defOccursInBlock(def, bb1, _, _) and + predicate varBlockReachesExt(DefinitionExt def, SourceVariable v, BasicBlock bb1, BasicBlock bb2) { + defOccursInBlock(def, bb1, v, _) and bb2 = getABasicBlockSuccessor(bb1) or exists(BasicBlock mid | - varBlockReachesInclPhiRead(def, bb1, mid) and + varBlockReachesExt(def, v, bb1, mid) and ssaDefReachesThroughBlock(def, mid) and bb2 = getABasicBlockSuccessor(mid) ) } pragma[nomagic] - private predicate phiReadStep(Definition def, SourceVariable v, BasicBlock bb1, BasicBlock bb2) { - varBlockReachesInclPhiRead(def, bb1, bb2) and - defOccursInBlock(def, bb2, v, SsaPhiRead()) + private predicate phiReadStep(DefinitionExt def, PhiReadNode phi, BasicBlock bb1, BasicBlock bb2) { + exists(SourceVariable v | + varBlockReachesExt(pragma[only_bind_into](def), v, bb1, pragma[only_bind_into](bb2)) and + phi.definesAt(v, bb2, _, _) and + not ref(bb2, _, v, _) + ) } pragma[nomagic] - private predicate varBlockReachesExclPhiRead(Definition def, BasicBlock bb1, BasicBlock bb2) { - varBlockReachesInclPhiRead(pragma[only_bind_into](def), bb1, pragma[only_bind_into](bb2)) and - ssaRef(bb2, _, def.getSourceVariable(), [SsaActualRead().(TSsaRefKind), SsaDef()]) + private predicate varBlockReachesExclPhiRead( + DefinitionExt def, SourceVariable v, BasicBlock bb1, BasicBlock bb2 + ) { + varBlockReachesExt(def, v, bb1, bb2) and + ssaRefNonPhiRead(bb2, _, v) or - exists(BasicBlock mid | - varBlockReachesExclPhiRead(def, mid, bb2) and - phiReadStep(def, _, bb1, mid) + exists(PhiReadNode phi, BasicBlock mid | + varBlockReachesExclPhiRead(phi, v, mid, bb2) and + phiReadStep(def, phi, bb1, mid) ) } /** - * Holds if `def` is accessed in basic block `bb1` (either a read or a write), - * the underlying variable `v` of `def` is accessed in basic block `bb2` - * (either a read or a write), `bb2` is a transitive successor of `bb1`, and - * `v` is neither read nor written in any block on the path between `bb1` - * and `bb2`. + * Same as `varBlockReachesExt`, but ignores phi-reads, and furthermore + * `bb2` is restricted to blocks in which the underlying variable `v` of + * `def` is referenced (either a read or a write). */ pragma[nomagic] - predicate varBlockReaches(Definition def, BasicBlock bb1, BasicBlock bb2) { - varBlockReachesExclPhiRead(def, bb1, bb2) and - not defOccursInBlock(def, bb1, _, SsaPhiRead()) + predicate varBlockReachesRef(Definition def, SourceVariable v, BasicBlock bb1, BasicBlock bb2) { + varBlockReachesExclPhiRead(getAnUltimateOutput(def), v, bb1, bb2) and + ssaRefNonPhiRead(bb1, _, v) + } + + pragma[nomagic] + predicate defAdjacentReadExt(DefinitionExt def, BasicBlock bb1, BasicBlock bb2, int i2) { + exists(SourceVariable v | + varBlockReachesExt(def, v, bb1, bb2) and + ssaRefRank(bb2, i2, v, SsaActualRead()) = 1 + ) } pragma[nomagic] predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) { - varBlockReaches(def, bb1, bb2) and - ssaRefRank(bb2, i2, def.getSourceVariable(), SsaActualRead()) = 1 + exists(SourceVariable v | varBlockReachesRef(def, v, bb1, bb2) | + ssaRefRank(bb2, i2, v, SsaActualRead()) = 1 + or + ssaRefRank(bb2, _, v, SsaPhiRead()) = 1 and + ssaRefRank(bb2, i2, v, SsaActualRead()) = 2 + ) } /** * Holds if `def` is accessed in basic block `bb` (either a read or a write), - * `bb1` can reach a transitive successor `bb2` where `def` is no longer live, + * `bb` can reach a transitive successor `bb2` where `def` is no longer live, * and `v` is neither read nor written in any block on the path between `bb` * and `bb2`. */ pragma[nomagic] - predicate varBlockReachesExit(Definition def, BasicBlock bb) { - exists(BasicBlock bb2 | varBlockReachesInclPhiRead(def, bb, bb2) | + predicate varBlockReachesExitExt(DefinitionExt def, BasicBlock bb) { + exists(BasicBlock bb2 | varBlockReachesExt(def, _, bb, bb2) | not defOccursInBlock(def, bb2, _, _) and - not ssaDefReachesEndOfBlock(bb2, def, _) - ) - or - exists(BasicBlock mid | - varBlockReachesExit(def, mid) and - phiReadStep(def, _, bb, mid) + not ssaDefReachesEndOfBlockExt(bb2, def, _) ) } - } - predicate phiReadExposedForTesting = phiRead/2; + pragma[nomagic] + private predicate varBlockReachesExitExclPhiRead(DefinitionExt def, BasicBlock bb) { + exists(BasicBlock bb2, SourceVariable v | + varBlockReachesExt(def, v, bb, bb2) and + not defOccursInBlock(def, bb2, _, _) and + not ssaDefReachesEndOfBlockExt(bb2, def, _) and + not any(PhiReadNode phi).definesAt(v, bb2, _, _) + ) + or + exists(PhiReadNode phi, BasicBlock bb2 | + varBlockReachesExitExclPhiRead(phi, bb2) and + phiReadStep(def, phi, bb, bb2) + ) + } + + /** + * Same as `varBlockReachesExitExt`, but ignores phi-reads. + */ + pragma[nomagic] + predicate varBlockReachesExit(Definition def, BasicBlock bb) { + varBlockReachesExitExclPhiRead(getAnUltimateOutput(def), bb) + } + } private import SsaDefReaches pragma[nomagic] - private predicate liveThrough(BasicBlock bb, SourceVariable v) { + private predicate liveThroughExt(BasicBlock bb, SourceVariable v) { liveAtExit(bb, v) and - not ssaRef(bb, _, v, SsaDef()) + not ssaRef(bb, _, v, ssaDefExt()) } /** @@ -592,7 +595,7 @@ module Make { * SSA definition of `v`. */ pragma[nomagic] - predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable v) { + predicate ssaDefReachesEndOfBlockExt(BasicBlock bb, DefinitionExt def, SourceVariable v) { exists(int last | last = maxSsaRefRank(pragma[only_bind_into](bb), pragma[only_bind_into](v)) and ssaDefReachesRank(bb, def, last, v) and @@ -605,8 +608,31 @@ module Make { // the node. If two definitions dominate a node then one must dominate the // other, so therefore the definition of _closest_ is given by the dominator // tree. Thus, reaching definitions can be calculated in terms of dominance. - ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and - liveThrough(bb, pragma[only_bind_into](v)) + ssaDefReachesEndOfBlockExt(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and + liveThroughExt(bb, pragma[only_bind_into](v)) + } + + pragma[nomagic] + private predicate phiReadReachesEndOfBlock(BasicBlock pred, BasicBlock bb, SourceVariable v) { + exists(PhiReadNode phi | + ssaDefReachesEndOfBlockExt(bb, phi, v) and + pred = getImmediateBasicBlockDominator(phi.getBasicBlock()) + ) + } + + /** + * NB: If this predicate is exposed, it should be cached. + * + * Same as `ssaDefReachesEndOfBlockExt`, but ignores phi-reads. + */ + pragma[nomagic] + predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable v) { + ssaDefReachesEndOfBlockExt(bb, def, v) + or + exists(BasicBlock mid | + ssaDefReachesEndOfBlock(mid, def, v) and + phiReadReachesEndOfBlock(mid, bb, v) + ) } /** @@ -623,20 +649,50 @@ module Make { ) } + /** + * NB: If this predicate is exposed, it should be cached. + * + * Holds if `inp` is an input to the phi (read) node `phi` along the edge originating in `bb`. + */ + pragma[nomagic] + predicate phiHasInputFromBlockExt(DefinitionExt phi, DefinitionExt inp, BasicBlock bb) { + exists(SourceVariable v, BasicBlock bbDef | + phi.definesAt(v, bbDef, _, _) and + getABasicBlockPredecessor(bbDef) = bb and + ssaDefReachesEndOfBlockExt(bb, inp, v) + | + phi instanceof PhiNode or + phi instanceof PhiReadNode + ) + } + /** * NB: If this predicate is exposed, it should be cached. * * Holds if the SSA definition of `v` at `def` reaches a read at index `i` in - * basic block `bb`, without crossing another SSA definition of `v`. The read - * is of kind `rk`. + * basic block `bb`, without crossing another SSA definition of `v`. + */ + pragma[nomagic] + predicate ssaDefReachesReadExt(SourceVariable v, DefinitionExt def, BasicBlock bb, int i) { + ssaDefReachesReadWithinBlock(v, def, bb, i) + or + ssaRef(bb, i, v, SsaActualRead()) and + ssaDefReachesEndOfBlockExt(getABasicBlockPredecessor(bb), def, v) and + not ssaDefReachesReadWithinBlock(v, _, bb, i) + } + + /** + * NB: If this predicate is exposed, it should be cached. + * + * Same as `ssaDefReachesReadExt`, but ignores phi-reads. */ pragma[nomagic] predicate ssaDefReachesRead(SourceVariable v, Definition def, BasicBlock bb, int i) { ssaDefReachesReadWithinBlock(v, def, bb, i) or - ssaRef(bb, i, v, any(SsaRead k)) and + ssaRef(bb, i, v, SsaActualRead()) and ssaDefReachesEndOfBlock(getABasicBlockPredecessor(bb), def, v) and - not ssaDefReachesReadWithinBlock(v, _, bb, i) + not exists(Definition other | ssaDefReachesReadWithinBlock(v, other, bb, i)) } /** @@ -647,14 +703,32 @@ module Make { * path between them without any read of `def`. */ pragma[nomagic] - predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2) { + predicate adjacentDefReadExt( + DefinitionExt def, SourceVariable v, BasicBlock bb1, int i1, BasicBlock bb2, int i2 + ) { exists(int rnk | - rnk = ssaDefRank(def, _, bb1, i1, _) and - rnk + 1 = ssaDefRank(def, _, bb1, i2, SsaActualRead()) and - variableRead(bb1, i2, _, _) and + rnk = ssaDefRank(def, v, bb1, i1, _) and + rnk + 1 = ssaDefRank(def, v, bb1, i2, SsaActualRead()) and + variableRead(bb1, i2, v, _) and bb2 = bb1 ) or + lastSsaRefExt(def, v, bb1, i1) and + defAdjacentReadExt(def, bb1, bb2, i2) + } + + /** + * NB: If this predicate is exposed, it should be cached. + * + * Same as `adjacentDefReadExt`, but ignores phi-reads. + */ + pragma[nomagic] + predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2) { + exists(SourceVariable v | + adjacentDefReadExt(getAnUltimateOutput(def), v, bb1, i1, bb2, i2) and + ssaRefNonPhiRead(bb1, i1, v) + ) + or lastSsaRef(def, _, bb1, i1) and defAdjacentRead(def, bb1, bb2, i2) } @@ -704,19 +778,41 @@ module Make { * without passing through another read or write. */ pragma[nomagic] + predicate lastRefRedefExt( + DefinitionExt def, SourceVariable v, BasicBlock bb, int i, DefinitionExt next + ) { + // Next reference to `v` inside `bb` is a write + exists(int rnk, int j | + rnk = ssaDefRank(def, v, bb, i, _) and + next.definesAt(v, bb, j, _) and + rnk + 1 = ssaRefRank(bb, j, v, ssaDefExt()) + ) + or + // Can reach a write using one or more steps + lastSsaRefExt(def, v, bb, i) and + exists(BasicBlock bb2 | + varBlockReachesExt(def, v, bb, bb2) and + 1 = ssaDefRank(next, v, bb2, _, ssaDefExt()) + ) + } + + /** + * NB: If this predicate is exposed, it should be cached. + * + * Same as `lastRefRedefExt`, but ignores phi-reads. + */ + pragma[nomagic] predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) { exists(SourceVariable v | - // Next reference to `v` inside `bb` is a write - exists(int rnk, int j | - rnk = ssaDefRank(def, v, bb, i, _) and - next.definesAt(v, bb, j) and - rnk + 1 = ssaRefRank(bb, j, v, SsaDef()) - ) - or - // Can reach a write using one or more steps + lastRefRedefExt(getAnUltimateOutput(def), v, bb, i, next) and + ssaRefNonPhiRead(bb, i, v) + ) + or + // Can reach a write using one or more steps + exists(SourceVariable v | lastSsaRef(def, v, bb, i) and exists(BasicBlock bb2 | - varBlockReaches(def, bb, bb2) and + varBlockReachesRef(def, v, bb, bb2) and 1 = ssaDefRank(next, v, bb2, _, SsaDef()) ) ) @@ -770,6 +866,25 @@ module Make { * another read. */ pragma[nomagic] + predicate lastRefExt(DefinitionExt def, BasicBlock bb, int i) { + // Can reach another definition + lastRefRedefExt(def, _, bb, i, _) + or + exists(SourceVariable v | lastSsaRefExt(def, v, bb, i) | + // Can reach exit directly + bb instanceof ExitBasicBlock + or + // Can reach a block using one or more steps, where `def` is no longer live + varBlockReachesExitExt(def, bb) + ) + } + + /** + * NB: If this predicate is exposed, it should be cached. + * + * Same as `lastRefExt`, but ignores phi-reads. + */ + pragma[nomagic] predicate lastRef(Definition def, BasicBlock bb, int i) { // Can reach another definition lastRefRedef(def, bb, i, _) @@ -819,7 +934,7 @@ module Make { final BasicBlock getBasicBlock() { this.definesAt(_, result, _) } /** Gets a textual representation of this SSA definition. */ - string toString() { none() } + string toString() { result = "SSA def(" + this.getSourceVariable() + ")" } } /** An SSA definition that corresponds to a write. */ @@ -829,13 +944,11 @@ module Make { private int i; WriteDefinition() { this = TWriteDef(v, bb, i) } - - override string toString() { result = "WriteDef" } } /** A phi node. */ class PhiNode extends Definition, TPhiNode { - override string toString() { result = "Phi" } + override string toString() { result = "SSA phi(" + this.getSourceVariable() + ")" } } /** @@ -851,6 +964,120 @@ module Make { } } + /** + * An extended static single assignment (SSA) definition. + * + * This is either a normal SSA definition (`Definition`) or a + * phi-read node (`PhiReadNode`). + */ + class DefinitionExt extends TDefinitionExt { + /** Gets the source variable underlying this SSA definition. */ + SourceVariable getSourceVariable() { this.definesAt(result, _, _, _) } + + /** + * Holds if this SSA definition defines `v` at index `i` in basic block `bb`. + * Phi nodes are considered to be at index `-1`, while normal variable writes + * are at the index of the control flow node they wrap. + */ + final predicate definesAt(SourceVariable v, BasicBlock bb, int i, SsaRefKind kind) { + this.(Definition).definesAt(v, bb, i) and + kind = SsaDef() + or + this = TPhiReadNode(v, bb) and i = -1 and kind = SsaPhiRead() + } + + /** Gets the basic block to which this SSA definition belongs. */ + final BasicBlock getBasicBlock() { this.definesAt(_, result, _, _) } + + /** Gets a textual representation of this SSA definition. */ + string toString() { result = this.(Definition).toString() } + } + + /** + * A phi-read node. + * + * Phi-read nodes are like normal phi nodes, but they are inserted based on reads + * instead of writes, and only if the dominance-frontier block does not already + * contain a normal phi node. + * + * The motivation for adding phi-reads is to improve performance of the use-use + * calculation in cases where there is a large number of reads that can reach the + * same join-point, and from there reach a large number of basic blocks. Example: + * + * ```cs + * if (a) + * use(x); + * else if (b) + * use(x); + * else if (c) + * use(x); + * else if (d) + * use(x); + * // many more ifs ... + * + * // phi-read for `x` inserted here + * + * // program not mentioning `x`, with large basic block graph + * + * use(x); + * ``` + * + * Without phi-reads, the analysis has to replicate reachability for each of + * the guarded uses of `x`. However, with phi-reads, the analysis will limit + * each conditional use of `x` to reach the basic block containing the phi-read + * node for `x`, and only that basic block will have to compute reachability + * through the remainder of the large program. + * + * Another motivation for phi-reads is when a large number of reads can reach + * another large number of reads: + * + * ```cs + * if (a) + * use(x); + * else if (b) + * use(x); + * else if (c) + * use(x); + * else if (d) + * use(x); + * // many more ifs ... + * + * // phi-read for `x` inserted here + * + * if (a) + * use(x); + * else if (b) + * use(x); + * else if (c) + * use(x); + * else if (d) + * use(x); + * // many more ifs ... + * ``` + * + * Without phi-reads, one needs to add `n*m` data-flow edges (assuming `n` reads + * before the phi-read and `m` reads after the phi-read), whereas if we include + * phi-reads in the data-flow graph, we only need to add `n+m` edges. + * + * Like normal reads, each phi-read node `phi-read` can be reached from exactly + * one SSA definition (without passing through another definition): Assume, for + * the sake of contradiction, that there are two reaching definitions `def1` and + * `def2`. Now, if both `def1` and `def2` dominate `phi-read`, then the nearest + * dominating definition will prevent the other from reaching `phi-read`. So, at + * least one of `def1` and `def2` cannot dominate `phi-read`; assume it is `def1`. + * Then `def1` must go through one of its dominance-frontier blocks in order to + * reach `phi-read`. However, such a block will always start with a (normal) phi + * node, which contradicts reachability. + * + * Also, like normal reads, the unique SSA definition `def` that reaches `phi-read`, + * will dominate `phi-read`. Assuming it doesn't means that the path from `def` + * to `phi-read` goes through a dominance-frontier block, and hence a phi node, + * which contradicts reachability. + */ + class PhiReadNode extends DefinitionExt, TPhiReadNode { + override string toString() { result = "SSA phi read(" + this.getSourceVariable() + ")" } + } + /** Provides a set of consistency queries. */ module Consistency { /** A definition that is relevant for the consistency queries. */ @@ -861,18 +1088,40 @@ module Make { ); } + /** A definition that is relevant for the consistency queries. */ + abstract class RelevantDefinitionExt extends DefinitionExt { + /** Override this predicate to ensure locations in consistency results. */ + abstract predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ); + } + /** Holds if a read can be reached from multiple definitions. */ query predicate nonUniqueDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) { ssaDefReachesRead(v, def, bb, i) and not exists(unique(Definition def0 | ssaDefReachesRead(v, def0, bb, i))) } + /** Holds if a read can be reached from multiple definitions. */ + query predicate nonUniqueDefExt( + RelevantDefinitionExt def, SourceVariable v, BasicBlock bb, int i + ) { + ssaDefReachesReadExt(v, def, bb, i) and + not exists(unique(DefinitionExt def0 | ssaDefReachesReadExt(v, def0, bb, i))) + } + /** Holds if a read cannot be reached from a definition. */ query predicate readWithoutDef(SourceVariable v, BasicBlock bb, int i) { variableRead(bb, i, v, _) and not ssaDefReachesRead(v, _, bb, i) } + /** Holds if a read cannot be reached from a definition. */ + query predicate readWithoutDefExt(SourceVariable v, BasicBlock bb, int i) { + variableRead(bb, i, v, _) and + not ssaDefReachesReadExt(v, _, bb, i) + } + /** Holds if a definition cannot reach a read. */ query predicate deadDef(RelevantDefinition def, SourceVariable v) { v = def.getSourceVariable() and @@ -881,6 +1130,14 @@ module Make { not uncertainWriteDefinitionInput(_, def) } + /** Holds if a definition cannot reach a read. */ + query predicate deadDefExt(RelevantDefinitionExt def, SourceVariable v) { + v = def.getSourceVariable() and + not ssaDefReachesReadExt(_, def, _, _) and + not phiHasInputFromBlockExt(_, def, _) and + not uncertainWriteDefinitionInput(_, def) + } + /** Holds if a read is not dominated by a definition. */ query predicate notDominatedByDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) { exists(BasicBlock bbDef, int iDef | def.definesAt(v, bbDef, iDef) | @@ -892,5 +1149,19 @@ module Make { not def.definesAt(v, getImmediateBasicBlockDominator*(bb), _) ) } + + /** Holds if a read is not dominated by a definition. */ + query predicate notDominatedByDefExt( + RelevantDefinitionExt def, SourceVariable v, BasicBlock bb, int i + ) { + exists(BasicBlock bbDef, int iDef | def.definesAt(v, bbDef, iDef, _) | + ssaDefReachesReadWithinBlock(v, def, bb, i) and + (bb != bbDef or i < iDef) + or + ssaDefReachesReadExt(v, def, bb, i) and + not ssaDefReachesReadWithinBlock(v, def, bb, i) and + not def.definesAt(v, getImmediateBasicBlockDominator*(bb), _, _) + ) + } } } From bd78e73131061aa7732278546656e19146016a61 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 10 Nov 2022 16:41:08 +0100 Subject: [PATCH 0270/1420] C#: Add tests for phi reads --- .../ql/consistency-queries/SsaConsistency.ql | 11 ++- .../lib/semmle/code/csharp/dataflow/SSA.qll | 38 +++----- .../code/csharp/dataflow/internal/SsaImpl.qll | 91 ++++++++++++++++--- .../dataflow/ssa/SSAPhiRead.expected | 34 ++++++- .../library-tests/dataflow/ssa/SSAPhiRead.ql | 17 +++- .../test/library-tests/dataflow/ssa/Test.cs | 2 +- 6 files changed, 144 insertions(+), 49 deletions(-) diff --git a/csharp/ql/consistency-queries/SsaConsistency.ql b/csharp/ql/consistency-queries/SsaConsistency.ql index bd666a71e49..225aeb4e6de 100644 --- a/csharp/ql/consistency-queries/SsaConsistency.ql +++ b/csharp/ql/consistency-queries/SsaConsistency.ql @@ -1,5 +1,6 @@ import csharp -import semmle.code.csharp.dataflow.internal.SsaImpl::Consistency +import semmle.code.csharp.dataflow.internal.SsaImpl as Impl +import Impl::Consistency import Ssa class MyRelevantDefinition extends RelevantDefinition, Ssa::Definition { @@ -10,6 +11,14 @@ class MyRelevantDefinition extends RelevantDefinition, Ssa::Definition { } } +class MyRelevantDefinitionExt extends RelevantDefinitionExt, Impl::DefinitionExt { + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + query predicate localDeclWithSsaDef(LocalVariableDeclExpr d) { // Local variables in C# must be initialized before every use, so uninitialized // local variables should not have an SSA definition, as that would imply that diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll index f377d94e15c..8764da0a784 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll @@ -138,25 +138,6 @@ module Ssa { } } - private string getSplitString(Definition def) { - exists(ControlFlow::BasicBlock bb, int i, ControlFlow::Node cfn | - def.definesAt(_, bb, i) and - result = cfn.(ControlFlow::Nodes::ElementNode).getSplitsString() - | - cfn = bb.getNode(i) - or - not exists(bb.getNode(i)) and - cfn = bb.getFirstNode() - ) - } - - private string getToStringPrefix(Definition def) { - result = "[" + getSplitString(def) + "] " - or - not exists(getSplitString(def)) and - result = "" - } - /** * A static single assignment (SSA) definition. Either an explicit variable * definition (`ExplicitDefinition`), an implicit variable definition @@ -521,8 +502,8 @@ module Ssa { override string toString() { if this.getADefinition() instanceof AssignableDefinitions::ImplicitParameterDefinition - then result = getToStringPrefix(this) + "SSA param(" + this.getSourceVariable() + ")" - else result = getToStringPrefix(this) + "SSA def(" + this.getSourceVariable() + ")" + then result = SsaImpl::getToStringPrefix(this) + "SSA param(" + this.getSourceVariable() + ")" + else result = SsaImpl::getToStringPrefix(this) + "SSA def(" + this.getSourceVariable() + ")" } override Location getLocation() { result = ad.getLocation() } @@ -570,8 +551,12 @@ module Ssa { override string toString() { if this.getSourceVariable().getAssignable() instanceof LocalScopeVariable - then result = getToStringPrefix(this) + "SSA capture def(" + this.getSourceVariable() + ")" - else result = getToStringPrefix(this) + "SSA entry def(" + this.getSourceVariable() + ")" + then + result = + SsaImpl::getToStringPrefix(this) + "SSA capture def(" + this.getSourceVariable() + ")" + else + result = + SsaImpl::getToStringPrefix(this) + "SSA entry def(" + this.getSourceVariable() + ")" } override Location getLocation() { result = this.getCallable().getLocation() } @@ -612,7 +597,7 @@ module Ssa { } override string toString() { - result = getToStringPrefix(this) + "SSA call def(" + this.getSourceVariable() + ")" + result = SsaImpl::getToStringPrefix(this) + "SSA call def(" + this.getSourceVariable() + ")" } override Location getLocation() { result = this.getCall().getLocation() } @@ -640,7 +625,8 @@ module Ssa { final Definition getQualifierDefinition() { result = q } override string toString() { - result = getToStringPrefix(this) + "SSA qualifier def(" + this.getSourceVariable() + ")" + result = + SsaImpl::getToStringPrefix(this) + "SSA qualifier def(" + this.getSourceVariable() + ")" } override Location getLocation() { result = this.getQualifierDefinition().getLocation() } @@ -682,7 +668,7 @@ module Ssa { } override string toString() { - result = getToStringPrefix(this) + "SSA phi(" + this.getSourceVariable() + ")" + result = SsaImpl::getToStringPrefix(this) + "SSA phi(" + this.getSourceVariable() + ")" } /* diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll index adfacd2970a..98a1cad843a 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll @@ -49,7 +49,23 @@ private module SsaInput implements SsaImplCommon::InputSig { } } -import SsaImplCommon::Make +private import SsaImplCommon::Make as Impl + +class Definition = Impl::Definition; + +class WriteDefinition = Impl::WriteDefinition; + +class UncertainWriteDefinition = Impl::UncertainWriteDefinition; + +class PhiNode = Impl::PhiNode; + +module Consistency = Impl::Consistency; + +module ExposedForTestingOnly { + predicate ssaDefReachesReadExt = Impl::ssaDefReachesReadExt/4; + + predicate phiHasInputFromBlockExt = Impl::phiHasInputFromBlockExt/3; +} /** * Holds if the `i`th node of basic block `bb` reads source variable `v`. @@ -1072,7 +1088,7 @@ private predicate adjacentDefRead( Definition def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2, SsaInput::SourceVariable v ) { - adjacentDefRead(def, bb1, i1, bb2, i2) and + Impl::adjacentDefRead(def, bb1, i1, bb2, i2) and v = def.getSourceVariable() } @@ -1088,7 +1104,7 @@ private predicate adjacentDefReachesRead( exists(SsaInput::BasicBlock bb3, int i3 | adjacentDefReachesRead(def, bb1, i1, bb3, i3) and SsaInput::variableRead(bb3, i3, _, false) and - adjacentDefRead(def, bb3, i3, bb2, i2) + Impl::adjacentDefRead(def, bb3, i3, bb2, i2) ) } @@ -1111,11 +1127,11 @@ private predicate adjacentDefReachesUncertainRead( /** Same as `lastRefRedef`, but skips uncertain reads. */ pragma[nomagic] private predicate lastRefSkipUncertainReads(Definition def, SsaInput::BasicBlock bb, int i) { - lastRef(def, bb, i) and + Impl::lastRef(def, bb, i) and not SsaInput::variableRead(bb, i, def.getSourceVariable(), false) or exists(SsaInput::BasicBlock bb0, int i0 | - lastRef(def, bb0, i0) and + Impl::lastRef(def, bb0, i0) and adjacentDefReachesUncertainRead(def, bb, i, bb0, i0) ) } @@ -1237,7 +1253,7 @@ private module Cached { v = def.getSourceVariable() and capturedReadIn(_, _, v, edef.getSourceVariable(), c, additionalCalls) and def = def0.getAnUltimateDefinition() and - ssaDefReachesRead(_, def0, bb, i) and + Impl::ssaDefReachesRead(_, def0, bb, i) and capturedReadIn(bb, i, v, _, _, _) and c = bb.getNode(i) ) @@ -1264,18 +1280,18 @@ private module Cached { cached predicate isLiveAtEndOfBlock(Definition def, ControlFlow::BasicBlock bb) { - ssaDefReachesEndOfBlock(bb, def, _) + Impl::ssaDefReachesEndOfBlock(bb, def, _) } cached - Definition phiHasInputFromBlock(PhiNode phi, ControlFlow::BasicBlock bb) { - phiHasInputFromBlock(phi, result, bb) + Definition phiHasInputFromBlock(Ssa::PhiNode phi, ControlFlow::BasicBlock bb) { + Impl::phiHasInputFromBlock(phi, result, bb) } cached AssignableRead getAReadAtNode(Definition def, ControlFlow::Node cfn) { exists(Ssa::SourceVariable v, ControlFlow::BasicBlock bb, int i | - ssaDefReachesRead(v, def, bb, i) and + Impl::ssaDefReachesRead(v, def, bb, i) and variableReadActual(bb, i, v) and cfn = bb.getNode(i) and result.getAControlFlowNode() = cfn @@ -1313,11 +1329,11 @@ private module Cached { /** Same as `lastRefRedef`, but skips uncertain reads. */ cached predicate lastRefBeforeRedef(Definition def, ControlFlow::BasicBlock bb, int i, Definition next) { - lastRefRedef(def, bb, i, next) and + Impl::lastRefRedef(def, bb, i, next) and not SsaInput::variableRead(bb, i, def.getSourceVariable(), false) or exists(SsaInput::BasicBlock bb0, int i0 | - lastRefRedef(def, bb0, i0, next) and + Impl::lastRefRedef(def, bb0, i0, next) and adjacentDefReachesUncertainRead(def, bb, i, bb0, i0) ) } @@ -1333,7 +1349,7 @@ private module Cached { cached Definition uncertainWriteDefinitionInput(UncertainWriteDefinition def) { - uncertainWriteDefinitionInput(def, result) + Impl::uncertainWriteDefinitionInput(def, result) } cached @@ -1343,10 +1359,57 @@ private module Cached { v = def.getSourceVariable() and p = v.getAssignable() and def = def0.getAnUltimateDefinition() and - ssaDefReachesRead(_, def0, bb, i) and + Impl::ssaDefReachesRead(_, def0, bb, i) and outRefExitRead(bb, i, v) ) } } import Cached + +private string getSplitString(DefinitionExt def) { + exists(ControlFlow::BasicBlock bb, int i, ControlFlow::Node cfn | + def.definesAt(_, bb, i, _) and + result = cfn.(ControlFlow::Nodes::ElementNode).getSplitsString() + | + cfn = bb.getNode(i) + or + not exists(bb.getNode(i)) and + cfn = bb.getFirstNode() + ) +} + +string getToStringPrefix(DefinitionExt def) { + result = "[" + getSplitString(def) + "] " + or + not exists(getSplitString(def)) and + result = "" +} + +/** + * An extended static single assignment (SSA) definition. + * + * This is either a normal SSA definition (`Definition`) or a + * phi-read node (`PhiReadNode`). + * + * Only intended for internal use. + */ +class DefinitionExt extends Impl::DefinitionExt { + override string toString() { result = this.(Ssa::Definition).toString() } + + /** Gets the location of this definition. */ + Location getLocation() { result = this.(Ssa::Definition).getLocation() } +} + +/** + * A phi-read node. + * + * Only intended for internal use. + */ +class PhiReadNode extends DefinitionExt, Impl::PhiReadNode { + override string toString() { + result = getToStringPrefix(this) + "SSA phi read(" + this.getSourceVariable() + ")" + } + + override Location getLocation() { result = this.getBasicBlock().getLocation() } +} diff --git a/csharp/ql/test/library-tests/dataflow/ssa/SSAPhiRead.expected b/csharp/ql/test/library-tests/dataflow/ssa/SSAPhiRead.expected index 19c394b58fd..970e6fce524 100644 --- a/csharp/ql/test/library-tests/dataflow/ssa/SSAPhiRead.expected +++ b/csharp/ql/test/library-tests/dataflow/ssa/SSAPhiRead.expected @@ -1,4 +1,30 @@ -| DefUse.cs:63:9:63:14 | this.Field2 | DefUse.cs:80:30:80:31 | access to local variable x1 | -| Fields.cs:65:24:65:32 | this.LoopField | Fields.cs:63:16:63:28 | this access | -| Properties.cs:65:24:65:31 | this.LoopProp | Properties.cs:63:16:63:16 | access to parameter i | -| Test.cs:78:13:78:13 | x | Test.cs:90:9:97:9 | if (...) ... | +phiReadNode +| DefUse.cs:80:30:80:31 | SSA phi read(this.Field2) | DefUse.cs:63:9:63:14 | this.Field2 | +| Fields.cs:63:16:63:28 | SSA phi read(this.LoopField) | Fields.cs:65:24:65:32 | this.LoopField | +| Patterns.cs:20:9:38:9 | SSA phi read(o) | Patterns.cs:7:16:7:16 | o | +| Properties.cs:63:16:63:16 | SSA phi read(this.LoopProp) | Properties.cs:65:24:65:31 | this.LoopProp | +| Test.cs:25:16:25:16 | SSA phi read(x) | Test.cs:8:13:8:13 | x | +| Test.cs:90:9:97:9 | SSA phi read(x) | Test.cs:78:13:78:13 | x | +| Test.cs:99:9:99:15 | SSA phi read(x) | Test.cs:78:13:78:13 | x | +phiReadNodeRead +| DefUse.cs:80:30:80:31 | SSA phi read(this.Field2) | DefUse.cs:63:9:63:14 | this.Field2 | DefUse.cs:80:37:80:42 | access to field Field2 | +| Fields.cs:63:16:63:28 | SSA phi read(this.LoopField) | Fields.cs:65:24:65:32 | this.LoopField | Fields.cs:65:24:65:32 | access to field LoopField | +| Patterns.cs:20:9:38:9 | SSA phi read(o) | Patterns.cs:7:16:7:16 | o | Patterns.cs:20:17:20:17 | access to local variable o | +| Properties.cs:63:16:63:16 | SSA phi read(this.LoopProp) | Properties.cs:65:24:65:31 | this.LoopProp | Properties.cs:65:24:65:31 | access to property LoopProp | +| Test.cs:25:16:25:16 | SSA phi read(x) | Test.cs:8:13:8:13 | x | Test.cs:25:16:25:16 | access to local variable x | +| Test.cs:90:9:97:9 | SSA phi read(x) | Test.cs:78:13:78:13 | x | Test.cs:92:17:92:17 | access to local variable x | +| Test.cs:90:9:97:9 | SSA phi read(x) | Test.cs:78:13:78:13 | x | Test.cs:96:17:96:17 | access to local variable x | +| Test.cs:99:9:99:15 | SSA phi read(x) | Test.cs:78:13:78:13 | x | Test.cs:99:13:99:13 | access to local variable x | +| Test.cs:99:9:99:15 | SSA phi read(x) | Test.cs:78:13:78:13 | x | Test.cs:104:17:104:17 | access to local variable x | +phiReadInput +| DefUse.cs:80:30:80:31 | SSA phi read(this.Field2) | DefUse.cs:63:9:63:18 | SSA def(this.Field2) | +| DefUse.cs:80:30:80:31 | SSA phi read(this.Field2) | DefUse.cs:80:30:80:31 | SSA phi read(this.Field2) | +| Fields.cs:63:16:63:28 | SSA phi read(this.LoopField) | Fields.cs:61:17:61:17 | SSA entry def(this.LoopField) | +| Fields.cs:63:16:63:28 | SSA phi read(this.LoopField) | Fields.cs:63:16:63:28 | SSA phi read(this.LoopField) | +| Patterns.cs:20:9:38:9 | SSA phi read(o) | Patterns.cs:7:16:7:23 | SSA def(o) | +| Properties.cs:63:16:63:16 | SSA phi read(this.LoopProp) | Properties.cs:61:17:61:17 | SSA entry def(this.LoopProp) | +| Properties.cs:63:16:63:16 | SSA phi read(this.LoopProp) | Properties.cs:63:16:63:16 | SSA phi read(this.LoopProp) | +| Test.cs:25:16:25:16 | SSA phi read(x) | Test.cs:24:9:24:15 | SSA phi(x) | +| Test.cs:25:16:25:16 | SSA phi read(x) | Test.cs:25:16:25:16 | SSA phi read(x) | +| Test.cs:90:9:97:9 | SSA phi read(x) | Test.cs:78:13:78:17 | SSA def(x) | +| Test.cs:99:9:99:15 | SSA phi read(x) | Test.cs:90:9:97:9 | SSA phi read(x) | diff --git a/csharp/ql/test/library-tests/dataflow/ssa/SSAPhiRead.ql b/csharp/ql/test/library-tests/dataflow/ssa/SSAPhiRead.ql index f9603dc1da2..8fee62217bf 100644 --- a/csharp/ql/test/library-tests/dataflow/ssa/SSAPhiRead.ql +++ b/csharp/ql/test/library-tests/dataflow/ssa/SSAPhiRead.ql @@ -1,6 +1,17 @@ import csharp import semmle.code.csharp.dataflow.internal.SsaImpl +import ExposedForTestingOnly -from Ssa::SourceVariable v, ControlFlow::BasicBlock bb -where phiReadExposedForTesting(bb, v) -select v, bb +query predicate phiReadNode(PhiReadNode phi, Ssa::SourceVariable v) { phi.getSourceVariable() = v } + +query predicate phiReadNodeRead(PhiReadNode phi, Ssa::SourceVariable v, ControlFlow::Node read) { + phi.getSourceVariable() = v and + exists(ControlFlow::BasicBlock bb, int i | + ssaDefReachesReadExt(v, phi, bb, i) and + read = bb.getNode(i) + ) +} + +query predicate phiReadInput(PhiReadNode phi, DefinitionExt inp) { + phiHasInputFromBlockExt(phi, inp, _) +} diff --git a/csharp/ql/test/library-tests/dataflow/ssa/Test.cs b/csharp/ql/test/library-tests/dataflow/ssa/Test.cs index 5d321d0117d..23fee7d4706 100644 --- a/csharp/ql/test/library-tests/dataflow/ssa/Test.cs +++ b/csharp/ql/test/library-tests/dataflow/ssa/Test.cs @@ -95,7 +95,7 @@ class Test { use(x); } - // no phi_use for `x`, because actual use exists in the block + // phi_use for `x`, even though there is an actual use in the block use(x); From 67f31ffdf06e30fcf29caff37062a45f9266ede6 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 10 Nov 2022 14:28:35 +0100 Subject: [PATCH 0271/1420] Ruby: Add tests for phi reads --- ruby/ql/consistency-queries/SsaConsistency.ql | 11 ++- .../codeql/ruby/dataflow/internal/SsaImpl.qll | 67 +++++++++++++--- .../variables/parameter.expected | 4 + .../test/library-tests/variables/ssa.expected | 77 +++++++++++++++++++ ruby/ql/test/library-tests/variables/ssa.ql | 16 ++++ ruby/ql/test/library-tests/variables/ssa.rb | 15 ++++ .../variables/varaccess.expected | 34 ++++++++ .../library-tests/variables/variable.expected | 8 +- .../variables/varscopes.expected | 3 +- 9 files changed, 220 insertions(+), 15 deletions(-) diff --git a/ruby/ql/consistency-queries/SsaConsistency.ql b/ruby/ql/consistency-queries/SsaConsistency.ql index 7ba9262baa4..30503e6aa1f 100644 --- a/ruby/ql/consistency-queries/SsaConsistency.ql +++ b/ruby/ql/consistency-queries/SsaConsistency.ql @@ -1,5 +1,6 @@ import codeql.ruby.dataflow.SSA -import codeql.ruby.dataflow.internal.SsaImpl::Consistency +import codeql.ruby.dataflow.internal.SsaImpl +import Consistency class MyRelevantDefinition extends RelevantDefinition, Ssa::Definition { override predicate hasLocationInfo( @@ -8,3 +9,11 @@ class MyRelevantDefinition extends RelevantDefinition, Ssa::Definition { this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) } } + +class MyRelevantDefinitionExt extends RelevantDefinitionExt, DefinitionExt { + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll index c7154b50d0f..2f3b1e085b0 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll @@ -2,6 +2,7 @@ private import codeql.ssa.Ssa as SsaImplCommon private import codeql.ruby.AST private import codeql.ruby.CFG as Cfg private import codeql.ruby.controlflow.internal.ControlFlowGraphImplShared as ControlFlowGraphImplShared +private import codeql.ruby.dataflow.SSA private import codeql.ruby.ast.Variable private import Cfg::CfgNodes::ExprNodes @@ -61,7 +62,23 @@ private module SsaInput implements SsaImplCommon::InputSig { } } -import SsaImplCommon::Make +private import SsaImplCommon::Make as Impl + +class Definition = Impl::Definition; + +class WriteDefinition = Impl::WriteDefinition; + +class UncertainWriteDefinition = Impl::UncertainWriteDefinition; + +class PhiNode = Impl::PhiNode; + +module Consistency = Impl::Consistency; + +module ExposedForTestingOnly { + predicate ssaDefReachesReadExt = Impl::ssaDefReachesReadExt/4; + + predicate phiHasInputFromBlockExt = Impl::phiHasInputFromBlockExt/3; +} /** Holds if `v` is uninitialized at index `i` in entry block `bb`. */ predicate uninitializedWrite(Cfg::EntryBasicBlock bb, int i, LocalVariable v) { @@ -204,7 +221,7 @@ private predicate adjacentDefRead( Definition def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2, SsaInput::SourceVariable v ) { - adjacentDefRead(def, bb1, i1, bb2, i2) and + Impl::adjacentDefRead(def, bb1, i1, bb2, i2) and v = def.getSourceVariable() } @@ -220,7 +237,7 @@ private predicate adjacentDefReachesRead( exists(SsaInput::BasicBlock bb3, int i3 | adjacentDefReachesRead(def, bb1, i1, bb3, i3) and SsaInput::variableRead(bb3, i3, _, false) and - adjacentDefRead(def, bb3, i3, bb2, i2) + Impl::adjacentDefRead(def, bb3, i3, bb2, i2) ) } @@ -243,11 +260,11 @@ private predicate adjacentDefReachesUncertainRead( /** Same as `lastRefRedef`, but skips uncertain reads. */ pragma[nomagic] private predicate lastRefSkipUncertainReads(Definition def, SsaInput::BasicBlock bb, int i) { - lastRef(def, bb, i) and + Impl::lastRef(def, bb, i) and not SsaInput::variableRead(bb, i, def.getSourceVariable(), false) or exists(SsaInput::BasicBlock bb0, int i0 | - lastRef(def, bb0, i0) and + Impl::lastRef(def, bb0, i0) and adjacentDefReachesUncertainRead(def, bb, i, bb0, i0) ) } @@ -304,7 +321,7 @@ private module Cached { cached VariableReadAccessCfgNode getARead(Definition def) { exists(LocalVariable v, Cfg::BasicBlock bb, int i | - ssaDefReachesRead(v, def, bb, i) and + Impl::ssaDefReachesRead(v, def, bb, i) and variableReadActual(bb, i, v) and result = bb.getNode(i) ) @@ -315,7 +332,7 @@ private module Cached { Definition def, CallCfgNode call, LocalVariable v, Cfg::CfgScope scope ) { exists(Cfg::BasicBlock bb, int i | - ssaDefReachesRead(v, def, bb, i) and + Impl::ssaDefReachesRead(v, def, bb, i) and capturedCallRead(call, bb, i, v) and scope.getOuterCfgScope() = bb.getScope() ) @@ -360,7 +377,7 @@ private module Cached { Definition def, LocalVariable v, Cfg::CfgScope scope ) { exists(Cfg::BasicBlock bb, int i | - ssaDefReachesRead(v, def, bb, i) and + Impl::ssaDefReachesRead(v, def, bb, i) and capturedExitRead(bb, i, v) and scope = bb.getScope().getOuterCfgScope*() ) @@ -403,7 +420,7 @@ private module Cached { cached Definition phiHasInputFromBlock(PhiNode phi, Cfg::BasicBlock bb) { - phiHasInputFromBlock(phi, result, bb) + Impl::phiHasInputFromBlock(phi, result, bb) } /** @@ -459,19 +476,45 @@ private module Cached { */ cached predicate lastRefBeforeRedef(Definition def, Cfg::BasicBlock bb, int i, Definition next) { - lastRefRedef(def, bb, i, next) and + Impl::lastRefRedef(def, bb, i, next) and not SsaInput::variableRead(bb, i, def.getSourceVariable(), false) or exists(SsaInput::BasicBlock bb0, int i0 | - lastRefRedef(def, bb0, i0, next) and + Impl::lastRefRedef(def, bb0, i0, next) and adjacentDefReachesUncertainRead(def, bb, i, bb0, i0) ) } cached Definition uncertainWriteDefinitionInput(UncertainWriteDefinition def) { - uncertainWriteDefinitionInput(def, result) + Impl::uncertainWriteDefinitionInput(def, result) } } import Cached + +/** + * An extended static single assignment (SSA) definition. + * + * This is either a normal SSA definition (`Definition`) or a + * phi-read node (`PhiReadNode`). + * + * Only intended for internal use. + */ +class DefinitionExt extends Impl::DefinitionExt { + override string toString() { result = this.(Ssa::Definition).toString() } + + /** Gets the location of this definition. */ + Location getLocation() { result = this.(Ssa::Definition).getLocation() } +} + +/** + * A phi-read node. + * + * Only intended for internal use. + */ +class PhiReadNode extends DefinitionExt, Impl::PhiReadNode { + override string toString() { result = "SSA phi read(" + this.getSourceVariable() + ")" } + + override Location getLocation() { result = this.getBasicBlock().getLocation() } +} diff --git a/ruby/ql/test/library-tests/variables/parameter.expected b/ruby/ql/test/library-tests/variables/parameter.expected index 292ae665964..6e6c8426f86 100644 --- a/ruby/ql/test/library-tests/variables/parameter.expected +++ b/ruby/ql/test/library-tests/variables/parameter.expected @@ -38,6 +38,10 @@ parameterVariable | ssa.rb:53:8:53:10 | foo | ssa.rb:53:8:53:10 | foo | | ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a | | ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a | +| ssa.rb:90:9:90:10 | b1 | ssa.rb:90:9:90:10 | b1 | +| ssa.rb:90:13:90:14 | b2 | ssa.rb:90:13:90:14 | b2 | +| ssa.rb:90:17:90:18 | b3 | ssa.rb:90:17:90:18 | b3 | +| ssa.rb:90:21:90:22 | b4 | ssa.rb:90:21:90:22 | b4 | parameterNoVariable | parameters.rb:45:22:45:22 | _ | parameterVariableNoAccess diff --git a/ruby/ql/test/library-tests/variables/ssa.expected b/ruby/ql/test/library-tests/variables/ssa.expected index e1df08d4285..20133a71857 100644 --- a/ruby/ql/test/library-tests/variables/ssa.expected +++ b/ruby/ql/test/library-tests/variables/ssa.expected @@ -164,6 +164,12 @@ definition | ssa.rb:83:7:87:5 | self | ssa.rb:81:1:88:3 | self | | ssa.rb:84:10:86:8 | captured | ssa.rb:82:3:82:10 | captured | | ssa.rb:84:10:86:8 | self | ssa.rb:81:1:88:3 | self | +| ssa.rb:90:1:103:3 | self (m12) | ssa.rb:90:1:103:3 | self | +| ssa.rb:90:9:90:10 | b1 | ssa.rb:90:9:90:10 | b1 | +| ssa.rb:90:13:90:14 | b2 | ssa.rb:90:13:90:14 | b2 | +| ssa.rb:90:17:90:18 | b3 | ssa.rb:90:17:90:18 | b3 | +| ssa.rb:90:21:90:22 | b4 | ssa.rb:90:21:90:22 | b4 | +| ssa.rb:91:3:91:7 | ... = ... | ssa.rb:91:3:91:3 | x | read | class_variables.rb:1:1:29:4 | self (class_variables.rb) | class_variables.rb:1:1:29:4 | self | class_variables.rb:3:1:3:5 | self | | class_variables.rb:5:1:7:3 | self (print) | class_variables.rb:5:1:7:3 | self | class_variables.rb:6:2:6:6 | self | @@ -347,6 +353,18 @@ read | ssa.rb:83:7:87:5 | self | ssa.rb:81:1:88:3 | self | ssa.rb:84:6:86:8 | self | | ssa.rb:84:10:86:8 | captured | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured | | ssa.rb:84:10:86:8 | self | ssa.rb:81:1:88:3 | self | ssa.rb:85:10:85:22 | self | +| ssa.rb:90:1:103:3 | self (m12) | ssa.rb:90:1:103:3 | self | ssa.rb:93:5:93:10 | self | +| ssa.rb:90:1:103:3 | self (m12) | ssa.rb:90:1:103:3 | self | ssa.rb:95:5:95:10 | self | +| ssa.rb:90:1:103:3 | self (m12) | ssa.rb:90:1:103:3 | self | ssa.rb:99:5:99:10 | self | +| ssa.rb:90:1:103:3 | self (m12) | ssa.rb:90:1:103:3 | self | ssa.rb:101:5:101:10 | self | +| ssa.rb:90:9:90:10 | b1 | ssa.rb:90:9:90:10 | b1 | ssa.rb:92:7:92:8 | b1 | +| ssa.rb:90:13:90:14 | b2 | ssa.rb:90:13:90:14 | b2 | ssa.rb:94:10:94:11 | b2 | +| ssa.rb:90:17:90:18 | b3 | ssa.rb:90:17:90:18 | b3 | ssa.rb:98:7:98:8 | b3 | +| ssa.rb:90:21:90:22 | b4 | ssa.rb:90:21:90:22 | b4 | ssa.rb:100:10:100:11 | b4 | +| ssa.rb:91:3:91:7 | ... = ... | ssa.rb:91:3:91:3 | x | ssa.rb:93:10:93:10 | x | +| ssa.rb:91:3:91:7 | ... = ... | ssa.rb:91:3:91:3 | x | ssa.rb:95:10:95:10 | x | +| ssa.rb:91:3:91:7 | ... = ... | ssa.rb:91:3:91:3 | x | ssa.rb:99:10:99:10 | x | +| ssa.rb:91:3:91:7 | ... = ... | ssa.rb:91:3:91:3 | x | ssa.rb:101:10:101:10 | x | firstRead | class_variables.rb:1:1:29:4 | self (class_variables.rb) | class_variables.rb:1:1:29:4 | self | class_variables.rb:3:1:3:5 | self | | class_variables.rb:5:1:7:3 | self (print) | class_variables.rb:5:1:7:3 | self | class_variables.rb:6:2:6:6 | self | @@ -485,6 +503,18 @@ firstRead | ssa.rb:83:7:87:5 | self | ssa.rb:81:1:88:3 | self | ssa.rb:84:6:86:8 | self | | ssa.rb:84:10:86:8 | captured | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured | | ssa.rb:84:10:86:8 | self | ssa.rb:81:1:88:3 | self | ssa.rb:85:10:85:22 | self | +| ssa.rb:90:1:103:3 | self (m12) | ssa.rb:90:1:103:3 | self | ssa.rb:93:5:93:10 | self | +| ssa.rb:90:1:103:3 | self (m12) | ssa.rb:90:1:103:3 | self | ssa.rb:95:5:95:10 | self | +| ssa.rb:90:1:103:3 | self (m12) | ssa.rb:90:1:103:3 | self | ssa.rb:99:5:99:10 | self | +| ssa.rb:90:1:103:3 | self (m12) | ssa.rb:90:1:103:3 | self | ssa.rb:101:5:101:10 | self | +| ssa.rb:90:9:90:10 | b1 | ssa.rb:90:9:90:10 | b1 | ssa.rb:92:7:92:8 | b1 | +| ssa.rb:90:13:90:14 | b2 | ssa.rb:90:13:90:14 | b2 | ssa.rb:94:10:94:11 | b2 | +| ssa.rb:90:17:90:18 | b3 | ssa.rb:90:17:90:18 | b3 | ssa.rb:98:7:98:8 | b3 | +| ssa.rb:90:21:90:22 | b4 | ssa.rb:90:21:90:22 | b4 | ssa.rb:100:10:100:11 | b4 | +| ssa.rb:91:3:91:7 | ... = ... | ssa.rb:91:3:91:3 | x | ssa.rb:93:10:93:10 | x | +| ssa.rb:91:3:91:7 | ... = ... | ssa.rb:91:3:91:3 | x | ssa.rb:95:10:95:10 | x | +| ssa.rb:91:3:91:7 | ... = ... | ssa.rb:91:3:91:3 | x | ssa.rb:99:10:99:10 | x | +| ssa.rb:91:3:91:7 | ... = ... | ssa.rb:91:3:91:3 | x | ssa.rb:101:10:101:10 | x | lastRead | class_variables.rb:1:1:29:4 | self (class_variables.rb) | class_variables.rb:1:1:29:4 | self | class_variables.rb:3:1:3:5 | self | | class_variables.rb:5:1:7:3 | self (print) | class_variables.rb:5:1:7:3 | self | class_variables.rb:6:2:6:6 | self | @@ -624,6 +654,18 @@ lastRead | ssa.rb:83:7:87:5 | self | ssa.rb:81:1:88:3 | self | ssa.rb:84:6:86:8 | self | | ssa.rb:84:10:86:8 | captured | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured | | ssa.rb:84:10:86:8 | self | ssa.rb:81:1:88:3 | self | ssa.rb:85:10:85:22 | self | +| ssa.rb:90:1:103:3 | self (m12) | ssa.rb:90:1:103:3 | self | ssa.rb:93:5:93:10 | self | +| ssa.rb:90:1:103:3 | self (m12) | ssa.rb:90:1:103:3 | self | ssa.rb:95:5:95:10 | self | +| ssa.rb:90:1:103:3 | self (m12) | ssa.rb:90:1:103:3 | self | ssa.rb:99:5:99:10 | self | +| ssa.rb:90:1:103:3 | self (m12) | ssa.rb:90:1:103:3 | self | ssa.rb:101:5:101:10 | self | +| ssa.rb:90:9:90:10 | b1 | ssa.rb:90:9:90:10 | b1 | ssa.rb:92:7:92:8 | b1 | +| ssa.rb:90:13:90:14 | b2 | ssa.rb:90:13:90:14 | b2 | ssa.rb:94:10:94:11 | b2 | +| ssa.rb:90:17:90:18 | b3 | ssa.rb:90:17:90:18 | b3 | ssa.rb:98:7:98:8 | b3 | +| ssa.rb:90:21:90:22 | b4 | ssa.rb:90:21:90:22 | b4 | ssa.rb:100:10:100:11 | b4 | +| ssa.rb:91:3:91:7 | ... = ... | ssa.rb:91:3:91:3 | x | ssa.rb:93:10:93:10 | x | +| ssa.rb:91:3:91:7 | ... = ... | ssa.rb:91:3:91:3 | x | ssa.rb:95:10:95:10 | x | +| ssa.rb:91:3:91:7 | ... = ... | ssa.rb:91:3:91:3 | x | ssa.rb:99:10:99:10 | x | +| ssa.rb:91:3:91:7 | ... = ... | ssa.rb:91:3:91:3 | x | ssa.rb:101:10:101:10 | x | adjacentReads | class_variables.rb:26:1:29:3 | self (N) | class_variables.rb:26:1:29:3 | self | class_variables.rb:27:3:27:11 | self | class_variables.rb:28:3:28:7 | self | | instance_variables.rb:1:1:44:4 | self (instance_variables.rb) | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:1:1:1:4 | self | instance_variables.rb:11:1:11:9 | self | @@ -674,6 +716,14 @@ adjacentReads | ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:39:8:39:9 | self | ssa.rb:41:3:41:13 | self | | ssa.rb:66:11:70:5 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:68:10:68:17 | captured | ssa.rb:69:5:69:12 | captured | | ssa.rb:66:11:70:5 | self | ssa.rb:64:1:72:3 | self | ssa.rb:67:5:67:10 | self | ssa.rb:68:5:68:17 | self | +| ssa.rb:90:1:103:3 | self (m12) | ssa.rb:90:1:103:3 | self | ssa.rb:93:5:93:10 | self | ssa.rb:99:5:99:10 | self | +| ssa.rb:90:1:103:3 | self (m12) | ssa.rb:90:1:103:3 | self | ssa.rb:93:5:93:10 | self | ssa.rb:101:5:101:10 | self | +| ssa.rb:90:1:103:3 | self (m12) | ssa.rb:90:1:103:3 | self | ssa.rb:95:5:95:10 | self | ssa.rb:99:5:99:10 | self | +| ssa.rb:90:1:103:3 | self (m12) | ssa.rb:90:1:103:3 | self | ssa.rb:95:5:95:10 | self | ssa.rb:101:5:101:10 | self | +| ssa.rb:91:3:91:7 | ... = ... | ssa.rb:91:3:91:3 | x | ssa.rb:93:10:93:10 | x | ssa.rb:99:10:99:10 | x | +| ssa.rb:91:3:91:7 | ... = ... | ssa.rb:91:3:91:3 | x | ssa.rb:93:10:93:10 | x | ssa.rb:101:10:101:10 | x | +| ssa.rb:91:3:91:7 | ... = ... | ssa.rb:91:3:91:3 | x | ssa.rb:95:10:95:10 | x | ssa.rb:99:10:99:10 | x | +| ssa.rb:91:3:91:7 | ... = ... | ssa.rb:91:3:91:3 | x | ssa.rb:95:10:95:10 | x | ssa.rb:101:10:101:10 | x | phi | parameters.rb:37:3:37:18 | phi | parameters.rb:35:16:35:16 | b | parameters.rb:35:1:38:3 | | | parameters.rb:37:3:37:18 | phi | parameters.rb:35:16:35:16 | b | parameters.rb:35:16:35:20 | ... = ... | @@ -689,3 +739,30 @@ phi | ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x | ssa.rb:45:3:45:7 | ... = ... | | ssa.rb:50:3:50:8 | phi | ssa.rb:49:14:49:14 | y | ssa.rb:49:1:51:3 | | | ssa.rb:50:3:50:8 | phi | ssa.rb:49:14:49:14 | y | ssa.rb:49:14:49:19 | ... = ... | +phiReadNode +| parameters.rb:26:3:26:11 | SSA phi read(name) | parameters.rb:25:15:25:18 | name | +| ssa.rb:5:3:13:5 | SSA phi read(self) | ssa.rb:1:1:16:3 | self | +| ssa.rb:19:9:19:9 | SSA phi read(self) | ssa.rb:18:1:23:3 | self | +| ssa.rb:92:3:96:5 | SSA phi read(self) | ssa.rb:90:1:103:3 | self | +| ssa.rb:92:3:96:5 | SSA phi read(x) | ssa.rb:91:3:91:3 | x | +| ssa.rb:94:3:95:10 | SSA phi read(self) | ssa.rb:90:1:103:3 | self | +| ssa.rb:94:3:95:10 | SSA phi read(x) | ssa.rb:91:3:91:3 | x | +phiReadNodeRead +| parameters.rb:26:3:26:11 | SSA phi read(name) | parameters.rb:25:15:25:18 | name | parameters.rb:26:8:26:11 | name | +| ssa.rb:5:3:13:5 | SSA phi read(self) | ssa.rb:1:1:16:3 | self | ssa.rb:15:3:15:8 | self | +| ssa.rb:19:9:19:9 | SSA phi read(self) | ssa.rb:18:1:23:3 | self | ssa.rb:20:5:20:10 | self | +| ssa.rb:92:3:96:5 | SSA phi read(self) | ssa.rb:90:1:103:3 | self | ssa.rb:99:5:99:10 | self | +| ssa.rb:92:3:96:5 | SSA phi read(self) | ssa.rb:90:1:103:3 | self | ssa.rb:101:5:101:10 | self | +| ssa.rb:92:3:96:5 | SSA phi read(x) | ssa.rb:91:3:91:3 | x | ssa.rb:99:10:99:10 | x | +| ssa.rb:92:3:96:5 | SSA phi read(x) | ssa.rb:91:3:91:3 | x | ssa.rb:101:10:101:10 | x | +phiReadInput +| parameters.rb:26:3:26:11 | SSA phi read(name) | parameters.rb:25:15:25:18 | name | +| ssa.rb:5:3:13:5 | SSA phi read(self) | ssa.rb:1:1:16:3 | self (m) | +| ssa.rb:19:9:19:9 | SSA phi read(self) | ssa.rb:18:1:23:3 | self (m1) | +| ssa.rb:19:9:19:9 | SSA phi read(self) | ssa.rb:19:9:19:9 | SSA phi read(self) | +| ssa.rb:92:3:96:5 | SSA phi read(self) | ssa.rb:90:1:103:3 | self (m12) | +| ssa.rb:92:3:96:5 | SSA phi read(self) | ssa.rb:94:3:95:10 | SSA phi read(self) | +| ssa.rb:92:3:96:5 | SSA phi read(x) | ssa.rb:91:3:91:7 | ... = ... | +| ssa.rb:92:3:96:5 | SSA phi read(x) | ssa.rb:94:3:95:10 | SSA phi read(x) | +| ssa.rb:94:3:95:10 | SSA phi read(self) | ssa.rb:90:1:103:3 | self (m12) | +| ssa.rb:94:3:95:10 | SSA phi read(x) | ssa.rb:91:3:91:7 | ... = ... | diff --git a/ruby/ql/test/library-tests/variables/ssa.ql b/ruby/ql/test/library-tests/variables/ssa.ql index 26f3d5f8c0d..b7a8378e8ff 100644 --- a/ruby/ql/test/library-tests/variables/ssa.ql +++ b/ruby/ql/test/library-tests/variables/ssa.ql @@ -1,6 +1,8 @@ import codeql.ruby.AST import codeql.ruby.CFG import codeql.ruby.dataflow.SSA +import codeql.ruby.dataflow.internal.SsaImpl +import ExposedForTestingOnly query predicate definition(Ssa::Definition def, Variable v) { def.getSourceVariable() = v } @@ -24,3 +26,17 @@ query predicate adjacentReads(Ssa::Definition def, Variable v, CfgNode read1, Cf query predicate phi(Ssa::PhiNode phi, Variable v, Ssa::Definition input) { phi.getSourceVariable() = v and input = phi.getAnInput() } + +query predicate phiReadNode(PhiReadNode phi, Variable v) { phi.getSourceVariable() = v } + +query predicate phiReadNodeRead(PhiReadNode phi, Variable v, CfgNode read) { + phi.getSourceVariable() = v and + exists(BasicBlock bb, int i | + ssaDefReachesReadExt(v, phi, bb, i) and + read = bb.getNode(i) + ) +} + +query predicate phiReadInput(PhiReadNode phi, DefinitionExt inp) { + phiHasInputFromBlockExt(phi, inp, _) +} diff --git a/ruby/ql/test/library-tests/variables/ssa.rb b/ruby/ql/test/library-tests/variables/ssa.rb index bb13dd6c4c9..c8a28e39f07 100644 --- a/ruby/ql/test/library-tests/variables/ssa.rb +++ b/ruby/ql/test/library-tests/variables/ssa.rb @@ -85,4 +85,19 @@ def m11 puts captured end end +end + +def m12(b1, b2, b3, b4) + x = 0 + if (b1) then + puts x + elsif (b2) then + puts x + end + # phi read for x + if (b3) then + puts x + elsif (b4) then + puts x + end end \ No newline at end of file diff --git a/ruby/ql/test/library-tests/variables/varaccess.expected b/ruby/ql/test/library-tests/variables/varaccess.expected index 59a5ee362f6..4443373e8c2 100644 --- a/ruby/ql/test/library-tests/variables/varaccess.expected +++ b/ruby/ql/test/library-tests/variables/varaccess.expected @@ -279,6 +279,23 @@ variableAccess | ssa.rb:84:6:86:8 | self | ssa.rb:81:1:88:3 | self | ssa.rb:81:1:88:3 | m11 | | ssa.rb:85:10:85:22 | self | ssa.rb:81:1:88:3 | self | ssa.rb:81:1:88:3 | m11 | | ssa.rb:85:15:85:22 | captured | ssa.rb:82:3:82:10 | captured | ssa.rb:81:1:88:3 | m11 | +| ssa.rb:90:9:90:10 | b1 | ssa.rb:90:9:90:10 | b1 | ssa.rb:90:1:103:3 | m12 | +| ssa.rb:90:13:90:14 | b2 | ssa.rb:90:13:90:14 | b2 | ssa.rb:90:1:103:3 | m12 | +| ssa.rb:90:17:90:18 | b3 | ssa.rb:90:17:90:18 | b3 | ssa.rb:90:1:103:3 | m12 | +| ssa.rb:90:21:90:22 | b4 | ssa.rb:90:21:90:22 | b4 | ssa.rb:90:1:103:3 | m12 | +| ssa.rb:91:3:91:3 | x | ssa.rb:91:3:91:3 | x | ssa.rb:90:1:103:3 | m12 | +| ssa.rb:92:7:92:8 | b1 | ssa.rb:90:9:90:10 | b1 | ssa.rb:90:1:103:3 | m12 | +| ssa.rb:93:5:93:10 | self | ssa.rb:90:1:103:3 | self | ssa.rb:90:1:103:3 | m12 | +| ssa.rb:93:10:93:10 | x | ssa.rb:91:3:91:3 | x | ssa.rb:90:1:103:3 | m12 | +| ssa.rb:94:10:94:11 | b2 | ssa.rb:90:13:90:14 | b2 | ssa.rb:90:1:103:3 | m12 | +| ssa.rb:95:5:95:10 | self | ssa.rb:90:1:103:3 | self | ssa.rb:90:1:103:3 | m12 | +| ssa.rb:95:10:95:10 | x | ssa.rb:91:3:91:3 | x | ssa.rb:90:1:103:3 | m12 | +| ssa.rb:98:7:98:8 | b3 | ssa.rb:90:17:90:18 | b3 | ssa.rb:90:1:103:3 | m12 | +| ssa.rb:99:5:99:10 | self | ssa.rb:90:1:103:3 | self | ssa.rb:90:1:103:3 | m12 | +| ssa.rb:99:10:99:10 | x | ssa.rb:91:3:91:3 | x | ssa.rb:90:1:103:3 | m12 | +| ssa.rb:100:10:100:11 | b4 | ssa.rb:90:21:90:22 | b4 | ssa.rb:90:1:103:3 | m12 | +| ssa.rb:101:5:101:10 | self | ssa.rb:90:1:103:3 | self | ssa.rb:90:1:103:3 | m12 | +| ssa.rb:101:10:101:10 | x | ssa.rb:91:3:91:3 | x | ssa.rb:90:1:103:3 | m12 | explicitWrite | class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:1:8 | ... = ... | | class_variables.rb:19:3:19:5 | @@x | class_variables.rb:19:3:19:10 | ... = ... | @@ -349,6 +366,7 @@ explicitWrite | ssa.rb:69:5:69:12 | captured | ssa.rb:69:5:69:17 | ... = ... | | ssa.rb:75:3:75:10 | captured | ssa.rb:75:3:75:14 | ... = ... | | ssa.rb:82:3:82:10 | captured | ssa.rb:82:3:82:14 | ... = ... | +| ssa.rb:91:3:91:3 | x | ssa.rb:91:3:91:7 | ... = ... | implicitWrite | nested_scopes.rb:15:23:15:23 | a | | nested_scopes.rb:16:26:16:26 | x | @@ -390,6 +408,10 @@ implicitWrite | ssa.rb:53:8:53:10 | foo | | ssa.rb:64:8:64:8 | a | | ssa.rb:66:15:66:15 | a | +| ssa.rb:90:9:90:10 | b1 | +| ssa.rb:90:13:90:14 | b2 | +| ssa.rb:90:17:90:18 | b3 | +| ssa.rb:90:21:90:22 | b4 | readAccess | class_variables.rb:3:1:3:5 | self | | class_variables.rb:3:3:3:5 | @@x | @@ -582,3 +604,15 @@ readAccess | ssa.rb:84:6:86:8 | self | | ssa.rb:85:10:85:22 | self | | ssa.rb:85:15:85:22 | captured | +| ssa.rb:92:7:92:8 | b1 | +| ssa.rb:93:5:93:10 | self | +| ssa.rb:93:10:93:10 | x | +| ssa.rb:94:10:94:11 | b2 | +| ssa.rb:95:5:95:10 | self | +| ssa.rb:95:10:95:10 | x | +| ssa.rb:98:7:98:8 | b3 | +| ssa.rb:99:5:99:10 | self | +| ssa.rb:99:10:99:10 | x | +| ssa.rb:100:10:100:11 | b4 | +| ssa.rb:101:5:101:10 | self | +| ssa.rb:101:10:101:10 | x | diff --git a/ruby/ql/test/library-tests/variables/variable.expected b/ruby/ql/test/library-tests/variables/variable.expected index 541ed394653..ccab8a3cdf4 100644 --- a/ruby/ql/test/library-tests/variables/variable.expected +++ b/ruby/ql/test/library-tests/variables/variable.expected @@ -120,7 +120,7 @@ | scopes.rb:43:2:43:4 | foo | | scopes.rb:46:5:46:8 | var2 | | ssa.rb:1:1:16:3 | self | -| ssa.rb:1:1:88:3 | self | +| ssa.rb:1:1:103:3 | self | | ssa.rb:1:7:1:7 | b | | ssa.rb:2:3:2:3 | i | | ssa.rb:18:1:23:3 | self | @@ -152,3 +152,9 @@ | ssa.rb:75:3:75:10 | captured | | ssa.rb:81:1:88:3 | self | | ssa.rb:82:3:82:10 | captured | +| ssa.rb:90:1:103:3 | self | +| ssa.rb:90:9:90:10 | b1 | +| ssa.rb:90:13:90:14 | b2 | +| ssa.rb:90:17:90:18 | b3 | +| ssa.rb:90:21:90:22 | b4 | +| ssa.rb:91:3:91:3 | x | diff --git a/ruby/ql/test/library-tests/variables/varscopes.expected b/ruby/ql/test/library-tests/variables/varscopes.expected index 1859ab01550..6b64ca6f97d 100644 --- a/ruby/ql/test/library-tests/variables/varscopes.expected +++ b/ruby/ql/test/library-tests/variables/varscopes.expected @@ -57,7 +57,7 @@ | scopes.rb:37:1:39:3 | foo | | scopes.rb:41:1:49:3 | M | | ssa.rb:1:1:16:3 | m | -| ssa.rb:1:1:88:3 | ssa.rb | +| ssa.rb:1:1:103:3 | ssa.rb | | ssa.rb:18:1:23:3 | m1 | | ssa.rb:25:1:30:3 | m2 | | ssa.rb:26:3:28:5 | { ... } | @@ -75,3 +75,4 @@ | ssa.rb:81:1:88:3 | m11 | | ssa.rb:83:7:87:5 | do ... end | | ssa.rb:84:10:86:8 | do ... end | +| ssa.rb:90:1:103:3 | m12 | From dff7b475fbcd1eef192382c437eeb8ce43b7b118 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Tue, 15 Nov 2022 11:46:44 +0100 Subject: [PATCH 0272/1420] make the top-level comment in SuperlinearBackTracking.qll a QLDoc --- shared/regex/codeql/regex/nfa/SuperlinearBackTracking.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/regex/codeql/regex/nfa/SuperlinearBackTracking.qll b/shared/regex/codeql/regex/nfa/SuperlinearBackTracking.qll index 00f53784554..efe5beb3b4a 100644 --- a/shared/regex/codeql/regex/nfa/SuperlinearBackTracking.qll +++ b/shared/regex/codeql/regex/nfa/SuperlinearBackTracking.qll @@ -1,4 +1,4 @@ -/* +/** * This module implements the analysis described in the paper: * Valentin Wustholz, Oswaldo Olivo, Marijn J. H. Heule, and Isil Dillig: * Static Detection of DoS Vulnerabilities in From 8d9b106be1c437aff320246213de46a0be95c508 Mon Sep 17 00:00:00 2001 From: Joe Farebrother Date: Tue, 15 Nov 2022 11:19:40 +0000 Subject: [PATCH 0273/1420] Exclude invalid identifiers from generated stubs --- java/ql/src/utils/stub-generator/Stubs.qll | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/java/ql/src/utils/stub-generator/Stubs.qll b/java/ql/src/utils/stub-generator/Stubs.qll index 5c04dee0090..91e7835f4ba 100644 --- a/java/ql/src/utils/stub-generator/Stubs.qll +++ b/java/ql/src/utils/stub-generator/Stubs.qll @@ -7,12 +7,17 @@ import java +/** Holds if `id` is a valid Java identifier. */ +bindingset[id] +private predicate isValidIdentifier(string id) { id.regexpMatch("[\\w_$]+") } + /** A type that should be in the generated code. */ abstract private class GeneratedType extends ClassOrInterface { GeneratedType() { not this instanceof AnonymousClass and not this.isLocal() and - not this.getPackage() instanceof ExcludedPackage + not this.getPackage() instanceof ExcludedPackage and + isValidIdentifier(this.getName()) } private string stubKeyword() { @@ -108,7 +113,8 @@ abstract private class GeneratedType extends ClassOrInterface { not result.isPrivate() and not result.isPackageProtected() and not result instanceof StaticInitializer and - not result instanceof InstanceInitializer + not result instanceof InstanceInitializer and + isValidIdentifier(result.getName()) } final Type getAGeneratedType() { From 9b38e1102a7f2e2743c7b8cafe9a661ea18e8cdf Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 14 Nov 2022 17:31:29 +0000 Subject: [PATCH 0274/1420] Swift: Add more tests of optionals. --- .../dataflow/dataflow/DataFlow.expected | 123 +++++++++-------- .../dataflow/dataflow/LocalFlow.expected | 130 +++++++++++------- .../dataflow/dataflow/test.swift | 49 +++++-- 3 files changed, 182 insertions(+), 120 deletions(-) diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected index c4e0a7a01e6..0f55b0155c3 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected @@ -100,29 +100,30 @@ edges | test.swift:225:14:225:21 | call to source() : | test.swift:235:13:235:15 | .source_value | | test.swift:225:14:225:21 | call to source() : | test.swift:238:13:238:15 | .source_value | | test.swift:259:12:259:19 | call to source() : | test.swift:263:13:263:28 | call to optionalSource() : | -| test.swift:263:13:263:28 | call to optionalSource() : | test.swift:264:15:264:16 | ...! | -| test.swift:263:13:263:28 | call to optionalSource() : | test.swift:266:15:266:16 | ...? : | -| test.swift:265:15:265:22 | call to source() : | file://:0:0:0:0 | [summary param] this in signum() : | -| test.swift:265:15:265:22 | call to source() : | test.swift:265:15:265:31 | call to signum() | -| test.swift:266:15:266:16 | ...? : | file://:0:0:0:0 | [summary param] this in signum() : | -| test.swift:266:15:266:16 | ...? : | test.swift:266:15:266:25 | call to signum() : | -| test.swift:266:15:266:25 | call to signum() : | test.swift:266:15:266:25 | OptionalEvaluationExpr | -| test.swift:277:14:277:26 | (...) [Tuple element at index 1] : | test.swift:281:15:281:15 | t1 [Tuple element at index 1] : | -| test.swift:277:18:277:25 | call to source() : | test.swift:277:14:277:26 | (...) [Tuple element at index 1] : | -| test.swift:281:15:281:15 | t1 [Tuple element at index 1] : | test.swift:281:15:281:18 | .1 | -| test.swift:289:5:289:5 | [post] t1 [Tuple element at index 0] : | test.swift:292:15:292:15 | t1 [Tuple element at index 0] : | -| test.swift:289:12:289:19 | call to source() : | test.swift:289:5:289:5 | [post] t1 [Tuple element at index 0] : | -| test.swift:292:15:292:15 | t1 [Tuple element at index 0] : | test.swift:292:15:292:18 | .0 | -| test.swift:297:14:297:45 | (...) [Tuple element at index 0] : | test.swift:302:15:302:15 | t1 [Tuple element at index 0] : | -| test.swift:297:14:297:45 | (...) [Tuple element at index 0] : | test.swift:306:15:306:15 | t2 [Tuple element at index 0] : | -| test.swift:297:14:297:45 | (...) [Tuple element at index 1] : | test.swift:303:15:303:15 | t1 [Tuple element at index 1] : | -| test.swift:297:14:297:45 | (...) [Tuple element at index 1] : | test.swift:307:15:307:15 | t2 [Tuple element at index 1] : | -| test.swift:297:18:297:25 | call to source() : | test.swift:297:14:297:45 | (...) [Tuple element at index 0] : | -| test.swift:297:31:297:38 | call to source() : | test.swift:297:14:297:45 | (...) [Tuple element at index 1] : | -| test.swift:302:15:302:15 | t1 [Tuple element at index 0] : | test.swift:302:15:302:18 | .0 | -| test.swift:303:15:303:15 | t1 [Tuple element at index 1] : | test.swift:303:15:303:18 | .1 | -| test.swift:306:15:306:15 | t2 [Tuple element at index 0] : | test.swift:306:15:306:18 | .0 | -| test.swift:307:15:307:15 | t2 [Tuple element at index 1] : | test.swift:307:15:307:18 | .1 | +| test.swift:263:13:263:28 | call to optionalSource() : | test.swift:265:15:265:15 | x | +| test.swift:263:13:263:28 | call to optionalSource() : | test.swift:267:15:267:16 | ...! | +| test.swift:263:13:263:28 | call to optionalSource() : | test.swift:271:15:271:16 | ...? : | +| test.swift:270:15:270:22 | call to source() : | file://:0:0:0:0 | [summary param] this in signum() : | +| test.swift:270:15:270:22 | call to source() : | test.swift:270:15:270:31 | call to signum() | +| test.swift:271:15:271:16 | ...? : | file://:0:0:0:0 | [summary param] this in signum() : | +| test.swift:271:15:271:16 | ...? : | test.swift:271:15:271:25 | call to signum() : | +| test.swift:271:15:271:25 | call to signum() : | test.swift:271:15:271:25 | OptionalEvaluationExpr | +| test.swift:302:14:302:26 | (...) [Tuple element at index 1] : | test.swift:306:15:306:15 | t1 [Tuple element at index 1] : | +| test.swift:302:18:302:25 | call to source() : | test.swift:302:14:302:26 | (...) [Tuple element at index 1] : | +| test.swift:306:15:306:15 | t1 [Tuple element at index 1] : | test.swift:306:15:306:18 | .1 | +| test.swift:314:5:314:5 | [post] t1 [Tuple element at index 0] : | test.swift:317:15:317:15 | t1 [Tuple element at index 0] : | +| test.swift:314:12:314:19 | call to source() : | test.swift:314:5:314:5 | [post] t1 [Tuple element at index 0] : | +| test.swift:317:15:317:15 | t1 [Tuple element at index 0] : | test.swift:317:15:317:18 | .0 | +| test.swift:322:14:322:45 | (...) [Tuple element at index 0] : | test.swift:327:15:327:15 | t1 [Tuple element at index 0] : | +| test.swift:322:14:322:45 | (...) [Tuple element at index 0] : | test.swift:331:15:331:15 | t2 [Tuple element at index 0] : | +| test.swift:322:14:322:45 | (...) [Tuple element at index 1] : | test.swift:328:15:328:15 | t1 [Tuple element at index 1] : | +| test.swift:322:14:322:45 | (...) [Tuple element at index 1] : | test.swift:332:15:332:15 | t2 [Tuple element at index 1] : | +| test.swift:322:18:322:25 | call to source() : | test.swift:322:14:322:45 | (...) [Tuple element at index 0] : | +| test.swift:322:31:322:38 | call to source() : | test.swift:322:14:322:45 | (...) [Tuple element at index 1] : | +| test.swift:327:15:327:15 | t1 [Tuple element at index 0] : | test.swift:327:15:327:18 | .0 | +| test.swift:328:15:328:15 | t1 [Tuple element at index 1] : | test.swift:328:15:328:18 | .1 | +| test.swift:331:15:331:15 | t2 [Tuple element at index 0] : | test.swift:331:15:331:18 | .0 | +| test.swift:332:15:332:15 | t2 [Tuple element at index 1] : | test.swift:332:15:332:18 | .1 | nodes | file://:0:0:0:0 | .a [x] : | semmle.label | .a [x] : | | file://:0:0:0:0 | .x : | semmle.label | .x : | @@ -237,32 +238,33 @@ nodes | test.swift:238:13:238:15 | .source_value | semmle.label | .source_value | | test.swift:259:12:259:19 | call to source() : | semmle.label | call to source() : | | test.swift:263:13:263:28 | call to optionalSource() : | semmle.label | call to optionalSource() : | -| test.swift:264:15:264:16 | ...! | semmle.label | ...! | -| test.swift:265:15:265:22 | call to source() : | semmle.label | call to source() : | -| test.swift:265:15:265:31 | call to signum() | semmle.label | call to signum() | -| test.swift:266:15:266:16 | ...? : | semmle.label | ...? : | -| test.swift:266:15:266:25 | OptionalEvaluationExpr | semmle.label | OptionalEvaluationExpr | -| test.swift:266:15:266:25 | call to signum() : | semmle.label | call to signum() : | -| test.swift:277:14:277:26 | (...) [Tuple element at index 1] : | semmle.label | (...) [Tuple element at index 1] : | -| test.swift:277:18:277:25 | call to source() : | semmle.label | call to source() : | -| test.swift:281:15:281:15 | t1 [Tuple element at index 1] : | semmle.label | t1 [Tuple element at index 1] : | -| test.swift:281:15:281:18 | .1 | semmle.label | .1 | -| test.swift:289:5:289:5 | [post] t1 [Tuple element at index 0] : | semmle.label | [post] t1 [Tuple element at index 0] : | -| test.swift:289:12:289:19 | call to source() : | semmle.label | call to source() : | -| test.swift:292:15:292:15 | t1 [Tuple element at index 0] : | semmle.label | t1 [Tuple element at index 0] : | -| test.swift:292:15:292:18 | .0 | semmle.label | .0 | -| test.swift:297:14:297:45 | (...) [Tuple element at index 0] : | semmle.label | (...) [Tuple element at index 0] : | -| test.swift:297:14:297:45 | (...) [Tuple element at index 1] : | semmle.label | (...) [Tuple element at index 1] : | -| test.swift:297:18:297:25 | call to source() : | semmle.label | call to source() : | -| test.swift:297:31:297:38 | call to source() : | semmle.label | call to source() : | -| test.swift:302:15:302:15 | t1 [Tuple element at index 0] : | semmle.label | t1 [Tuple element at index 0] : | -| test.swift:302:15:302:18 | .0 | semmle.label | .0 | -| test.swift:303:15:303:15 | t1 [Tuple element at index 1] : | semmle.label | t1 [Tuple element at index 1] : | -| test.swift:303:15:303:18 | .1 | semmle.label | .1 | -| test.swift:306:15:306:15 | t2 [Tuple element at index 0] : | semmle.label | t2 [Tuple element at index 0] : | -| test.swift:306:15:306:18 | .0 | semmle.label | .0 | -| test.swift:307:15:307:15 | t2 [Tuple element at index 1] : | semmle.label | t2 [Tuple element at index 1] : | -| test.swift:307:15:307:18 | .1 | semmle.label | .1 | +| test.swift:265:15:265:15 | x | semmle.label | x | +| test.swift:267:15:267:16 | ...! | semmle.label | ...! | +| test.swift:270:15:270:22 | call to source() : | semmle.label | call to source() : | +| test.swift:270:15:270:31 | call to signum() | semmle.label | call to signum() | +| test.swift:271:15:271:16 | ...? : | semmle.label | ...? : | +| test.swift:271:15:271:25 | OptionalEvaluationExpr | semmle.label | OptionalEvaluationExpr | +| test.swift:271:15:271:25 | call to signum() : | semmle.label | call to signum() : | +| test.swift:302:14:302:26 | (...) [Tuple element at index 1] : | semmle.label | (...) [Tuple element at index 1] : | +| test.swift:302:18:302:25 | call to source() : | semmle.label | call to source() : | +| test.swift:306:15:306:15 | t1 [Tuple element at index 1] : | semmle.label | t1 [Tuple element at index 1] : | +| test.swift:306:15:306:18 | .1 | semmle.label | .1 | +| test.swift:314:5:314:5 | [post] t1 [Tuple element at index 0] : | semmle.label | [post] t1 [Tuple element at index 0] : | +| test.swift:314:12:314:19 | call to source() : | semmle.label | call to source() : | +| test.swift:317:15:317:15 | t1 [Tuple element at index 0] : | semmle.label | t1 [Tuple element at index 0] : | +| test.swift:317:15:317:18 | .0 | semmle.label | .0 | +| test.swift:322:14:322:45 | (...) [Tuple element at index 0] : | semmle.label | (...) [Tuple element at index 0] : | +| test.swift:322:14:322:45 | (...) [Tuple element at index 1] : | semmle.label | (...) [Tuple element at index 1] : | +| test.swift:322:18:322:25 | call to source() : | semmle.label | call to source() : | +| test.swift:322:31:322:38 | call to source() : | semmle.label | call to source() : | +| test.swift:327:15:327:15 | t1 [Tuple element at index 0] : | semmle.label | t1 [Tuple element at index 0] : | +| test.swift:327:15:327:18 | .0 | semmle.label | .0 | +| test.swift:328:15:328:15 | t1 [Tuple element at index 1] : | semmle.label | t1 [Tuple element at index 1] : | +| test.swift:328:15:328:18 | .1 | semmle.label | .1 | +| test.swift:331:15:331:15 | t2 [Tuple element at index 0] : | semmle.label | t2 [Tuple element at index 0] : | +| test.swift:331:15:331:18 | .0 | semmle.label | .0 | +| test.swift:332:15:332:15 | t2 [Tuple element at index 1] : | semmle.label | t2 [Tuple element at index 1] : | +| test.swift:332:15:332:18 | .1 | semmle.label | .1 | subpaths | test.swift:75:21:75:22 | &... : | test.swift:65:16:65:28 | arg1 : | test.swift:65:1:70:1 | arg2[return] : | test.swift:75:31:75:32 | [post] &... : | | test.swift:114:19:114:19 | arg : | test.swift:109:9:109:14 | arg : | test.swift:110:12:110:12 | arg : | test.swift:114:12:114:22 | call to ... : | @@ -289,8 +291,8 @@ subpaths | test.swift:218:11:218:18 | call to source() : | test.swift:169:12:169:22 | value : | test.swift:170:5:170:5 | [post] self [x] : | test.swift:218:3:218:5 | [post] getter for .a [x] : | | test.swift:219:13:219:13 | b [a, x] : | test.swift:185:7:185:7 | self [a, x] : | file://:0:0:0:0 | .a [x] : | test.swift:219:13:219:15 | .a [x] : | | test.swift:219:13:219:15 | .a [x] : | test.swift:163:7:163:7 | self [x] : | file://:0:0:0:0 | .x : | test.swift:219:13:219:17 | .x | -| test.swift:265:15:265:22 | call to source() : | file://:0:0:0:0 | [summary param] this in signum() : | file://:0:0:0:0 | [summary] to write: return (return) in signum() : | test.swift:265:15:265:31 | call to signum() | -| test.swift:266:15:266:16 | ...? : | file://:0:0:0:0 | [summary param] this in signum() : | file://:0:0:0:0 | [summary] to write: return (return) in signum() : | test.swift:266:15:266:25 | call to signum() : | +| test.swift:270:15:270:22 | call to source() : | file://:0:0:0:0 | [summary param] this in signum() : | file://:0:0:0:0 | [summary] to write: return (return) in signum() : | test.swift:270:15:270:31 | call to signum() | +| test.swift:271:15:271:16 | ...? : | file://:0:0:0:0 | [summary param] this in signum() : | file://:0:0:0:0 | [summary] to write: return (return) in signum() : | test.swift:271:15:271:25 | call to signum() : | #select | test.swift:7:15:7:15 | t1 | test.swift:6:19:6:26 | call to source() : | test.swift:7:15:7:15 | t1 | result | | test.swift:9:15:9:15 | t1 | test.swift:6:19:6:26 | call to source() : | test.swift:9:15:9:15 | t1 | result | @@ -320,12 +322,13 @@ subpaths | test.swift:219:13:219:17 | .x | test.swift:218:11:218:18 | call to source() : | test.swift:219:13:219:17 | .x | result | | test.swift:235:13:235:15 | .source_value | test.swift:225:14:225:21 | call to source() : | test.swift:235:13:235:15 | .source_value | result | | test.swift:238:13:238:15 | .source_value | test.swift:225:14:225:21 | call to source() : | test.swift:238:13:238:15 | .source_value | result | -| test.swift:264:15:264:16 | ...! | test.swift:259:12:259:19 | call to source() : | test.swift:264:15:264:16 | ...! | result | -| test.swift:265:15:265:31 | call to signum() | test.swift:265:15:265:22 | call to source() : | test.swift:265:15:265:31 | call to signum() | result | -| test.swift:266:15:266:25 | OptionalEvaluationExpr | test.swift:259:12:259:19 | call to source() : | test.swift:266:15:266:25 | OptionalEvaluationExpr | result | -| test.swift:281:15:281:18 | .1 | test.swift:277:18:277:25 | call to source() : | test.swift:281:15:281:18 | .1 | result | -| test.swift:292:15:292:18 | .0 | test.swift:289:12:289:19 | call to source() : | test.swift:292:15:292:18 | .0 | result | -| test.swift:302:15:302:18 | .0 | test.swift:297:18:297:25 | call to source() : | test.swift:302:15:302:18 | .0 | result | -| test.swift:303:15:303:18 | .1 | test.swift:297:31:297:38 | call to source() : | test.swift:303:15:303:18 | .1 | result | -| test.swift:306:15:306:18 | .0 | test.swift:297:18:297:25 | call to source() : | test.swift:306:15:306:18 | .0 | result | -| test.swift:307:15:307:18 | .1 | test.swift:297:31:297:38 | call to source() : | test.swift:307:15:307:18 | .1 | result | +| test.swift:265:15:265:15 | x | test.swift:259:12:259:19 | call to source() : | test.swift:265:15:265:15 | x | result | +| test.swift:267:15:267:16 | ...! | test.swift:259:12:259:19 | call to source() : | test.swift:267:15:267:16 | ...! | result | +| test.swift:270:15:270:31 | call to signum() | test.swift:270:15:270:22 | call to source() : | test.swift:270:15:270:31 | call to signum() | result | +| test.swift:271:15:271:25 | OptionalEvaluationExpr | test.swift:259:12:259:19 | call to source() : | test.swift:271:15:271:25 | OptionalEvaluationExpr | result | +| test.swift:306:15:306:18 | .1 | test.swift:302:18:302:25 | call to source() : | test.swift:306:15:306:18 | .1 | result | +| test.swift:317:15:317:18 | .0 | test.swift:314:12:314:19 | call to source() : | test.swift:317:15:317:18 | .0 | result | +| test.swift:327:15:327:18 | .0 | test.swift:322:18:322:25 | call to source() : | test.swift:327:15:327:18 | .0 | result | +| test.swift:328:15:328:18 | .1 | test.swift:322:31:322:38 | call to source() : | test.swift:328:15:328:18 | .1 | result | +| test.swift:331:15:331:18 | .0 | test.swift:322:18:322:25 | call to source() : | test.swift:331:15:331:18 | .0 | result | +| test.swift:332:15:332:18 | .1 | test.swift:322:31:322:38 | call to source() : | test.swift:332:15:332:18 | .1 | result | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected index 987d7073b87..91709720aa2 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected @@ -184,52 +184,86 @@ | test.swift:247:9:247:9 | [post] self | test.swift:246:5:248:5 | self[return] | | test.swift:247:9:247:9 | self | test.swift:246:5:248:5 | self[return] | | test.swift:252:23:252:23 | value | test.swift:252:23:252:23 | WriteDef | -| test.swift:263:9:263:9 | WriteDef | test.swift:264:15:264:15 | x | +| test.swift:262:21:262:27 | WriteDef | test.swift:266:15:266:15 | y | +| test.swift:262:21:262:27 | y | test.swift:262:21:262:27 | WriteDef | +| test.swift:263:9:263:9 | WriteDef | test.swift:265:15:265:15 | x | | test.swift:263:13:263:28 | call to optionalSource() | test.swift:263:9:263:9 | WriteDef | -| test.swift:264:15:264:15 | x | test.swift:264:15:264:16 | ...! | -| test.swift:264:15:264:15 | x | test.swift:266:15:266:15 | x | -| test.swift:266:15:266:15 | x | test.swift:266:15:266:16 | ...? | -| test.swift:266:15:266:15 | x | test.swift:267:15:267:15 | x | -| test.swift:266:15:266:25 | call to signum() | test.swift:266:15:266:25 | OptionalEvaluationExpr | -| test.swift:267:15:267:15 | x | test.swift:268:16:268:16 | x | -| test.swift:277:9:277:9 | WriteDef | test.swift:279:15:279:15 | t1 | -| test.swift:277:14:277:26 | (...) | test.swift:277:9:277:9 | WriteDef | -| test.swift:279:15:279:15 | t1 | test.swift:280:15:280:15 | t1 | -| test.swift:280:15:280:15 | [post] t1 | test.swift:281:15:281:15 | t1 | -| test.swift:280:15:280:15 | t1 | test.swift:281:15:281:15 | t1 | -| test.swift:281:15:281:15 | [post] t1 | test.swift:283:5:283:5 | t1 | -| test.swift:281:15:281:15 | t1 | test.swift:283:5:283:5 | t1 | -| test.swift:283:5:283:5 | [post] t1 | test.swift:285:15:285:15 | t1 | -| test.swift:283:5:283:5 | t1 | test.swift:285:15:285:15 | t1 | -| test.swift:285:15:285:15 | t1 | test.swift:286:15:286:15 | t1 | -| test.swift:286:15:286:15 | [post] t1 | test.swift:287:15:287:15 | t1 | -| test.swift:286:15:286:15 | t1 | test.swift:287:15:287:15 | t1 | -| test.swift:287:15:287:15 | [post] t1 | test.swift:289:5:289:5 | t1 | -| test.swift:287:15:287:15 | t1 | test.swift:289:5:289:5 | t1 | -| test.swift:289:5:289:5 | [post] t1 | test.swift:291:15:291:15 | t1 | -| test.swift:289:5:289:5 | t1 | test.swift:291:15:291:15 | t1 | -| test.swift:291:15:291:15 | t1 | test.swift:292:15:292:15 | t1 | -| test.swift:292:15:292:15 | [post] t1 | test.swift:293:15:293:15 | t1 | -| test.swift:292:15:292:15 | t1 | test.swift:293:15:293:15 | t1 | -| test.swift:297:9:297:9 | WriteDef | test.swift:298:14:298:14 | t1 | -| test.swift:297:14:297:45 | (...) | test.swift:297:9:297:9 | WriteDef | -| test.swift:298:9:298:9 | WriteDef | test.swift:305:15:305:15 | t2 | -| test.swift:298:14:298:14 | t1 | test.swift:298:9:298:9 | WriteDef | -| test.swift:298:14:298:14 | t1 | test.swift:299:21:299:21 | t1 | -| test.swift:299:9:299:17 | WriteDef | test.swift:309:15:309:15 | a | -| test.swift:299:9:299:17 | WriteDef | test.swift:310:15:310:15 | b | -| test.swift:299:9:299:17 | WriteDef | test.swift:311:15:311:15 | c | -| test.swift:299:21:299:21 | t1 | test.swift:299:9:299:17 | WriteDef | -| test.swift:299:21:299:21 | t1 | test.swift:299:9:299:17 | WriteDef | -| test.swift:299:21:299:21 | t1 | test.swift:299:9:299:17 | WriteDef | -| test.swift:299:21:299:21 | t1 | test.swift:301:15:301:15 | t1 | -| test.swift:301:15:301:15 | t1 | test.swift:302:15:302:15 | t1 | -| test.swift:302:15:302:15 | [post] t1 | test.swift:303:15:303:15 | t1 | -| test.swift:302:15:302:15 | t1 | test.swift:303:15:303:15 | t1 | -| test.swift:303:15:303:15 | [post] t1 | test.swift:304:15:304:15 | t1 | -| test.swift:303:15:303:15 | t1 | test.swift:304:15:304:15 | t1 | -| test.swift:305:15:305:15 | t2 | test.swift:306:15:306:15 | t2 | -| test.swift:306:15:306:15 | [post] t2 | test.swift:307:15:307:15 | t2 | -| test.swift:306:15:306:15 | t2 | test.swift:307:15:307:15 | t2 | -| test.swift:307:15:307:15 | [post] t2 | test.swift:308:15:308:15 | t2 | -| test.swift:307:15:307:15 | t2 | test.swift:308:15:308:15 | t2 | +| test.swift:265:15:265:15 | x | test.swift:267:15:267:15 | x | +| test.swift:266:15:266:15 | y | test.swift:268:15:268:15 | y | +| test.swift:267:15:267:15 | x | test.swift:267:15:267:16 | ...! | +| test.swift:267:15:267:15 | x | test.swift:271:15:271:15 | x | +| test.swift:268:15:268:15 | y | test.swift:268:15:268:16 | ...! | +| test.swift:268:15:268:15 | y | test.swift:272:15:272:15 | y | +| test.swift:271:15:271:15 | x | test.swift:271:15:271:16 | ...? | +| test.swift:271:15:271:15 | x | test.swift:274:15:274:15 | x | +| test.swift:271:15:271:25 | call to signum() | test.swift:271:15:271:25 | OptionalEvaluationExpr | +| test.swift:272:15:272:15 | y | test.swift:272:15:272:16 | ...? | +| test.swift:272:15:272:15 | y | test.swift:276:15:276:15 | y | +| test.swift:272:15:272:25 | call to signum() | test.swift:272:15:272:25 | OptionalEvaluationExpr | +| test.swift:274:15:274:15 | x | test.swift:275:15:275:15 | x | +| test.swift:275:15:275:15 | x | test.swift:279:15:279:15 | x | +| test.swift:276:15:276:15 | y | test.swift:277:15:277:15 | y | +| test.swift:277:15:277:15 | y | test.swift:281:15:281:15 | y | +| test.swift:279:15:279:15 | x | test.swift:279:26:279:26 | x | +| test.swift:279:15:279:15 | x | test.swift:280:15:280:15 | x | +| test.swift:279:26:279:26 | x | test.swift:279:26:279:27 | ...! | +| test.swift:279:26:279:26 | x | test.swift:280:15:280:15 | x | +| test.swift:280:15:280:15 | x | test.swift:280:26:280:26 | x | +| test.swift:280:15:280:15 | x | test.swift:284:16:284:16 | x | +| test.swift:280:26:280:26 | x | test.swift:280:26:280:27 | ...! | +| test.swift:280:26:280:26 | x | test.swift:284:16:284:16 | x | +| test.swift:281:15:281:15 | y | test.swift:281:26:281:26 | y | +| test.swift:281:15:281:15 | y | test.swift:282:15:282:15 | y | +| test.swift:281:26:281:26 | y | test.swift:281:26:281:27 | ...! | +| test.swift:281:26:281:26 | y | test.swift:282:15:282:15 | y | +| test.swift:282:15:282:15 | y | test.swift:282:26:282:26 | y | +| test.swift:282:15:282:15 | y | test.swift:287:16:287:16 | y | +| test.swift:282:26:282:26 | y | test.swift:282:26:282:27 | ...! | +| test.swift:282:26:282:26 | y | test.swift:287:16:287:16 | y | +| test.swift:284:16:284:16 | x | test.swift:290:16:290:16 | x | +| test.swift:287:16:287:16 | y | test.swift:293:16:293:16 | y | +| test.swift:290:16:290:16 | x | test.swift:290:16:290:17 | ...? | +| test.swift:290:16:290:26 | call to signum() | test.swift:290:16:290:26 | OptionalEvaluationExpr | +| test.swift:293:16:293:16 | y | test.swift:293:16:293:17 | ...? | +| test.swift:293:16:293:26 | call to signum() | test.swift:293:16:293:26 | OptionalEvaluationExpr | +| test.swift:304:9:304:9 | WriteDef | test.swift:306:15:306:15 | t1 | +| test.swift:304:14:304:26 | (...) | test.swift:304:9:304:9 | WriteDef | +| test.swift:306:15:306:15 | t1 | test.swift:307:15:307:15 | t1 | +| test.swift:307:15:307:15 | [post] t1 | test.swift:308:15:308:15 | t1 | +| test.swift:307:15:307:15 | t1 | test.swift:308:15:308:15 | t1 | +| test.swift:308:15:308:15 | [post] t1 | test.swift:310:5:310:5 | t1 | +| test.swift:308:15:308:15 | t1 | test.swift:310:5:310:5 | t1 | +| test.swift:310:5:310:5 | [post] t1 | test.swift:312:15:312:15 | t1 | +| test.swift:310:5:310:5 | t1 | test.swift:312:15:312:15 | t1 | +| test.swift:312:15:312:15 | t1 | test.swift:313:15:313:15 | t1 | +| test.swift:313:15:313:15 | [post] t1 | test.swift:314:15:314:15 | t1 | +| test.swift:313:15:313:15 | t1 | test.swift:314:15:314:15 | t1 | +| test.swift:314:15:314:15 | [post] t1 | test.swift:316:5:316:5 | t1 | +| test.swift:314:15:314:15 | t1 | test.swift:316:5:316:5 | t1 | +| test.swift:316:5:316:5 | [post] t1 | test.swift:318:15:318:15 | t1 | +| test.swift:316:5:316:5 | t1 | test.swift:318:15:318:15 | t1 | +| test.swift:318:15:318:15 | t1 | test.swift:319:15:319:15 | t1 | +| test.swift:319:15:319:15 | [post] t1 | test.swift:320:15:320:15 | t1 | +| test.swift:319:15:319:15 | t1 | test.swift:320:15:320:15 | t1 | +| test.swift:324:9:324:9 | WriteDef | test.swift:325:14:325:14 | t1 | +| test.swift:324:14:324:45 | (...) | test.swift:324:9:324:9 | WriteDef | +| test.swift:325:9:325:9 | WriteDef | test.swift:332:15:332:15 | t2 | +| test.swift:325:14:325:14 | t1 | test.swift:325:9:325:9 | WriteDef | +| test.swift:325:14:325:14 | t1 | test.swift:326:21:326:21 | t1 | +| test.swift:326:9:326:17 | WriteDef | test.swift:336:15:336:15 | a | +| test.swift:326:9:326:17 | WriteDef | test.swift:337:15:337:15 | b | +| test.swift:326:9:326:17 | WriteDef | test.swift:338:15:338:15 | c | +| test.swift:326:21:326:21 | t1 | test.swift:326:9:326:17 | WriteDef | +| test.swift:326:21:326:21 | t1 | test.swift:326:9:326:17 | WriteDef | +| test.swift:326:21:326:21 | t1 | test.swift:326:9:326:17 | WriteDef | +| test.swift:326:21:326:21 | t1 | test.swift:328:15:328:15 | t1 | +| test.swift:328:15:328:15 | t1 | test.swift:329:15:329:15 | t1 | +| test.swift:329:15:329:15 | [post] t1 | test.swift:330:15:330:15 | t1 | +| test.swift:329:15:329:15 | t1 | test.swift:330:15:330:15 | t1 | +| test.swift:330:15:330:15 | [post] t1 | test.swift:331:15:331:15 | t1 | +| test.swift:330:15:330:15 | t1 | test.swift:331:15:331:15 | t1 | +| test.swift:332:15:332:15 | t2 | test.swift:333:15:333:15 | t2 | +| test.swift:333:15:333:15 | [post] t2 | test.swift:334:15:334:15 | t2 | +| test.swift:333:15:333:15 | t2 | test.swift:334:15:334:15 | t2 | +| test.swift:334:15:334:15 | [post] t2 | test.swift:335:15:335:15 | t2 | +| test.swift:334:15:334:15 | t2 | test.swift:335:15:335:15 | t2 | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/test.swift b/swift/ql/test/library-tests/dataflow/dataflow/test.swift index 23c8d138a2d..6995754e85c 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/test.swift +++ b/swift/ql/test/library-tests/dataflow/dataflow/test.swift @@ -259,14 +259,39 @@ func optionalSource() -> Int? { return source() } -func test_optionals() { +func test_optionals(y: Int?) { let x = optionalSource() + + sink(opt: x) // $ flow=259 + sink(opt: y) sink(arg: x!) // $ flow=259 - sink(arg: source().signum()) // $ flow=265 + sink(arg: y!) + + sink(arg: source().signum()) // $ flow=270 sink(opt: x?.signum()) // $ flow=259 + sink(opt: y?.signum()) + sink(arg: x ?? 0) // $ MISSING: flow=259 - if let y = x { - sink(arg: y) // $ MISSING: flow=259 + sink(arg: x ?? source()) // $ MISSING: flow=259, 276 + sink(arg: y ?? 0) + sink(arg: y ?? source()) // $ MISSING: flow=278 + + sink(arg: x != nil ? x! : 0) // $ MISSING: flow=259 + sink(arg: x != nil ? x! : source()) // $ MISSING: flow=259, 281 + sink(arg: y != nil ? y! : 0) + sink(arg: y != nil ? y! : source()) // $ MISSING: flow=283 + + if let z = x { + sink(arg: z) // $ MISSING: flow=259 + } + if let z = y { + sink(arg: z) + } + if let z = x?.signum() { // $ MISSING: flow=259 + sink(arg: z) + } + if let z = y?.signum() { + sink(arg: z) } } @@ -278,7 +303,7 @@ func testTuples() { sink(arg: t1) sink(arg: t1.0) - sink(arg: t1.1) // $ flow=277 + sink(arg: t1.1) // $ flow=302 t1.1 = 2 @@ -289,7 +314,7 @@ func testTuples() { t1.0 = source() sink(arg: t1) - sink(arg: t1.0) // $ flow=289 + sink(arg: t1.0) // $ flow=314 sink(arg: t1.1) } @@ -299,14 +324,14 @@ func testTuples2() { let (a, b, c) = t1 sink(arg: t1) - sink(arg: t1.x) // $ flow=297 - sink(arg: t1.y) // $ flow=297 + sink(arg: t1.x) // $ flow=322 + sink(arg: t1.y) // $ flow=322 sink(arg: t1.z) sink(arg: t2) - sink(arg: t2.x) // $ flow=297 - sink(arg: t2.y) // $ flow=297 + sink(arg: t2.x) // $ flow=322 + sink(arg: t2.y) // $ flow=322 sink(arg: t2.z) - sink(arg: a) // $ MISSING: flow=297 - sink(arg: b) // $ MISSING: flow=297 + sink(arg: a) // $ MISSING: flow=322 + sink(arg: b) // $ MISSING: flow=322 sink(arg: c) } From 2dbb8919424978a588ed69c1828f8a5ade13d459 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 14 Nov 2022 18:29:35 +0000 Subject: [PATCH 0275/1420] Swift: Dataflow through ??. --- .../dataflow/internal/DataFlowPrivate.qll | 7 ++ .../dataflow/dataflow/DataFlow.expected | 6 ++ .../dataflow/dataflow/LocalFlow.expected | 86 ++++++++++--------- .../dataflow/dataflow/test.swift | 4 +- 4 files changed, 62 insertions(+), 41 deletions(-) diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index 82332b4c47d..f811a540c97 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -153,6 +153,13 @@ private module Cached { or nodeFrom.asExpr() = nodeTo.asExpr().(OptionalEvaluationExpr).getSubExpr() or + // flow through nil-coalescing operator `??` + exists(BinaryExpr nco | + nco.getFunction().(DeclRefExpr).getDecl().(FreeFunctionDecl).getName() = "??(_:_:)" and + nodeFrom.asExpr() = nco.getAnOperand() and + nodeTo.asExpr() = nco + ) + or // flow through a flow summary (extension of `SummaryModelCsv`) FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true) } diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected index 0f55b0155c3..8c9c931a4ff 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected @@ -103,6 +103,8 @@ edges | test.swift:263:13:263:28 | call to optionalSource() : | test.swift:265:15:265:15 | x | | test.swift:263:13:263:28 | call to optionalSource() : | test.swift:267:15:267:16 | ...! | | test.swift:263:13:263:28 | call to optionalSource() : | test.swift:271:15:271:16 | ...? : | +| test.swift:263:13:263:28 | call to optionalSource() : | test.swift:274:15:274:20 | ... ??(_:_:) ... | +| test.swift:263:13:263:28 | call to optionalSource() : | test.swift:275:15:275:27 | ... ??(_:_:) ... | | test.swift:270:15:270:22 | call to source() : | file://:0:0:0:0 | [summary param] this in signum() : | | test.swift:270:15:270:22 | call to source() : | test.swift:270:15:270:31 | call to signum() | | test.swift:271:15:271:16 | ...? : | file://:0:0:0:0 | [summary param] this in signum() : | @@ -245,6 +247,8 @@ nodes | test.swift:271:15:271:16 | ...? : | semmle.label | ...? : | | test.swift:271:15:271:25 | OptionalEvaluationExpr | semmle.label | OptionalEvaluationExpr | | test.swift:271:15:271:25 | call to signum() : | semmle.label | call to signum() : | +| test.swift:274:15:274:20 | ... ??(_:_:) ... | semmle.label | ... ??(_:_:) ... | +| test.swift:275:15:275:27 | ... ??(_:_:) ... | semmle.label | ... ??(_:_:) ... | | test.swift:302:14:302:26 | (...) [Tuple element at index 1] : | semmle.label | (...) [Tuple element at index 1] : | | test.swift:302:18:302:25 | call to source() : | semmle.label | call to source() : | | test.swift:306:15:306:15 | t1 [Tuple element at index 1] : | semmle.label | t1 [Tuple element at index 1] : | @@ -326,6 +330,8 @@ subpaths | test.swift:267:15:267:16 | ...! | test.swift:259:12:259:19 | call to source() : | test.swift:267:15:267:16 | ...! | result | | test.swift:270:15:270:31 | call to signum() | test.swift:270:15:270:22 | call to source() : | test.swift:270:15:270:31 | call to signum() | result | | test.swift:271:15:271:25 | OptionalEvaluationExpr | test.swift:259:12:259:19 | call to source() : | test.swift:271:15:271:25 | OptionalEvaluationExpr | result | +| test.swift:274:15:274:20 | ... ??(_:_:) ... | test.swift:259:12:259:19 | call to source() : | test.swift:274:15:274:20 | ... ??(_:_:) ... | result | +| test.swift:275:15:275:27 | ... ??(_:_:) ... | test.swift:259:12:259:19 | call to source() : | test.swift:275:15:275:27 | ... ??(_:_:) ... | result | | test.swift:306:15:306:18 | .1 | test.swift:302:18:302:25 | call to source() : | test.swift:306:15:306:18 | .1 | result | | test.swift:317:15:317:18 | .0 | test.swift:314:12:314:19 | call to source() : | test.swift:317:15:317:18 | .0 | result | | test.swift:327:15:327:18 | .0 | test.swift:322:18:322:25 | call to source() : | test.swift:327:15:327:18 | .0 | result | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected index 91709720aa2..0d48a6af63f 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected @@ -200,10 +200,18 @@ | test.swift:272:15:272:15 | y | test.swift:272:15:272:16 | ...? | | test.swift:272:15:272:15 | y | test.swift:276:15:276:15 | y | | test.swift:272:15:272:25 | call to signum() | test.swift:272:15:272:25 | OptionalEvaluationExpr | +| test.swift:274:15:274:15 | x | test.swift:274:15:274:20 | ... ??(_:_:) ... | | test.swift:274:15:274:15 | x | test.swift:275:15:275:15 | x | +| test.swift:274:20:274:20 | { ... } | test.swift:274:15:274:20 | ... ??(_:_:) ... | +| test.swift:275:15:275:15 | x | test.swift:275:15:275:27 | ... ??(_:_:) ... | | test.swift:275:15:275:15 | x | test.swift:279:15:279:15 | x | +| test.swift:275:20:275:27 | { ... } | test.swift:275:15:275:27 | ... ??(_:_:) ... | +| test.swift:276:15:276:15 | y | test.swift:276:15:276:20 | ... ??(_:_:) ... | | test.swift:276:15:276:15 | y | test.swift:277:15:277:15 | y | +| test.swift:276:20:276:20 | { ... } | test.swift:276:15:276:20 | ... ??(_:_:) ... | +| test.swift:277:15:277:15 | y | test.swift:277:15:277:27 | ... ??(_:_:) ... | | test.swift:277:15:277:15 | y | test.swift:281:15:281:15 | y | +| test.swift:277:20:277:27 | { ... } | test.swift:277:15:277:27 | ... ??(_:_:) ... | | test.swift:279:15:279:15 | x | test.swift:279:26:279:26 | x | | test.swift:279:15:279:15 | x | test.swift:280:15:280:15 | x | | test.swift:279:26:279:26 | x | test.swift:279:26:279:27 | ...! | @@ -226,44 +234,44 @@ | test.swift:290:16:290:26 | call to signum() | test.swift:290:16:290:26 | OptionalEvaluationExpr | | test.swift:293:16:293:16 | y | test.swift:293:16:293:17 | ...? | | test.swift:293:16:293:26 | call to signum() | test.swift:293:16:293:26 | OptionalEvaluationExpr | -| test.swift:304:9:304:9 | WriteDef | test.swift:306:15:306:15 | t1 | -| test.swift:304:14:304:26 | (...) | test.swift:304:9:304:9 | WriteDef | -| test.swift:306:15:306:15 | t1 | test.swift:307:15:307:15 | t1 | -| test.swift:307:15:307:15 | [post] t1 | test.swift:308:15:308:15 | t1 | -| test.swift:307:15:307:15 | t1 | test.swift:308:15:308:15 | t1 | -| test.swift:308:15:308:15 | [post] t1 | test.swift:310:5:310:5 | t1 | -| test.swift:308:15:308:15 | t1 | test.swift:310:5:310:5 | t1 | -| test.swift:310:5:310:5 | [post] t1 | test.swift:312:15:312:15 | t1 | -| test.swift:310:5:310:5 | t1 | test.swift:312:15:312:15 | t1 | -| test.swift:312:15:312:15 | t1 | test.swift:313:15:313:15 | t1 | -| test.swift:313:15:313:15 | [post] t1 | test.swift:314:15:314:15 | t1 | -| test.swift:313:15:313:15 | t1 | test.swift:314:15:314:15 | t1 | -| test.swift:314:15:314:15 | [post] t1 | test.swift:316:5:316:5 | t1 | -| test.swift:314:15:314:15 | t1 | test.swift:316:5:316:5 | t1 | -| test.swift:316:5:316:5 | [post] t1 | test.swift:318:15:318:15 | t1 | -| test.swift:316:5:316:5 | t1 | test.swift:318:15:318:15 | t1 | -| test.swift:318:15:318:15 | t1 | test.swift:319:15:319:15 | t1 | -| test.swift:319:15:319:15 | [post] t1 | test.swift:320:15:320:15 | t1 | -| test.swift:319:15:319:15 | t1 | test.swift:320:15:320:15 | t1 | -| test.swift:324:9:324:9 | WriteDef | test.swift:325:14:325:14 | t1 | -| test.swift:324:14:324:45 | (...) | test.swift:324:9:324:9 | WriteDef | -| test.swift:325:9:325:9 | WriteDef | test.swift:332:15:332:15 | t2 | -| test.swift:325:14:325:14 | t1 | test.swift:325:9:325:9 | WriteDef | -| test.swift:325:14:325:14 | t1 | test.swift:326:21:326:21 | t1 | -| test.swift:326:9:326:17 | WriteDef | test.swift:336:15:336:15 | a | -| test.swift:326:9:326:17 | WriteDef | test.swift:337:15:337:15 | b | -| test.swift:326:9:326:17 | WriteDef | test.swift:338:15:338:15 | c | -| test.swift:326:21:326:21 | t1 | test.swift:326:9:326:17 | WriteDef | -| test.swift:326:21:326:21 | t1 | test.swift:326:9:326:17 | WriteDef | -| test.swift:326:21:326:21 | t1 | test.swift:326:9:326:17 | WriteDef | -| test.swift:326:21:326:21 | t1 | test.swift:328:15:328:15 | t1 | +| test.swift:302:9:302:9 | WriteDef | test.swift:304:15:304:15 | t1 | +| test.swift:302:14:302:26 | (...) | test.swift:302:9:302:9 | WriteDef | +| test.swift:304:15:304:15 | t1 | test.swift:305:15:305:15 | t1 | +| test.swift:305:15:305:15 | [post] t1 | test.swift:306:15:306:15 | t1 | +| test.swift:305:15:305:15 | t1 | test.swift:306:15:306:15 | t1 | +| test.swift:306:15:306:15 | [post] t1 | test.swift:308:5:308:5 | t1 | +| test.swift:306:15:306:15 | t1 | test.swift:308:5:308:5 | t1 | +| test.swift:308:5:308:5 | [post] t1 | test.swift:310:15:310:15 | t1 | +| test.swift:308:5:308:5 | t1 | test.swift:310:15:310:15 | t1 | +| test.swift:310:15:310:15 | t1 | test.swift:311:15:311:15 | t1 | +| test.swift:311:15:311:15 | [post] t1 | test.swift:312:15:312:15 | t1 | +| test.swift:311:15:311:15 | t1 | test.swift:312:15:312:15 | t1 | +| test.swift:312:15:312:15 | [post] t1 | test.swift:314:5:314:5 | t1 | +| test.swift:312:15:312:15 | t1 | test.swift:314:5:314:5 | t1 | +| test.swift:314:5:314:5 | [post] t1 | test.swift:316:15:316:15 | t1 | +| test.swift:314:5:314:5 | t1 | test.swift:316:15:316:15 | t1 | +| test.swift:316:15:316:15 | t1 | test.swift:317:15:317:15 | t1 | +| test.swift:317:15:317:15 | [post] t1 | test.swift:318:15:318:15 | t1 | +| test.swift:317:15:317:15 | t1 | test.swift:318:15:318:15 | t1 | +| test.swift:322:9:322:9 | WriteDef | test.swift:323:14:323:14 | t1 | +| test.swift:322:14:322:45 | (...) | test.swift:322:9:322:9 | WriteDef | +| test.swift:323:9:323:9 | WriteDef | test.swift:330:15:330:15 | t2 | +| test.swift:323:14:323:14 | t1 | test.swift:323:9:323:9 | WriteDef | +| test.swift:323:14:323:14 | t1 | test.swift:324:21:324:21 | t1 | +| test.swift:324:9:324:17 | WriteDef | test.swift:334:15:334:15 | a | +| test.swift:324:9:324:17 | WriteDef | test.swift:335:15:335:15 | b | +| test.swift:324:9:324:17 | WriteDef | test.swift:336:15:336:15 | c | +| test.swift:324:21:324:21 | t1 | test.swift:324:9:324:17 | WriteDef | +| test.swift:324:21:324:21 | t1 | test.swift:324:9:324:17 | WriteDef | +| test.swift:324:21:324:21 | t1 | test.swift:324:9:324:17 | WriteDef | +| test.swift:324:21:324:21 | t1 | test.swift:326:15:326:15 | t1 | +| test.swift:326:15:326:15 | t1 | test.swift:327:15:327:15 | t1 | +| test.swift:327:15:327:15 | [post] t1 | test.swift:328:15:328:15 | t1 | +| test.swift:327:15:327:15 | t1 | test.swift:328:15:328:15 | t1 | +| test.swift:328:15:328:15 | [post] t1 | test.swift:329:15:329:15 | t1 | | test.swift:328:15:328:15 | t1 | test.swift:329:15:329:15 | t1 | -| test.swift:329:15:329:15 | [post] t1 | test.swift:330:15:330:15 | t1 | -| test.swift:329:15:329:15 | t1 | test.swift:330:15:330:15 | t1 | -| test.swift:330:15:330:15 | [post] t1 | test.swift:331:15:331:15 | t1 | -| test.swift:330:15:330:15 | t1 | test.swift:331:15:331:15 | t1 | +| test.swift:330:15:330:15 | t2 | test.swift:331:15:331:15 | t2 | +| test.swift:331:15:331:15 | [post] t2 | test.swift:332:15:332:15 | t2 | +| test.swift:331:15:331:15 | t2 | test.swift:332:15:332:15 | t2 | +| test.swift:332:15:332:15 | [post] t2 | test.swift:333:15:333:15 | t2 | | test.swift:332:15:332:15 | t2 | test.swift:333:15:333:15 | t2 | -| test.swift:333:15:333:15 | [post] t2 | test.swift:334:15:334:15 | t2 | -| test.swift:333:15:333:15 | t2 | test.swift:334:15:334:15 | t2 | -| test.swift:334:15:334:15 | [post] t2 | test.swift:335:15:335:15 | t2 | -| test.swift:334:15:334:15 | t2 | test.swift:335:15:335:15 | t2 | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/test.swift b/swift/ql/test/library-tests/dataflow/dataflow/test.swift index 6995754e85c..a8a90fd5dca 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/test.swift +++ b/swift/ql/test/library-tests/dataflow/dataflow/test.swift @@ -271,8 +271,8 @@ func test_optionals(y: Int?) { sink(opt: x?.signum()) // $ flow=259 sink(opt: y?.signum()) - sink(arg: x ?? 0) // $ MISSING: flow=259 - sink(arg: x ?? source()) // $ MISSING: flow=259, 276 + sink(arg: x ?? 0) // $ flow=259 + sink(arg: x ?? source()) // $ flow=259 MISSING: flow=276 sink(arg: y ?? 0) sink(arg: y ?? source()) // $ MISSING: flow=278 From 52d5578fb5d842e5c07dc40ef2387588b3345893 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 14 Nov 2022 22:10:17 +0000 Subject: [PATCH 0276/1420] Swift: Dataflow through second argument of ??. --- .../codeql/swift/dataflow/internal/DataFlowPrivate.qll | 9 ++++++++- 1 file changed, 8 insertions(+), 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 f811a540c97..81a8bd4c85a 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -156,7 +156,14 @@ private module Cached { // flow through nil-coalescing operator `??` exists(BinaryExpr nco | nco.getFunction().(DeclRefExpr).getDecl().(FreeFunctionDecl).getName() = "??(_:_:)" and - nodeFrom.asExpr() = nco.getAnOperand() and + ( + // value argument + nodeFrom.asExpr() = nco.getAnOperand() + or + // unpack closure (the second argument is typically an `AutoClosureExpr` argument) + nodeFrom.asExpr() = + nco.getAnOperand().(AbstractClosureExpr).getBody().getAnElement().(ReturnStmt).getResult() + ) and nodeTo.asExpr() = nco ) or From 4c806a442aa6f193af9fcb38b7bcb87d26171519 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 15 Nov 2022 11:53:34 +0000 Subject: [PATCH 0277/1420] Swift: Dataflow through ? :. --- .../swift/dataflow/internal/DataFlowPrivate.qll | 9 +++++++++ .../dataflow/dataflow/DataFlow.expected | 13 +++++++++++++ .../dataflow/dataflow/LocalFlow.expected | 8 ++++++++ .../test/library-tests/dataflow/dataflow/test.swift | 6 +++--- 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index 81a8bd4c85a..7fcabf587ef 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -167,6 +167,15 @@ private module Cached { nodeTo.asExpr() = nco ) or + // flow through ternary operator `? :` + exists(IfExpr ie | + nodeTo.asExpr() = ie and + ( + nodeFrom.asExpr() = ie.getThenExpr() or + nodeFrom.asExpr() = ie.getElseExpr() + ) + ) + or // flow through a flow summary (extension of `SummaryModelCsv`) FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true) } diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected index 8c9c931a4ff..1277d30cdc2 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected @@ -105,11 +105,15 @@ edges | test.swift:263:13:263:28 | call to optionalSource() : | test.swift:271:15:271:16 | ...? : | | test.swift:263:13:263:28 | call to optionalSource() : | test.swift:274:15:274:20 | ... ??(_:_:) ... | | test.swift:263:13:263:28 | call to optionalSource() : | test.swift:275:15:275:27 | ... ??(_:_:) ... | +| test.swift:263:13:263:28 | call to optionalSource() : | test.swift:279:15:279:31 | ... ? ... : ... | +| test.swift:263:13:263:28 | call to optionalSource() : | test.swift:280:15:280:38 | ... ? ... : ... | | test.swift:270:15:270:22 | call to source() : | file://:0:0:0:0 | [summary param] this in signum() : | | test.swift:270:15:270:22 | call to source() : | test.swift:270:15:270:31 | call to signum() | | test.swift:271:15:271:16 | ...? : | file://:0:0:0:0 | [summary param] this in signum() : | | test.swift:271:15:271:16 | ...? : | test.swift:271:15:271:25 | call to signum() : | | test.swift:271:15:271:25 | call to signum() : | test.swift:271:15:271:25 | OptionalEvaluationExpr | +| test.swift:280:31:280:38 | call to source() : | test.swift:280:15:280:38 | ... ? ... : ... | +| test.swift:282:31:282:38 | call to source() : | test.swift:282:15:282:38 | ... ? ... : ... | | test.swift:302:14:302:26 | (...) [Tuple element at index 1] : | test.swift:306:15:306:15 | t1 [Tuple element at index 1] : | | test.swift:302:18:302:25 | call to source() : | test.swift:302:14:302:26 | (...) [Tuple element at index 1] : | | test.swift:306:15:306:15 | t1 [Tuple element at index 1] : | test.swift:306:15:306:18 | .1 | @@ -249,6 +253,11 @@ nodes | test.swift:271:15:271:25 | call to signum() : | semmle.label | call to signum() : | | test.swift:274:15:274:20 | ... ??(_:_:) ... | semmle.label | ... ??(_:_:) ... | | test.swift:275:15:275:27 | ... ??(_:_:) ... | semmle.label | ... ??(_:_:) ... | +| test.swift:279:15:279:31 | ... ? ... : ... | semmle.label | ... ? ... : ... | +| test.swift:280:15:280:38 | ... ? ... : ... | semmle.label | ... ? ... : ... | +| test.swift:280:31:280:38 | call to source() : | semmle.label | call to source() : | +| test.swift:282:15:282:38 | ... ? ... : ... | semmle.label | ... ? ... : ... | +| test.swift:282:31:282:38 | call to source() : | semmle.label | call to source() : | | test.swift:302:14:302:26 | (...) [Tuple element at index 1] : | semmle.label | (...) [Tuple element at index 1] : | | test.swift:302:18:302:25 | call to source() : | semmle.label | call to source() : | | test.swift:306:15:306:15 | t1 [Tuple element at index 1] : | semmle.label | t1 [Tuple element at index 1] : | @@ -332,6 +341,10 @@ subpaths | test.swift:271:15:271:25 | OptionalEvaluationExpr | test.swift:259:12:259:19 | call to source() : | test.swift:271:15:271:25 | OptionalEvaluationExpr | result | | test.swift:274:15:274:20 | ... ??(_:_:) ... | test.swift:259:12:259:19 | call to source() : | test.swift:274:15:274:20 | ... ??(_:_:) ... | result | | test.swift:275:15:275:27 | ... ??(_:_:) ... | test.swift:259:12:259:19 | call to source() : | test.swift:275:15:275:27 | ... ??(_:_:) ... | result | +| test.swift:279:15:279:31 | ... ? ... : ... | test.swift:259:12:259:19 | call to source() : | test.swift:279:15:279:31 | ... ? ... : ... | result | +| test.swift:280:15:280:38 | ... ? ... : ... | test.swift:259:12:259:19 | call to source() : | test.swift:280:15:280:38 | ... ? ... : ... | result | +| test.swift:280:15:280:38 | ... ? ... : ... | test.swift:280:31:280:38 | call to source() : | test.swift:280:15:280:38 | ... ? ... : ... | result | +| test.swift:282:15:282:38 | ... ? ... : ... | test.swift:282:31:282:38 | call to source() : | test.swift:282:15:282:38 | ... ? ... : ... | result | | test.swift:306:15:306:18 | .1 | test.swift:302:18:302:25 | call to source() : | test.swift:306:15:306:18 | .1 | result | | test.swift:317:15:317:18 | .0 | test.swift:314:12:314:19 | call to source() : | test.swift:317:15:317:18 | .0 | result | | test.swift:327:15:327:18 | .0 | test.swift:322:18:322:25 | call to source() : | test.swift:327:15:327:18 | .0 | result | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected index 0d48a6af63f..69e2a3ad207 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected @@ -216,18 +216,26 @@ | test.swift:279:15:279:15 | x | test.swift:280:15:280:15 | x | | test.swift:279:26:279:26 | x | test.swift:279:26:279:27 | ...! | | test.swift:279:26:279:26 | x | test.swift:280:15:280:15 | x | +| test.swift:279:26:279:27 | ...! | test.swift:279:15:279:31 | ... ? ... : ... | +| test.swift:279:31:279:31 | 0 | test.swift:279:15:279:31 | ... ? ... : ... | | test.swift:280:15:280:15 | x | test.swift:280:26:280:26 | x | | test.swift:280:15:280:15 | x | test.swift:284:16:284:16 | x | | test.swift:280:26:280:26 | x | test.swift:280:26:280:27 | ...! | | test.swift:280:26:280:26 | x | test.swift:284:16:284:16 | x | +| test.swift:280:26:280:27 | ...! | test.swift:280:15:280:38 | ... ? ... : ... | +| test.swift:280:31:280:38 | call to source() | test.swift:280:15:280:38 | ... ? ... : ... | | test.swift:281:15:281:15 | y | test.swift:281:26:281:26 | y | | test.swift:281:15:281:15 | y | test.swift:282:15:282:15 | y | | test.swift:281:26:281:26 | y | test.swift:281:26:281:27 | ...! | | test.swift:281:26:281:26 | y | test.swift:282:15:282:15 | y | +| test.swift:281:26:281:27 | ...! | test.swift:281:15:281:31 | ... ? ... : ... | +| test.swift:281:31:281:31 | 0 | test.swift:281:15:281:31 | ... ? ... : ... | | test.swift:282:15:282:15 | y | test.swift:282:26:282:26 | y | | test.swift:282:15:282:15 | y | test.swift:287:16:287:16 | y | | test.swift:282:26:282:26 | y | test.swift:282:26:282:27 | ...! | | test.swift:282:26:282:26 | y | test.swift:287:16:287:16 | y | +| test.swift:282:26:282:27 | ...! | test.swift:282:15:282:38 | ... ? ... : ... | +| test.swift:282:31:282:38 | call to source() | test.swift:282:15:282:38 | ... ? ... : ... | | test.swift:284:16:284:16 | x | test.swift:290:16:290:16 | x | | test.swift:287:16:287:16 | y | test.swift:293:16:293:16 | y | | test.swift:290:16:290:16 | x | test.swift:290:16:290:17 | ...? | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/test.swift b/swift/ql/test/library-tests/dataflow/dataflow/test.swift index a8a90fd5dca..093e8c2db95 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/test.swift +++ b/swift/ql/test/library-tests/dataflow/dataflow/test.swift @@ -276,10 +276,10 @@ func test_optionals(y: Int?) { sink(arg: y ?? 0) sink(arg: y ?? source()) // $ MISSING: flow=278 - sink(arg: x != nil ? x! : 0) // $ MISSING: flow=259 - sink(arg: x != nil ? x! : source()) // $ MISSING: flow=259, 281 + sink(arg: x != nil ? x! : 0) // $ flow=259 + sink(arg: x != nil ? x! : source()) // $ flow=259 flow=280 sink(arg: y != nil ? y! : 0) - sink(arg: y != nil ? y! : source()) // $ MISSING: flow=283 + sink(arg: y != nil ? y! : source()) // $ flow=282 if let z = x { sink(arg: z) // $ MISSING: flow=259 From ae5689b2952f64a36f99e1d1a62bd19899ac854f Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 15 Nov 2022 12:04:10 +0000 Subject: [PATCH 0278/1420] Swift: Update comment. --- 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 7fcabf587ef..565bb4a8e93 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -148,7 +148,7 @@ private module Cached { // flow through `!` nodeFrom.asExpr() = nodeTo.asExpr().(ForceValueExpr).getSubExpr() or - // flow through `?` + // flow through `?` and `?.` nodeFrom.asExpr() = nodeTo.asExpr().(BindOptionalExpr).getSubExpr() or nodeFrom.asExpr() = nodeTo.asExpr().(OptionalEvaluationExpr).getSubExpr() From 65c1e239eb1249d02361e65b0ffc38a1ada60593 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Tue, 15 Nov 2022 14:49:06 +0100 Subject: [PATCH 0279/1420] clean up the cache when compiling on main --- .github/workflows/compile-queries.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/compile-queries.yml b/.github/workflows/compile-queries.yml index bdcf0ba8d8a..73b674aff30 100644 --- a/.github/workflows/compile-queries.yml +++ b/.github/workflows/compile-queries.yml @@ -56,4 +56,20 @@ jobs: # do full compile if running on main - this populates the cache if : ${{ github.event_name != 'pull_request' }} shell: bash - run: codeql query compile -j0 */ql/src --keep-going --warnings=error \ No newline at end of file + run: | + # Move all the existing cache into another folder, so we only preserve the cache for the current queries. + mkdir -p ${COMBINED_CACHE_DIR} + rm */ql/src/.cache/{lock,size} + # replicate the folder structure from the .cache folders into the combined cache folder. (because cp doesn't have a "create missing folders" option) + find */ql/src/.cache -type d | cut -d "/" -f 6,7 | sort | uniq > folders.txt + cat folders.txt | xargs -I {} mkdir -p ${COMBINED_CACHE_DIR}/{} + # copy the contents of the .cache folders into the combined cache folder. + cp -r */ql/src/.cache/* ${COMBINED_CACHE_DIR}/ + # clean up the .cache folders + rm -rf */ql/src/.cache/* + rm folders.txt + + # compile the queries + codeql query compile -j0 */ql/src --keep-going --warnings=error --compilation-cache ${COMBINED_CACHE_DIR} + env: + COMBINED_CACHE_DIR: ${{ github.workspace }}/compilation-dir \ No newline at end of file From c2171c01e1514b331f8cb41a72640a9431286699 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Tue, 15 Nov 2022 15:00:30 +0100 Subject: [PATCH 0280/1420] Swift: remove double newlines in schema While PEP8 mandates those, they look bad in the schema file. `autopep8` already ignores those, and they were single newlines at some point until an overeager IDE has "fixed" them at some point without me realizing. Also, the pre-commit configuration was updated to take `schema.py` into account. --- .pre-commit-config.yaml | 4 +- swift/schema.py | 279 ---------------------------------------- 2 files changed, 2 insertions(+), 281 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 50a6adf80d9..14845337b36 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,7 +19,7 @@ repos: rev: v1.6.0 hooks: - id: autopep8 - files: ^swift/codegen/.*\.py + files: ^swift/.*\.py - repo: local hooks: @@ -44,7 +44,7 @@ repos: - id: swift-codegen name: Run Swift checked in code generation - files: ^swift/(codegen/|.*/generated/|ql/lib/(swift\.dbscheme$|codeql/swift/elements)) + files: ^swift/(schema.py$|codegen/|.*/generated/|ql/lib/(swift\.dbscheme$|codeql/swift/elements)) language: system entry: bazel run //swift/codegen -- --quiet pass_filenames: false diff --git a/swift/schema.py b/swift/schema.py index eac52c4e97b..e9e8746d0c3 100644 --- a/swift/schema.py +++ b/swift/schema.py @@ -11,17 +11,14 @@ from swift.codegen.lib.schema.defs import * include("prefix.dbscheme") - @qltest.skip class Element: is_unknown: predicate | cpp.skip - @qltest.collapse_hierarchy class File(Element): name: string - @qltest.skip @qltest.collapse_hierarchy class Location(Element): @@ -31,19 +28,16 @@ class Location(Element): end_line: int end_column: int - @qltest.skip class Locatable(Element): location: optional[Location] | cpp.skip | doc("location associated with this element in the code") - @qltest.collapse_hierarchy @qltest.skip class ErrorElement(Locatable): """The superclass of all elements indicating some kind of error.""" pass - @use_for_null class UnspecifiedElement(ErrorElement): parent: optional[Element] @@ -51,130 +45,103 @@ class UnspecifiedElement(ErrorElement): index: optional[int] error: string - class Comment(Locatable): text: string - class Diagnostics(Locatable): text: string kind: int - class DbFile(File): pass - class DbLocation(Location): pass - @synth.on_arguments() class UnknownFile(File): pass - @synth.on_arguments() class UnknownLocation(Location): pass - class AstNode(Locatable): pass - @group("type") class Type(Element): name: string canonical_type: "Type" - @group("decl") class Decl(AstNode): module: "ModuleDecl" - @group("expr") class Expr(AstNode): """The base class for all expressions in Swift.""" type: optional[Type] - @group("pattern") class Pattern(AstNode): pass - @group("stmt") class Stmt(AstNode): pass - @group("decl") class GenericContext(Element): generic_type_params: list["GenericTypeParamDecl"] | child - @group("decl") class IterableDeclContext(Element): members: list[Decl] | child - class EnumCaseDecl(Decl): elements: list["EnumElementDecl"] - class ExtensionDecl(GenericContext, IterableDeclContext, Decl): extended_type_decl: "NominalTypeDecl" - class IfConfigDecl(Decl): active_elements: list[AstNode] - class ImportDecl(Decl): is_exported: predicate imported_module: optional["ModuleDecl"] declarations: list["ValueDecl"] - @qltest.skip class MissingMemberDecl(Decl): """A placeholder for missing declarations that can arise on object deserialization.""" name: string - class OperatorDecl(Decl): name: string - class PatternBindingDecl(Decl): inits: list[optional[Expr]] | child patterns: list[Pattern] | child - class PoundDiagnosticDecl(Decl): """ A diagnostic directive, which is either `#error` or `#warning`.""" kind: int | doc("""This is 1 for `#error` and 2 for `#warning`""") message: "StringLiteralExpr" | child - class PrecedenceGroupDecl(Decl): pass - class TopLevelCodeDecl(Decl): body: "BraceStmt" | child - class ValueDecl(Decl): interface_type: Type - class AbstractStorageDecl(ValueDecl): accessor_decls: list["AccessorDecl"] | child - class VarDecl(AbstractStorageDecl): name: string type: Type @@ -198,7 +165,6 @@ class VarDecl(AbstractStorageDecl): prefixed with `$`. """) - class ParamDecl(VarDecl): is_inout: predicate | doc("this is an `inout` parameter") property_wrapper_local_wrapped_var_binding: optional[PatternBindingDecl] | child | desc(""" @@ -210,101 +176,80 @@ class ParamDecl(VarDecl): has a property wrapper. """) - class Callable(Element): self_param: optional[ParamDecl] | child params: list[ParamDecl] | child body: optional["BraceStmt"] | child | desc("The body is absent within protocol declarations.") - class AbstractFunctionDecl(GenericContext, ValueDecl, Callable): name: string | doc("name of this function") - class EnumElementDecl(ValueDecl): name: string params: list[ParamDecl] | child - class InfixOperatorDecl(OperatorDecl): precedence_group: optional[PrecedenceGroupDecl] - class PostfixOperatorDecl(OperatorDecl): pass - class PrefixOperatorDecl(OperatorDecl): pass - class TypeDecl(ValueDecl): name: string base_types: list[Type] - class AbstractTypeParamDecl(TypeDecl): pass - class ConstructorDecl(AbstractFunctionDecl): pass - class DestructorDecl(AbstractFunctionDecl): pass - class FuncDecl(AbstractFunctionDecl): pass - class GenericTypeDecl(GenericContext, TypeDecl): pass - class ModuleDecl(TypeDecl): is_builtin_module: predicate | doc("this module is the built-in one") is_system_module: predicate | doc("this module is a system one") imported_modules: list["ModuleDecl"] exported_modules: list["ModuleDecl"] - class SubscriptDecl(AbstractStorageDecl, GenericContext): params: list[ParamDecl] | child element_type: Type element_type: Type - class AccessorDecl(FuncDecl): is_getter: predicate | doc('this accessor is a getter') is_setter: predicate | doc('this accessor is a setter') is_will_set: predicate | doc('this accessor is a `willSet`, called before the property is set') is_did_set: predicate | doc('this accessor is a `didSet`, called after the property is set') - class AssociatedTypeDecl(AbstractTypeParamDecl): pass - class ConcreteFuncDecl(FuncDecl): pass - class ConcreteVarDecl(VarDecl): introducer_int: int | doc("introducer enumeration value") | desc(""" This is 0 if the variable was introduced with `let` and 1 if it was introduced with `var`. """) - class GenericTypeParamDecl(AbstractTypeParamDecl): pass - class NominalTypeDecl(GenericTypeDecl, IterableDeclContext): type: Type - class OpaqueTypeDecl(GenericTypeDecl): """ A declaration of an opaque type, that is formally equivalent to a given type but abstracts it @@ -321,71 +266,56 @@ class OpaqueTypeDecl(GenericTypeDecl): opaque_generic_params: list["GenericTypeParamType"] opaque_generic_params: list["GenericTypeParamType"] - class TypeAliasDecl(GenericTypeDecl): pass - class ClassDecl(NominalTypeDecl): pass - class EnumDecl(NominalTypeDecl): pass - class ProtocolDecl(NominalTypeDecl): pass - class StructDecl(NominalTypeDecl): pass - @group("expr") class Argument(Locatable): label: string expr: Expr | child - class AbstractClosureExpr(Expr, Callable): pass - class AnyTryExpr(Expr): sub_expr: Expr | child - class AppliedPropertyWrapperExpr(Expr): """An implicit application of a property wrapper on the argument of a call.""" kind: int | desc("This is 1 for a wrapped value and 2 for a projected one.") value: Expr | child | desc("The value on which the wrapper is applied.") param: ParamDecl | doc("parameter declaration owning this wrapper application") - class ApplyExpr(Expr): function: Expr | child | doc("function being applied") arguments: list[Argument] | child | doc("arguments passed to the applied function") - class AssignExpr(Expr): dest: Expr | child source: Expr | child - class BindOptionalExpr(Expr): sub_expr: Expr | child - class CaptureListExpr(Expr): binding_decls: list[PatternBindingDecl] | child closure_body: "ClosureExpr" | child - class CollectionExpr(Expr): pass - class DeclRefExpr(Expr): decl: Decl replacement_types: list[Type] @@ -393,125 +323,99 @@ class DeclRefExpr(Expr): has_direct_to_implementation_semantics: predicate has_ordinary_semantics: predicate - class DefaultArgumentExpr(Expr): param_decl: ParamDecl param_index: int caller_side_default: optional[Expr] - class DiscardAssignmentExpr(Expr): pass - class DotSyntaxBaseIgnoredExpr(Expr): qualifier: Expr | child sub_expr: Expr | child - class DynamicTypeExpr(Expr): base: Expr | child - class EnumIsCaseExpr(Expr): sub_expr: Expr | child element: EnumElementDecl - @qltest.skip class ErrorExpr(Expr, ErrorElement): pass - class ExplicitCastExpr(Expr): sub_expr: Expr | child - class ForceValueExpr(Expr): sub_expr: Expr | child - @qltest.collapse_hierarchy class IdentityExpr(Expr): sub_expr: Expr | child - class IfExpr(Expr): condition: Expr | child then_expr: Expr | child else_expr: Expr | child - @qltest.collapse_hierarchy class ImplicitConversionExpr(Expr): sub_expr: Expr | child - class InOutExpr(Expr): sub_expr: Expr | child - class KeyPathApplicationExpr(Expr): base: Expr | child key_path: Expr | child - class KeyPathDotExpr(Expr): pass - class KeyPathExpr(Expr): root: optional["TypeRepr"] | child parsed_path: optional[Expr] | child - class LazyInitializerExpr(Expr): sub_expr: Expr | child - class LiteralExpr(Expr): pass - class LookupExpr(Expr): base: Expr | child member: optional[Decl] - class MakeTemporarilyEscapableExpr(Expr): escaping_closure: "OpaqueValueExpr" | child nonescaping_closure: Expr | child sub_expr: Expr | child - @qltest.skip class ObjCSelectorExpr(Expr): sub_expr: Expr | child method: AbstractFunctionDecl - class OneWayExpr(Expr): sub_expr: Expr | child - class OpaqueValueExpr(Expr): pass - class OpenExistentialExpr(Expr): sub_expr: Expr | child existential: Expr | child opaque_expr: OpaqueValueExpr | child - class OptionalEvaluationExpr(Expr): sub_expr: Expr | child - class OtherConstructorDeclRefExpr(Expr): constructor_decl: ConstructorDecl - class PropertyWrapperValuePlaceholderExpr(Expr): """ A placeholder substituting property initializations with `=` when the property has a property @@ -520,233 +424,178 @@ class PropertyWrapperValuePlaceholderExpr(Expr): wrapped_value: optional[Expr] placeholder: OpaqueValueExpr - class RebindSelfInConstructorExpr(Expr): sub_expr: Expr | child self: VarDecl - @qltest.skip class SequenceExpr(Expr): elements: list[Expr] | child - class SuperRefExpr(Expr): self: VarDecl - class TapExpr(Expr): sub_expr: optional[Expr] | child body: "BraceStmt" | child var: VarDecl - class TupleElementExpr(Expr): sub_expr: Expr | child index: int - class TupleExpr(Expr): elements: list[Expr] | child - class TypeExpr(Expr): type_repr: optional["TypeRepr"] | child - class UnresolvedDeclRefExpr(Expr, ErrorElement): name: optional[string] - class UnresolvedDotExpr(Expr, ErrorElement): base: Expr | child name: string - class UnresolvedMemberExpr(Expr, ErrorElement): name: string - class UnresolvedPatternExpr(Expr, ErrorElement): sub_pattern: Pattern | child - class UnresolvedSpecializeExpr(Expr, ErrorElement): sub_expr: Expr | child - class VarargExpansionExpr(Expr): sub_expr: Expr | child - class AnyHashableErasureExpr(ImplicitConversionExpr): pass - class ArchetypeToSuperExpr(ImplicitConversionExpr): pass - class ArrayExpr(CollectionExpr): elements: list[Expr] | child - class ArrayToPointerExpr(ImplicitConversionExpr): pass - class AutoClosureExpr(AbstractClosureExpr): pass - class AwaitExpr(IdentityExpr): pass - class BinaryExpr(ApplyExpr): pass - @qltest.skip class BridgeFromObjCExpr(ImplicitConversionExpr): pass - @qltest.skip class BridgeToObjCExpr(ImplicitConversionExpr): pass - class BuiltinLiteralExpr(LiteralExpr): pass - class CallExpr(ApplyExpr): pass - class CheckedCastExpr(ExplicitCastExpr): pass - class ClassMetatypeToObjectExpr(ImplicitConversionExpr): pass - class ClosureExpr(AbstractClosureExpr): pass - class CoerceExpr(ExplicitCastExpr): pass - class CollectionUpcastConversionExpr(ImplicitConversionExpr): pass - @qltest.skip class ConditionalBridgeFromObjCExpr(ImplicitConversionExpr): pass - class CovariantFunctionConversionExpr(ImplicitConversionExpr): pass - class CovariantReturnConversionExpr(ImplicitConversionExpr): pass - class DerivedToBaseExpr(ImplicitConversionExpr): pass - class DestructureTupleExpr(ImplicitConversionExpr): pass - class DictionaryExpr(CollectionExpr): elements: list[Expr] | child - class DifferentiableFunctionExpr(ImplicitConversionExpr): pass - class DifferentiableFunctionExtractOriginalExpr(ImplicitConversionExpr): pass - class DotSelfExpr(IdentityExpr): pass - @qltest.collapse_hierarchy class DynamicLookupExpr(LookupExpr): pass - class ErasureExpr(ImplicitConversionExpr): pass - class ExistentialMetatypeToObjectExpr(ImplicitConversionExpr): pass - class ForceTryExpr(AnyTryExpr): pass - class ForeignObjectConversionExpr(ImplicitConversionExpr): pass - class FunctionConversionExpr(ImplicitConversionExpr): pass - class InOutToPointerExpr(ImplicitConversionExpr): pass - class InjectIntoOptionalExpr(ImplicitConversionExpr): pass - class InterpolatedStringLiteralExpr(LiteralExpr): interpolation_expr: optional[OpaqueValueExpr] interpolation_count_expr: optional[Expr] | child literal_capacity_expr: optional[Expr] | child appending_expr: optional[TapExpr] | child - class LinearFunctionExpr(ImplicitConversionExpr): pass - class LinearFunctionExtractOriginalExpr(ImplicitConversionExpr): pass - class LinearToDifferentiableFunctionExpr(ImplicitConversionExpr): pass - class LoadExpr(ImplicitConversionExpr): pass - class MemberRefExpr(LookupExpr): has_direct_to_storage_semantics: predicate has_direct_to_implementation_semantics: predicate has_ordinary_semantics: predicate - class MetatypeConversionExpr(ImplicitConversionExpr): pass - class NilLiteralExpr(LiteralExpr): pass - class ObjectLiteralExpr(LiteralExpr): """ An instance of `#fileLiteral`, `#imageLiteral` or `#colorLiteral` expressions, which are used in playgrounds. @@ -754,11 +603,9 @@ class ObjectLiteralExpr(LiteralExpr): kind: int | desc("""This is 0 for `#fileLiteral`, 1 for `#imageLiteral` and 2 for `#colorLiteral`.""") arguments: list[Argument] | child - class OptionalTryExpr(AnyTryExpr): pass - class OverloadedDeclRefExpr(Expr, ErrorElement): """ An ambiguous expression that might refer to multiple declarations. This will be present only @@ -766,287 +613,222 @@ class OverloadedDeclRefExpr(Expr, ErrorElement): """ possible_declarations: list[ValueDecl] - class ParenExpr(IdentityExpr): pass - class PointerToPointerExpr(ImplicitConversionExpr): pass - class PostfixUnaryExpr(ApplyExpr): pass - class PrefixUnaryExpr(ApplyExpr): pass - class ProtocolMetatypeToObjectExpr(ImplicitConversionExpr): pass - class RegexLiteralExpr(LiteralExpr): pass - class SelfApplyExpr(ApplyExpr): base: Expr - class StringToPointerExpr(ImplicitConversionExpr): pass - class SubscriptExpr(LookupExpr): arguments: list[Argument] | child has_direct_to_storage_semantics: predicate has_direct_to_implementation_semantics: predicate has_ordinary_semantics: predicate - class TryExpr(AnyTryExpr): pass - class UnderlyingToOpaqueExpr(ImplicitConversionExpr): pass - class UnevaluatedInstanceExpr(ImplicitConversionExpr): pass - class UnresolvedMemberChainResultExpr(IdentityExpr, ErrorElement): pass - class UnresolvedTypeConversionExpr(ImplicitConversionExpr, ErrorElement): pass - class BooleanLiteralExpr(BuiltinLiteralExpr): value: boolean - class ConditionalCheckedCastExpr(CheckedCastExpr): pass - class ConstructorRefCallExpr(SelfApplyExpr): pass - class DotSyntaxCallExpr(SelfApplyExpr): pass - @synth.from_class(DotSyntaxCallExpr) class MethodRefExpr(LookupExpr): pass - class DynamicMemberRefExpr(DynamicLookupExpr): pass - class DynamicSubscriptExpr(DynamicLookupExpr): pass - class ForcedCheckedCastExpr(CheckedCastExpr): pass - class IsExpr(CheckedCastExpr): pass - class MagicIdentifierLiteralExpr(BuiltinLiteralExpr): kind: string - class NumberLiteralExpr(BuiltinLiteralExpr): pass - class StringLiteralExpr(BuiltinLiteralExpr): value: string - class FloatLiteralExpr(NumberLiteralExpr): string_value: string - class IntegerLiteralExpr(NumberLiteralExpr): string_value: string - class AnyPattern(Pattern): pass - class BindingPattern(Pattern): sub_pattern: Pattern | child - class BoolPattern(Pattern): value: boolean - class EnumElementPattern(Pattern): element: EnumElementDecl sub_pattern: optional[Pattern] | child - class ExprPattern(Pattern): sub_expr: Expr | child - class IsPattern(Pattern): cast_type_repr: optional["TypeRepr"] | child sub_pattern: optional[Pattern] | child - class NamedPattern(Pattern): name: string - class OptionalSomePattern(Pattern): sub_pattern: Pattern | child - class ParenPattern(Pattern): sub_pattern: Pattern | child - class TuplePattern(Pattern): elements: list[Pattern] | child - class TypedPattern(Pattern): sub_pattern: Pattern | child type_repr: optional["TypeRepr"] | child - @group("stmt") class CaseLabelItem(AstNode): pattern: Pattern | child guard: optional[Expr] | child - @group("stmt") class ConditionElement(AstNode): boolean: optional[Expr] | child pattern: optional[Pattern] | child initializer: optional[Expr] | child - @group("stmt") class StmtCondition(AstNode): elements: list[ConditionElement] | child - class BraceStmt(Stmt): elements: list[AstNode] | child - class BreakStmt(Stmt): target_name: optional[string] target: optional[Stmt] - class CaseStmt(Stmt): body: Stmt | child labels: list[CaseLabelItem] | child variables: list[VarDecl] - class ContinueStmt(Stmt): target_name: optional[string] target: optional[Stmt] - class DeferStmt(Stmt): body: BraceStmt | child - class FailStmt(Stmt): pass - class FallthroughStmt(Stmt): fallthrough_source: CaseStmt fallthrough_dest: CaseStmt - class LabeledStmt(Stmt): label: optional[string] - class PoundAssertStmt(Stmt): condition: Expr message: string - class ReturnStmt(Stmt): result: optional[Expr] | child - class ThrowStmt(Stmt): sub_expr: Expr | child - class YieldStmt(Stmt): results: list[Expr] | child - class DoCatchStmt(LabeledStmt): body: Stmt | child catches: list[CaseStmt] | child - class DoStmt(LabeledStmt): body: BraceStmt | child - class ForEachStmt(LabeledStmt): pattern: Pattern | child sequence: Expr | child where: optional[Expr] | child body: BraceStmt | child - class LabeledConditionalStmt(LabeledStmt): condition: StmtCondition | child - class RepeatWhileStmt(LabeledStmt): condition: Expr | child body: Stmt | child - class SwitchStmt(LabeledStmt): expr: Expr | child cases: list[CaseStmt] | child - class GuardStmt(LabeledConditionalStmt): body: BraceStmt | child - class IfStmt(LabeledConditionalStmt): then: Stmt | child else_: optional[Stmt] | child - class WhileStmt(LabeledConditionalStmt): body: Stmt | child - @group("type") class TypeRepr(AstNode): type: Type - @ql.default_doc_name("function type") class AnyFunctionType(Type): result: Type @@ -1055,255 +837,194 @@ class AnyFunctionType(Type): is_throwing: predicate | doc("this type refers to a throwing function") is_async: predicate | doc("this type refers to an `async` function") - class AnyGenericType(Type): parent: optional[Type] declaration: Decl - class AnyMetatypeType(Type): pass - @qltest.collapse_hierarchy class BuiltinType(Type): pass - class DependentMemberType(Type): base_type: Type associated_type_decl: AssociatedTypeDecl - class DynamicSelfType(Type): static_self_type: Type - class ErrorType(Type, ErrorElement): pass - class ExistentialType(Type): constraint: Type - class InOutType(Type): object_type: Type - class LValueType(Type): object_type: Type - class ModuleType(Type): module: ModuleDecl - class ProtocolCompositionType(Type): members: list[Type] - class ReferenceStorageType(Type): referent_type: Type - class SubstitutableType(Type): pass - class SugarType(Type): pass - class TupleType(Type): types: list[Type] names: list[optional[string]] - class UnresolvedType(Type, ErrorElement): pass - class AnyBuiltinIntegerType(BuiltinType): pass - class ArchetypeType(SubstitutableType): interface_type: Type superclass: optional[Type] protocols: list[ProtocolDecl] - class BuiltinBridgeObjectType(BuiltinType): pass - class BuiltinDefaultActorStorageType(BuiltinType): pass - class BuiltinExecutorType(BuiltinType): pass - class BuiltinFloatType(BuiltinType): pass - class BuiltinJobType(BuiltinType): pass - class BuiltinNativeObjectType(BuiltinType): pass - class BuiltinRawPointerType(BuiltinType): pass - class BuiltinRawUnsafeContinuationType(BuiltinType): pass - class BuiltinUnsafeValueBufferType(BuiltinType): pass - class BuiltinVectorType(BuiltinType): pass - class ExistentialMetatypeType(AnyMetatypeType): pass - class FunctionType(AnyFunctionType): pass - class GenericFunctionType(AnyFunctionType): """ The type of a generic function with type parameters """ generic_params: list["GenericTypeParamType"] | doc("type {parameters} of this generic type") - class GenericTypeParamType(SubstitutableType): pass - class MetatypeType(AnyMetatypeType): pass - class NominalOrBoundGenericNominalType(AnyGenericType): pass - class ParenType(SugarType): type: Type - class SyntaxSugarType(SugarType): pass - class TypeAliasType(SugarType): decl: TypeAliasDecl - class UnboundGenericType(AnyGenericType): pass - class UnmanagedStorageType(ReferenceStorageType): pass - class UnownedStorageType(ReferenceStorageType): pass - class WeakStorageType(ReferenceStorageType): pass - class BoundGenericType(NominalOrBoundGenericNominalType): arg_types: list[Type] - class BuiltinIntegerLiteralType(AnyBuiltinIntegerType): pass - @qltest.uncollapse_hierarchy class BuiltinIntegerType(AnyBuiltinIntegerType): width: optional[int] - class DictionaryType(SyntaxSugarType): key_type: Type value_type: Type - class NominalType(NominalOrBoundGenericNominalType): pass - class OpaqueTypeArchetypeType(ArchetypeType): """An opaque type, that is a type formally equivalent to an underlying type but abstracting it away. See https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html.""" declaration: OpaqueTypeDecl - class OpenedArchetypeType(ArchetypeType): pass - class PrimaryArchetypeType(ArchetypeType): pass - class UnarySyntaxSugarType(SyntaxSugarType): base_type: Type - class ArraySliceType(UnarySyntaxSugarType): pass - class BoundGenericClassType(BoundGenericType): pass - class BoundGenericEnumType(BoundGenericType): pass - class BoundGenericStructType(BoundGenericType): pass - class ClassType(NominalType): pass - class EnumType(NominalType): pass - class OptionalType(UnarySyntaxSugarType): pass - class ProtocolType(NominalType): pass - class StructType(NominalType): pass - class VariadicSequenceType(UnarySyntaxSugarType): pass - class ParameterizedProtocolType(Type): """ A sugar type of the form `P` with `P` a protocol. From 635391eae8032f628e463d24383782f6c4df95c1 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Tue, 15 Nov 2022 15:43:05 +0100 Subject: [PATCH 0281/1420] Swift: autopep8 integration tests --- swift/integration-tests/posix-only/cross-references/test.py | 4 ++-- .../posix-only/frontend-invocations/test.py | 2 +- swift/integration-tests/posix-only/hello-world/test.py | 4 ++-- swift/integration-tests/posix-only/partial-modules/test.py | 6 +++--- swift/integration-tests/runner.py | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/swift/integration-tests/posix-only/cross-references/test.py b/swift/integration-tests/posix-only/cross-references/test.py index 19271030c19..6f2b8b26143 100644 --- a/swift/integration-tests/posix-only/cross-references/test.py +++ b/swift/integration-tests/posix-only/cross-references/test.py @@ -1,6 +1,6 @@ from create_database_utils import * run_codeql_database_create([ - 'swift package clean', - 'swift build' + 'swift package clean', + 'swift build' ], lang='swift') diff --git a/swift/integration-tests/posix-only/frontend-invocations/test.py b/swift/integration-tests/posix-only/frontend-invocations/test.py index 2c956137cb5..b415c024e56 100644 --- a/swift/integration-tests/posix-only/frontend-invocations/test.py +++ b/swift/integration-tests/posix-only/frontend-invocations/test.py @@ -1,5 +1,5 @@ from create_database_utils import * run_codeql_database_create([ - 'make', + 'make', ], lang='swift') diff --git a/swift/integration-tests/posix-only/hello-world/test.py b/swift/integration-tests/posix-only/hello-world/test.py index 19271030c19..6f2b8b26143 100644 --- a/swift/integration-tests/posix-only/hello-world/test.py +++ b/swift/integration-tests/posix-only/hello-world/test.py @@ -1,6 +1,6 @@ from create_database_utils import * run_codeql_database_create([ - 'swift package clean', - 'swift build' + 'swift package clean', + 'swift build' ], lang='swift') diff --git a/swift/integration-tests/posix-only/partial-modules/test.py b/swift/integration-tests/posix-only/partial-modules/test.py index ae89d5da5d7..17320f33628 100644 --- a/swift/integration-tests/posix-only/partial-modules/test.py +++ b/swift/integration-tests/posix-only/partial-modules/test.py @@ -1,7 +1,7 @@ from create_database_utils import * run_codeql_database_create([ - 'env', - 'swift package clean', - 'swift build' + 'env', + 'swift package clean', + 'swift build' ], lang='swift', keep_trap=True) diff --git a/swift/integration-tests/runner.py b/swift/integration-tests/runner.py index b5c608c2a97..b0b2ecd92b0 100755 --- a/swift/integration-tests/runner.py +++ b/swift/integration-tests/runner.py @@ -21,7 +21,7 @@ this_dir = pathlib.Path(__file__).parent.resolve() def options(): p = argparse.ArgumentParser() p.add_argument("--test-dir", "-d", type=pathlib.Path, action="append") - #FIXME: the following should be the default + # FIXME: the following should be the default p.add_argument("--check-databases", action="store_true") p.add_argument("--learn", action="store_true") p.add_argument("--threads", "-j", type=int, default=0) From 563a56af9d6781af6b1795b7d13a9c1f3f8d7528 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Tue, 15 Nov 2022 15:46:34 +0100 Subject: [PATCH 0282/1420] Update Hapi.qll --- .../ql/lib/semmle/javascript/frameworks/Hapi.qll | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll b/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll index 0d04aac34c6..b3bed8112ad 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll @@ -18,12 +18,14 @@ module Hapi { this = DataFlow::moduleMember("@hapi/glue", "compose").getAnInvocation() or // `register (server, options)` - exists(Function f | - this.(DataFlow::ParameterNode).getParameter() = f.getParameter(0) and - f.getName() = "register" and - f.getParameter(0).getName() = "server" and - f.getParameter(1).getName() = "options" - ) + // `module.exports.plugin = {register, pkg};` + this = + any(NodeModule m) + .getAnExportedValue("plugin") + .(DataFlow::ObjectLiteralNode) + .getAPropertySource("register") + .(DataFlow::FunctionNode) + .getParameter(0) or // `const after = function (server) {...};` // `server.dependency('name', after);` From 8ca004fde157f5fbc3a2cacf452fd886903ea3d9 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Tue, 15 Nov 2022 16:14:22 +0100 Subject: [PATCH 0283/1420] Add AdditionalTaintStep --- swift/ql/lib/codeql/swift/dataflow/FlowSteps.qll | 14 ++++++++++++++ .../dataflow/internal/TaintTrackingPrivate.qll | 2 ++ 2 files changed, 16 insertions(+) diff --git a/swift/ql/lib/codeql/swift/dataflow/FlowSteps.qll b/swift/ql/lib/codeql/swift/dataflow/FlowSteps.qll index e538f44b957..fef6646511d 100644 --- a/swift/ql/lib/codeql/swift/dataflow/FlowSteps.qll +++ b/swift/ql/lib/codeql/swift/dataflow/FlowSteps.qll @@ -1,6 +1,20 @@ import swift private import codeql.swift.dataflow.DataFlow +/** + * A unit class for adding additional taint steps. + * + * Extend this class to add additional taint steps that should apply to all + * taint configurations. + */ +class AdditionalTaintStep extends Unit { + /** + * Holds if the step from `node1` to `node2` should be considered a taint + * step for all configurations. + */ + abstract predicate step(DataFlow::Node node1, DataFlow::Node node2); +} + /** * A `Content` that should be implicitly regarded as tainted whenever an object with such `Content` * is itself tainted. diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll index cd38648aef9..92724729d0e 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll @@ -64,6 +64,8 @@ private module Cached { or // flow through a flow summary (extension of `SummaryModelCsv`) FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, false) + or + any(AdditionalTaintStep a).step(nodeFrom, nodeTo) } /** From e5e3bb3705e5272187b02cdf6b103dfb407bc910 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Tue, 15 Nov 2022 16:27:14 +0100 Subject: [PATCH 0284/1420] Generalize the server definition in plugin registration --- javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll b/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll index b3bed8112ad..b0b33c078db 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll @@ -20,11 +20,11 @@ module Hapi { // `register (server, options)` // `module.exports.plugin = {register, pkg};` this = - any(NodeModule m) + any(Module m) .getAnExportedValue("plugin") - .(DataFlow::ObjectLiteralNode) + .getALocalSource() .getAPropertySource("register") - .(DataFlow::FunctionNode) + .getAFunctionValue() .getParameter(0) or // `const after = function (server) {...};` From 8109a7b67a6cdb654e03d9ada70d235319cb4e70 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Tue, 15 Nov 2022 16:27:21 +0100 Subject: [PATCH 0285/1420] Update javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll Co-authored-by: Erik Krogh Kristensen --- javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll b/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll index b0b33c078db..c791cd9e25d 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll @@ -32,7 +32,7 @@ module Hapi { exists(ServerDefinition server, DataFlow::MethodCallNode call | call = server.ref().getAMethodCall() and call.getMethodName() = "dependency" and - this = call.getArgument(1).(DataFlow::FunctionNode).getParameter(0) + this = call.getABoundCallbackParameter(1, 0) ) } } From da7788dd64a22d51debadcf90f3a74932ed8f332 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Tue, 15 Nov 2022 16:25:59 +0100 Subject: [PATCH 0286/1420] CodeQL: add 'False positive' issue template --- .github/ISSUE_TEMPLATE/ql--false-positive.md | 36 ++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/ql--false-positive.md diff --git a/.github/ISSUE_TEMPLATE/ql--false-positive.md b/.github/ISSUE_TEMPLATE/ql--false-positive.md new file mode 100644 index 00000000000..b3d942ab5d7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/ql--false-positive.md @@ -0,0 +1,36 @@ +--- +name: False positive +about: Tell us about an alert that shouldn't be reported +title: False positive +labels: false-positive +assignees: '' + +--- + +**Description of the false positive** + + + +**Code samples or links to source code** + + + +**URL to the alert on GitHub CodeScanning (optional)** + + From 56b207e41f250dd0c143c860f4af67b94d97ab9e Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Tue, 15 Nov 2022 17:07:52 +0100 Subject: [PATCH 0287/1420] Swift: remove IPA classes from `cppgen` --- swift/codegen/generators/cppgen.py | 9 +++- swift/codegen/test/test_cppgen.py | 79 ++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/swift/codegen/generators/cppgen.py b/swift/codegen/generators/cppgen.py index 45f0d98949b..18d65d54150 100644 --- a/swift/codegen/generators/cppgen.py +++ b/swift/codegen/generators/cppgen.py @@ -80,10 +80,17 @@ class Processor: trap_name=trap_name, ) + @functools.lru_cache(maxsize=None) + def _is_ipa(self, name: str) -> bool: + cls = self._classmap[name] + return cls.ipa is not None or ( + cls.derived and all(self._is_ipa(d) for d in cls.derived)) + def get_classes(self): ret = {'': []} for k, cls in self._classmap.items(): - ret.setdefault(cls.group, []).append(self._get_class(cls.name)) + if not self._is_ipa(k): + ret.setdefault(cls.group, []).append(self._get_class(cls.name)) return ret diff --git a/swift/codegen/test/test_cppgen.py b/swift/codegen/test/test_cppgen.py index df4925cb70e..54f1fdf1f7a 100644 --- a/swift/codegen/test/test_cppgen.py +++ b/swift/codegen/test/test_cppgen.py @@ -180,5 +180,84 @@ def test_cpp_skip_pragma(generate): ] +def test_ipa_classes_ignored(generate): + assert generate([ + schema.Class( + name="X", + ipa=schema.IpaInfo(from_class="A"), + ), + schema.Class( + name="Y", + ipa=schema.IpaInfo(on_arguments={"a": "A", "b": "int"}), + ), + schema.Class( + name="Z", + ), + ]) == [ + cpp.Class(name="Z", final=True, trap_name="Zs"), + ] + + +def test_ipa_hierarchy_ignored(generate): + assert generate([ + schema.Class( + name="Root", + derived={"Base", "Z"}, + ), + schema.Class( + name="Base", + bases=["Root"], + derived={"X", "Y"} + ), + schema.Class( + name="X", + bases=["Base"], + ipa=schema.IpaInfo(from_class="A"), + ), + schema.Class( + name="Y", + bases=["Base"], + ipa=schema.IpaInfo(on_arguments={"a": "A", "b": "int"}), + ), + schema.Class( + name="Z", + ipa=schema.IpaInfo(from_class="A"), + ), + ]) == [] + + +def test_ipa_hierarchy_not_ignored_with_non_ipa_descendant(generate): + root = cpp.Class(name="Root") + base = cpp.Class(name="Base", bases=[root]) + assert generate([ + schema.Class( + name="Root", + derived={"Base", "Z"}, + ), + schema.Class( + name="Base", + bases=["Root"], + derived={"X", "Y"} + ), + schema.Class( + name="X", + bases=["Base"], + ), + schema.Class( + name="Y", + bases=["Base"], + ipa=schema.IpaInfo(on_arguments={"a": "A", "b": "int"}), + ), + schema.Class( + name="Z", + ipa=schema.IpaInfo(from_class="A"), + ), + ]) == [ + root, + base, + cpp.Class(name="X", bases=[base], final=True, trap_name="Xes"), + ] + + if __name__ == '__main__': sys.exit(pytest.main([__file__] + sys.argv[1:])) From e928777cb7825dcf506fa775ef6deefc2d28c1eb Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Tue, 1 Nov 2022 10:17:41 +0100 Subject: [PATCH 0288/1420] add codeql/regex as a dependency --- javascript/ql/lib/qlpack.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/javascript/ql/lib/qlpack.yml b/javascript/ql/lib/qlpack.yml index 0a3a773e368..00b2ffcba05 100644 --- a/javascript/ql/lib/qlpack.yml +++ b/javascript/ql/lib/qlpack.yml @@ -5,3 +5,5 @@ dbscheme: semmlecode.javascript.dbscheme extractor: javascript library: true upgrades: upgrades +dependencies: + codeql/regex: 0.0.1 From 4a2472a078ed319aaa14ca7ed1ff80aac65b5e0c Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Tue, 1 Nov 2022 10:18:07 +0100 Subject: [PATCH 0289/1420] add `hasLocationInfo` predicate to regexp terms --- javascript/ql/lib/semmle/javascript/Regexp.qll | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/javascript/ql/lib/semmle/javascript/Regexp.qll b/javascript/ql/lib/semmle/javascript/Regexp.qll index e443ede5104..fc82929500b 100644 --- a/javascript/ql/lib/semmle/javascript/Regexp.qll +++ b/javascript/ql/lib/semmle/javascript/Regexp.qll @@ -176,6 +176,13 @@ class RegExpTerm extends Locatable, @regexpterm { * Gets a string that is matched by this regular-expression term. */ string getAMatchedString() { result = this.getConstantValue() } + + /** Holds if this term has the specified location. */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } } /** From 031a9109891106d2243cc254ccff29a691549bd2 Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Tue, 1 Nov 2022 10:20:12 +0100 Subject: [PATCH 0290/1420] add a JS implementation of `RegexTreeViewSig` --- .../security/regexp/RegexTreeView.qll | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 javascript/ql/lib/semmle/javascript/security/regexp/RegexTreeView.qll diff --git a/javascript/ql/lib/semmle/javascript/security/regexp/RegexTreeView.qll b/javascript/ql/lib/semmle/javascript/security/regexp/RegexTreeView.qll new file mode 100644 index 00000000000..9009133f4c0 --- /dev/null +++ b/javascript/ql/lib/semmle/javascript/security/regexp/RegexTreeView.qll @@ -0,0 +1,73 @@ +/** + * Provides JavaScript-specific definitions for use in the NfaUtils module. + */ + +private import codeql.regex.nfa.NfaUtils as NfaUtils +private import codeql.regex.RegexTreeView + +/** An implementation that parses a regular expression into a tree of `RegExpTerm`s. */ +module RegexTreeView implements RegexTreeViewSig { + import javascript + + class Top = Locatable; + + /** + * Holds if `term` is an escape class representing e.g. `\d`. + * `clazz` is which character class it represents, e.g. "d" for `\d`. + */ + predicate isEscapeClass(RegExpTerm term, string clazz) { + exists(RegExpCharacterClassEscape escape | term = escape | escape.getValue() = clazz) + } + + /** + * Holds if `term` is a possessive quantifier. + * As javascript's regexes do not support possessive quantifiers, this never holds, but is used by the shared library. + */ + predicate isPossessive(RegExpQuantifier term) { none() } + + /** + * Holds if the regex that `term` is part of is used in a way that ignores any leading prefix of the input it's matched against. + * Not yet implemented for JavaScript. + */ + predicate matchesAnyPrefix(RegExpTerm term) { any() } + + /** + * Holds if the regex that `term` is part of is used in a way that ignores any trailing suffix of the input it's matched against. + * Not yet implemented for JavaScript. + */ + predicate matchesAnySuffix(RegExpTerm term) { any() } + + /** + * Holds if the regular expression should not be considered. + * + * For javascript we make the pragmatic performance optimization to ignore minified files. + */ + predicate isExcluded(RegExpParent parent) { parent.(Expr).getTopLevel().isMinified() } + + /** + * Holds if `root` has the `i` flag for case-insensitive matching. + */ + predicate isIgnoreCase(RegExpTerm root) { RegExp::isIgnoreCase(getFlags(root)) } + + /** + * Gets the flags for `root`, or the empty string if `root` has no flags. + */ + private string getFlags(RegExpTerm root) { + root.isRootTerm() and + exists(DataFlow::RegExpCreationNode node | node.getRoot() = root | + result = node.getFlags() + or + not exists(node.getFlags()) and + result = "" + ) + or + exists(RegExpPatternSource source | source.getRegExpTerm() = root | + result = source.getARegExpObject().(DataFlow::RegExpCreationNode).getFlags() + ) + } + + /** + * Holds if `root` has the `s` flag for multi-line matching. + */ + predicate isDotAll(RegExpTerm root) { RegExp::isDotAll(getFlags(root)) } +} From e18ceba49e2da2e21f193030317d039825bd907d Mon Sep 17 00:00:00 2001 From: erik-krogh Date: Tue, 1 Nov 2022 10:24:53 +0100 Subject: [PATCH 0291/1420] port the JS regex/redos queries to use the shared pack --- .../javascript/security/BadTagFilterQuery.qll | 156 +- ...leteMultiCharacterSanitizationSpecific.qll | 3 +- .../security/OverlyLargeRangeQuery.qll | 289 +--- .../regexp/ExponentialBackTracking.qll | 285 +--- .../javascript/security/regexp/NfaUtils.qll | 1333 +---------------- .../security/regexp/NfaUtilsSpecific.qll | 70 - .../regexp/PolynomialReDoSCustomizations.qll | 4 +- .../security/regexp/RegexpMatching.qll | 156 +- .../regexp/SuperlinearBackTracking.qll | 385 +---- .../ql/src/Performance/PolynomialReDoS.ql | 1 - javascript/ql/src/Performance/ReDoS.ql | 4 +- .../src/Security/CWE-020/OverlyLargeRange.ql | 5 +- .../ql/src/Security/CWE-116/BadTagFilter.ql | 3 +- .../CWE-178/CaseSensitiveMiddlewarePath.ql | 5 +- .../CWE-400/ReDoS/PolynomialBackTracking.ql | 3 +- 15 files changed, 41 insertions(+), 2661 deletions(-) delete mode 100644 javascript/ql/lib/semmle/javascript/security/regexp/NfaUtilsSpecific.qll diff --git a/javascript/ql/lib/semmle/javascript/security/BadTagFilterQuery.qll b/javascript/ql/lib/semmle/javascript/security/BadTagFilterQuery.qll index 95bfbeeeb5d..428275df487 100644 --- a/javascript/ql/lib/semmle/javascript/security/BadTagFilterQuery.qll +++ b/javascript/ql/lib/semmle/javascript/security/BadTagFilterQuery.qll @@ -2,155 +2,7 @@ * Provides predicates for reasoning about bad tag filter vulnerabilities. */ -import regexp.RegexpMatching - -/** - * Holds if the regexp `root` should be tested against `str`. - * Implements the `isRegexpMatchingCandidateSig` signature from `RegexpMatching`. - * `ignorePrefix` toggles whether the regular expression should be treated as accepting any prefix if it's unanchored. - * `testWithGroups` toggles whether it's tested which groups are filled by a given input string. - */ -private predicate isBadTagFilterCandidate( - RootTerm root, string str, boolean ignorePrefix, boolean testWithGroups -) { - // the regexp must mention "<" and ">" explicitly. - forall(string angleBracket | angleBracket = ["<", ">"] | - any(RegExpConstant term | term.getValue().matches("%" + angleBracket + "%")).getRootTerm() = - root - ) and - ignorePrefix = true and - ( - str = ["", "", "", "", "", - "", "", "", "", - "", "", - "", "", "", - "", "", "", - "", "") and - regexp.matches("") and - not regexp.matches("") and - ( - not regexp.matches("") and - msg = "This regular expression matches , but not " - or - not regexp.matches("") and - msg = "This regular expression matches , but not " - ) - or - regexp.matches("") and - regexp.matches("") and - not regexp.matches("") and - not regexp.matches("") and - msg = "This regular expression does not match script tags where the attribute uses single-quotes." - or - regexp.matches("") and - regexp.matches("") and - not regexp.matches("") and - not regexp.matches("") and - msg = "This regular expression does not match script tags where the attribute uses double-quotes." - or - regexp.matches("") and - regexp.matches("") and - not regexp.matches("") and - not regexp.matches("") and - not regexp.matches("") and - msg = "This regular expression does not match script tags where tabs are used between attributes." - or - regexp.matches("") and - not RegExpFlags::isIgnoreCase(regexp) and - not regexp.matches("") and - not regexp.matches("") and - ( - not regexp.matches("") and - msg = "This regular expression does not match upper case ") and - regexp.matches("") and - msg = "This regular expression does not match mixed case ") and - not regexp.matches("") and - not regexp.matches("") and - ( - not regexp.matches("") and - msg = "This regular expression does not match script end tags like ." - or - not regexp.matches("") and - msg = "This regular expression does not match script end tags like ." - or - not regexp.matches("` + /.*?<\/script>/is, // NOT OK - doesn't match `` + /.*?<\/script[^>]*>/is, // OK + //is, // OK - we don't care regexps that only match comments + /)|([^\/\s>]+)[\S\s]*?>/, // NOT OK - doesn't match comments with the right capture groups + /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))/, // NOT OK - capture groups + /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi, // NOT OK - capture groups + /<(?:(?:!--([\w\W]*?)-->)|(?:!\[CDATA\[([\w\W]*?)\]\]>)|(?:!DOCTYPE([\w\W]*?)>)|(?:\?([^\s\/<>]+) ?([\w\W]*?)[?/]>)|(?:\/([A-Za-z][A-Za-z0-9\-_\:\.]*)>)|(?:([A-Za-z][A-Za-z0-9\-_\:\.]*)((?:\s+[^"'>]+(?:(?:"[^"]*")|(?:'[^']*')|[^>]*))*|\/|\s+)>))/g, // NOT OK - capture groups + /|<([^>]*?)>/g, // NOT OK - capture groups +] + +doFilters(filters) + +var strip = ']*)>([\\S\\s]*?)<\/script([^>]*)>'; // OK - it's used with the ignorecase flag +new RegExp(strip, 'gi'); diff --git a/javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/autogenerated/XssThroughDom/DoubleEscaping/tst.js b/javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/autogenerated/XssThroughDom/DoubleEscaping/tst.js new file mode 100644 index 00000000000..76cfe80b238 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/autogenerated/XssThroughDom/DoubleEscaping/tst.js @@ -0,0 +1,96 @@ +function badEncode(s) { + return s.replace(/"/g, """) + .replace(/'/g, "'") + .replace(/&/g, "&"); +} + +function goodEncode(s) { + return s.replace(/&/g, "&") + .replace(/"/g, """) + .replace(/'/g, "'"); +} + +function goodDecode(s) { + return s.replace(/"/g, "\"") + .replace(/'/g, "'") + .replace(/&/g, "&"); +} + +function badDecode(s) { + return s.replace(/&/g, "&") + .replace(/"/g, "\"") + .replace(/'/g, "'"); +} + +function cleverEncode(code) { + return code.replace(//g, '>').replace(/&(?![\w\#]+;)/g, '&'); +} + +function badDecode2(s) { + return s.replace(/&/g, "&") + .replace(/s?ome|thin*g/g, "else") + .replace(/'/g, "'"); +} + +function goodDecodeInLoop(ss) { + var res = []; + for (var s of ss) { + s = s.replace(/"/g, "\"") + .replace(/'/g, "'") + .replace(/&/g, "&"); + res.push(s); + } + return res; +} + +function badDecode3(s) { + s = s.replace(/&/g, "&"); + s = s.replace(/"/g, "\""); + return s.replace(/'/g, "'"); +} + +function badUnescape(s) { + return s.replace(/\\\\/g, '\\') + .replace(/\\'/g, '\'') + .replace(/\\"/g, '\"'); +} + +function badPercentEscape(s) { + s = s.replace(/&/g, '%26'); + s = s.replace(/%/g, '%25'); + return s; +} + +function badEncode(s) { + var indirect1 = /"/g; + var indirect2 = /'/g; + var indirect3 = /&/g; + return s.replace(indirect1, """) + .replace(indirect2, "'") + .replace(indirect3, "&"); +} + +function badEncodeWithReplacer(s) { + var repl = { + '"': """, + "'": "'", + "&": "&" + }; + return s.replace(/["']/g, (c) => repl[c]).replace(/&/g, "&"); +} + +// dubious, but out of scope for this query +function badRoundtrip(s) { + return s.replace(/\\\\/g, "\\").replace(/\\/g, "\\\\"); +} + +function testWithCapturedVar(x) { + var captured = x; + (function() { + captured = captured.replace(/\\/g, "\\\\"); + })(); +} + +function encodeDecodeEncode(s) { + return goodEncode(goodDecode(goodEncode(s))); +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/autogenerated/XssThroughDom/IncompleteSanitization/UnsafeHtmlExpansion.js b/javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/autogenerated/XssThroughDom/IncompleteSanitization/UnsafeHtmlExpansion.js new file mode 100644 index 00000000000..9d45c9f8f49 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/autogenerated/XssThroughDom/IncompleteSanitization/UnsafeHtmlExpansion.js @@ -0,0 +1,39 @@ +(function(){ + let defaultPattern = /<(([\w:]+)[^>]*)\/>/gi; + let expanded = "<$1>"; + + // lib1 + html.replace( + /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, + expanded + ); // NOT OK + html.replace(/<(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, expanded); // NOT OK + + // lib2 + html.replace( + /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + expanded + ); // NOT OK + html.replace(/<(([\w:]+)[^>]*)\/>/gi, expanded); // NOT OK + + // lib3 + html.replace( + /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi, + expanded + ); // NOT OK + html.replace(/<(([\w:-]+)[^>]*)\/>/gi, expanded); // NOT OK + + html.replace(defaultPattern, expanded); // NOT OK + function getPattern() { + return defaultPattern; + } + html.replace(getPattern(), expanded); // NOT OK + + function getExpanded() { + return expanded; + } + html.replace(defaultPattern, getExpanded()); // NOT OK (but not tracking the expansion string) + html.replace(defaultPattern, something); // OK (possibly) + defaultPattern.match(something); // OK (possibly) + getPattern().match(something); // OK (possibly) +}); diff --git a/javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/autogenerated/XssThroughDom/IncompleteSanitization/tst-multi-character-sanitization.js b/javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/autogenerated/XssThroughDom/IncompleteSanitization/tst-multi-character-sanitization.js new file mode 100644 index 00000000000..43f650acce9 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/autogenerated/XssThroughDom/IncompleteSanitization/tst-multi-character-sanitization.js @@ -0,0 +1,155 @@ +// CVE-2019-10756 +(function(content) { + content = content.replace(/<.*cript.*\/scrip.*>/gi, ""); // NOT OK + content = content.replace(/ on\w+=".*"/g, ""); // NOT OK + content = content.replace(/ on\w+=\'.*\'/g, ""); // NOT OK + return content; +}); +(function(content) { + content = content.replace(/<.*cript.*/gi, ""); // NOT OK + content = content.replace(/.on\w+=.*".*"/g, ""); // NOT OK + content = content.replace(/.on\w+=.*\'.*\'/g, ""); // NOT OK + + return content; +}); + +// CVE-2020-7656 +(function(responseText) { + var rscript = /)<[^<]*)*<\/script>/gi; + responseText.replace(rscript, ""); // NOT OK + return responseText; +}); + +// CVE-2019-1010091 +(function(text) { + text = text.replace(//gm, ""); // NOT OK + x = x.replace(/\sng-[a-z-]+/, ""); // NOT OK + x = x.replace(/\sng-[a-z-]+/g, ""); // NOT OK (ng-attributes) + + x = x.replace(/()/g, "\n"); // OK: not a sanitizer + + x = x.replace(//g, ""); // OK [INCONSISTENCY] + x = x.replace(/