From 60b0f25a9a145556a896cc07c82926d18e787177 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 9 May 2023 11:03:52 +0200 Subject: [PATCH 1/4] Ruby: Improvements to `RegExpTracking` --- .../lib/codeql/ruby/controlflow/CfgNodes.qll | 6 +- .../internal/TaintTrackingPrivate.qll | 9 +- .../ruby/regexp/internal/RegExpTracking.qll | 221 ++++++++++++------ .../codeql/ruby/typetracking/TypeTracker.qll | 13 +- 4 files changed, 166 insertions(+), 83 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll index 6a5bc217303..96c015a6a4a 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll @@ -936,10 +936,10 @@ module ExprNodes { } /** A control-flow node that wraps a `StringLiteral` AST expression. */ - class StringLiteralCfgNode extends ExprCfgNode { - override string getAPrimaryQlClass() { result = "StringLiteralCfgNode" } + class StringLiteralCfgNode extends StringlikeLiteralCfgNode { + StringLiteralCfgNode() { e instanceof StringLiteral } - override StringLiteral e; + override string getAPrimaryQlClass() { result = "StringLiteralCfgNode" } final override StringLiteral getExpr() { result = super.getExpr() } } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll index c89a629e198..3381187985a 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll @@ -112,6 +112,13 @@ private module Cached { ) } + cached + predicate summaryThroughStepTaint( + DataFlow::Node arg, DataFlow::Node out, FlowSummaryImpl::Public::SummarizedCallable sc + ) { + FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(arg, out, sc) + } + /** * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local * (intra-procedural) step. @@ -122,7 +129,7 @@ private module Cached { defaultAdditionalTaintStep(nodeFrom, nodeTo) or // Simple flow through library code is included in the exposed local // step relation, even though flow is technically inter-procedural - FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(nodeFrom, nodeTo, _) + summaryThroughStepTaint(nodeFrom, nodeTo, _) } } diff --git a/ruby/ql/lib/codeql/ruby/regexp/internal/RegExpTracking.qll b/ruby/ql/lib/codeql/ruby/regexp/internal/RegExpTracking.qll index e787ae358e1..648bc533046 100644 --- a/ruby/ql/lib/codeql/ruby/regexp/internal/RegExpTracking.qll +++ b/ruby/ql/lib/codeql/ruby/regexp/internal/RegExpTracking.qll @@ -21,6 +21,7 @@ private import codeql.ruby.typetracking.TypeTracker private import codeql.ruby.ApiGraphs private import codeql.ruby.Concepts private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate +private import codeql.ruby.dataflow.internal.TaintTrackingPrivate as TaintTrackingPrivate private import codeql.ruby.TaintTracking private import codeql.ruby.frameworks.core.String @@ -37,43 +38,6 @@ DataFlow::LocalSourceNode strStart() { /** Gets a dataflow node for a regular expression literal. */ DataFlow::LocalSourceNode regStart() { result.asExpr().getExpr() instanceof Ast::RegExpLiteral } -/** - * Holds if the analysis should track flow from `nodeFrom` to `nodeTo` on top of the ordinary type-tracking steps. - * `nodeFrom` and `nodeTo` has type `fromType` and `toType` respectively. - * The types are either "string" or "regexp". - */ -predicate step( - DataFlow::Node nodeFrom, DataFlow::LocalSourceNode nodeTo, string fromType, string toType -) { - fromType = toType and - fromType = "string" and - ( - // include taint flow through `String` summaries - TaintTracking::localTaintStep(nodeFrom, nodeTo) and - nodeFrom.(DataFlowPrivate::SummaryNode).getSummarizedCallable() instanceof - String::SummarizedCallable - or - // string concatenations, and - exists(CfgNodes::ExprNodes::OperationCfgNode op | - op = nodeTo.asExpr() and - op.getAnOperand() = nodeFrom.asExpr() and - op.getExpr().(Ast::BinaryOperation).getOperator() = "+" - ) - or - // string interpolations - nodeFrom.asExpr() = - nodeTo.asExpr().(CfgNodes::ExprNodes::StringlikeLiteralCfgNode).getAComponent() - ) - or - fromType = "string" and - toType = "reg" and - exists(DataFlow::CallNode call | - call = API::getTopLevelMember("Regexp").getAMethodCall(["compile", "new"]) and - nodeFrom = call.getArgument(0) and - nodeTo = call - ) -} - /** Gets a node where string values that flow to the node are interpreted as regular expressions. */ DataFlow::Node stringSink() { result instanceof RE::RegExpInterpretation::Range and @@ -91,69 +55,172 @@ DataFlow::Node stringSink() { /** Gets a node where regular expressions that flow to the node are used. */ DataFlow::Node regSink() { result = any(RegexExecution exec).getRegex() } -/** Gets a node that is reachable by type-tracking from any string or regular expression. */ -DataFlow::LocalSourceNode forward(TypeTracker t) { - t.start() and - result = [strStart(), regStart()] - or - exists(TypeTracker t2 | result = forward(t2).track(t2, t)) - or - exists(TypeTracker t2 | t2 = t.continue() | step(forward(t2).getALocalUse(), result, _, _)) +private signature module ReachInputSig { + DataFlow::LocalSourceNode start(TypeTracker t); + + DataFlow::Node end(); + + predicate additionalStep(DataFlow::LocalSourceNode nodeFrom, DataFlow::LocalSourceNode nodeTo); } -/** - * Gets a node that is backwards reachable from any regular expression use, - * where that use is reachable by type-tracking from any string or regular expression. - */ -DataFlow::LocalSourceNode backwards(TypeBackTracker t) { - t.start() and - result.flowsTo([stringSink(), regSink()]) and - result = forward(TypeTracker::end()) - or - exists(TypeBackTracker t2 | result = backwards(t2).backtrack(t2, t)) - or - exists(TypeBackTracker t2 | t2 = t.continue() | step(result.getALocalUse(), backwards(t2), _, _)) +private module Reach { + /** Gets a node that is forwards reachable by type-tracking. */ + pragma[nomagic] + private DataFlow::LocalSourceNode forward(TypeTracker t) { + result = Input::start(t) + or + exists(TypeTracker t2 | result = forward(t2).track(t2, t)) + or + exists(TypeTracker t2 | t2 = t.continue() | Input::additionalStep(forward(t2), result)) + } + + bindingset[result, tbt] + pragma[inline_late] + pragma[noopt] + private DataFlow::LocalSourceNode forwardLateInline(TypeBackTracker tbt) { + exists(TypeTracker tt | + result = forward(tt) and + tt = tbt.getACompatibleTypeTracker() + ) + } + + /** Gets a node that is backwards reachable by type-tracking. */ + pragma[nomagic] + private DataFlow::LocalSourceNode backwards(TypeBackTracker t) { + result = forwardLateInline(t) and + ( + t.start() and + result.flowsTo(Input::end()) + or + exists(TypeBackTracker t2 | result = backwards(t2).backtrack(t2, t)) + or + exists(TypeBackTracker t2 | t2 = t.continue() | Input::additionalStep(result, backwards(t2))) + ) + } + + bindingset[result, tt] + pragma[inline_late] + pragma[noopt] + private DataFlow::LocalSourceNode backwardsInlineLate(TypeTracker tt) { + exists(TypeBackTracker tbt | + result = backwards(tbt) and + tt = tbt.getACompatibleTypeTracker() + ) + } + + pragma[nomagic] + predicate reached(DataFlow::LocalSourceNode n, TypeTracker t) { + n = forward(t) and + n = backwardsInlineLate(t) + } + + pragma[nomagic] + TypeTracker stepReached( + TypeTracker t, DataFlow::LocalSourceNode nodeFrom, DataFlow::LocalSourceNode nodeTo + ) { + exists(StepSummary summary | + StepSummary::step(nodeFrom, nodeTo, summary) and + reached(nodeFrom, t) and + reached(nodeTo, result) and + result = t.append(summary) + ) + or + Input::additionalStep(nodeFrom, nodeTo) and + reached(nodeFrom, pragma[only_bind_into](t)) and + reached(nodeTo, pragma[only_bind_into](t)) and + result = t.continue() + } } +pragma[nomagic] +private predicate regFromString(DataFlow::LocalSourceNode n, DataFlow::CallNode call) { + exists(DataFlow::Node mid | + n.flowsTo(mid) and + call = API::getTopLevelMember("Regexp").getAMethodCall(["compile", "new"]) and + mid = call.getArgument(0) + ) +} + +private module StringReachInput implements ReachInputSig { + DataFlow::LocalSourceNode start(TypeTracker t) { result = strStart() and t.start() } + + DataFlow::Node end() { + result = stringSink() or + regFromString(result, _) + } + + predicate additionalStep(DataFlow::LocalSourceNode nodeFrom, DataFlow::LocalSourceNode nodeTo) { + exists(DataFlow::Node mid | nodeFrom.flowsTo(mid) | + // include taint flow through `String` summaries + TaintTrackingPrivate::summaryThroughStepTaint(mid, nodeTo, any(String::SummarizedCallable c)) + or + // string concatenations, and + exists(CfgNodes::ExprNodes::OperationCfgNode op | + op = nodeTo.asExpr() and + op.getAnOperand() = mid.asExpr() and + op.getExpr().(Ast::BinaryOperation).getOperator() = "+" + ) + or + // string interpolations + mid.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::StringlikeLiteralCfgNode).getAComponent() + ) + } +} + +private module StringReach = Reach; + /** * Gets a node that has been tracked from the string constant `start` to some node. * This is used to figure out where `start` is evaluated as a regular expression against an input string, * or where `start` is compiled into a regular expression. */ private DataFlow::LocalSourceNode trackStrings(DataFlow::Node start, TypeTracker t) { - result = backwards(_) and - ( - t.start() and - start = result and - result = strStart() - or - exists(TypeTracker t2 | result = trackStrings(start, t2).track(t2, t)) - or - // an additional step from string to string - exists(TypeTracker t2 | t2 = t.continue() | - step(trackStrings(start, t2).getALocalUse(), result, "string", "string") - ) - ) + t.start() and + start = result and + result = strStart() and + StringReach::reached(result, t) + or + exists(TypeTracker t2 | t = StringReach::stepReached(t2, trackStrings(start, t2), result)) } +pragma[nomagic] +private predicate regFromStringStart(DataFlow::Node start, TypeTracker t, DataFlow::CallNode nodeTo) { + regFromString(trackStrings(start, t), nodeTo) and + exists(t.continue()) +} + +private module RegReachInput implements ReachInputSig { + DataFlow::LocalSourceNode start(TypeTracker t) { + result = regStart() and + t.start() + or + regFromStringStart(_, t, result) + } + + DataFlow::Node end() { result = regSink() } + + predicate additionalStep(DataFlow::LocalSourceNode nodeFrom, DataFlow::LocalSourceNode nodeTo) { + none() + } +} + +private module RegReach = Reach; + /** * Gets a node that has been tracked from the regular expression `start` to some node. * This is used to figure out where `start` is executed against an input string. */ private DataFlow::LocalSourceNode trackRegs(DataFlow::Node start, TypeTracker t) { - result = backwards(_) and + RegReach::reached(result, t) and ( t.start() and start = result and result = regStart() or - exists(TypeTracker t2 | result = trackRegs(start, t2).track(t2, t)) - or - // an additional step where a string is converted to a regular expression - exists(TypeTracker t2 | t2 = t.continue() | - step(trackStrings(start, t2).getALocalUse(), result, "string", "reg") - ) + regFromStringStart(start, t, result) ) + or + exists(TypeTracker t2 | t = RegReach::stepReached(t2, trackRegs(start, t2), result)) } /** Gets a node that references a regular expression. */ diff --git a/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll b/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll index 52807799c2c..d4d9e1f31f5 100644 --- a/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll +++ b/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll @@ -613,8 +613,17 @@ class TypeBackTracker extends TTypeBackTracker { * also flow to `sink`. */ TypeTracker getACompatibleTypeTracker() { - exists(boolean hasCall | result = MkTypeTracker(hasCall, content) | - hasCall = false or this.hasReturn() = false + exists(boolean hasCall, OptionalTypeTrackerContent c | + result = MkTypeTracker(hasCall, c) and + ( + compatibleContents(c, content) + or + content = noContent() and c = content + ) + | + hasCall = false + or + this.hasReturn() = false ) } } From 211a1e188cb15b0d009879fced378afe8fb73791 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 9 May 2023 15:15:53 +0200 Subject: [PATCH 2/4] Sync files --- .../python/dataflow/new/internal/TypeTracker.qll | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll index 52807799c2c..d4d9e1f31f5 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll @@ -613,8 +613,17 @@ class TypeBackTracker extends TTypeBackTracker { * also flow to `sink`. */ TypeTracker getACompatibleTypeTracker() { - exists(boolean hasCall | result = MkTypeTracker(hasCall, content) | - hasCall = false or this.hasReturn() = false + exists(boolean hasCall, OptionalTypeTrackerContent c | + result = MkTypeTracker(hasCall, c) and + ( + compatibleContents(c, content) + or + content = noContent() and c = content + ) + | + hasCall = false + or + this.hasReturn() = false ) } } From 51087d090b493810e2b634f201237c0cd412c661 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 10 May 2023 09:42:41 +0200 Subject: [PATCH 3/4] Address review comments --- .../codeql/ruby/regexp/internal/RegExpTracking.qll | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/regexp/internal/RegExpTracking.qll b/ruby/ql/lib/codeql/ruby/regexp/internal/RegExpTracking.qll index 648bc533046..9df923e2d86 100644 --- a/ruby/ql/lib/codeql/ruby/regexp/internal/RegExpTracking.qll +++ b/ruby/ql/lib/codeql/ruby/regexp/internal/RegExpTracking.qll @@ -108,6 +108,7 @@ private module Reach { ) } + /** Holds if `n` is forwards and backwards reachable with type tracker `t`. */ pragma[nomagic] predicate reached(DataFlow::LocalSourceNode n, TypeTracker t) { n = forward(t) and @@ -132,10 +133,11 @@ private module Reach { } } +/** Holds if `inputStr` is compiled to a regular expression that is returned at `call`. */ pragma[nomagic] -private predicate regFromString(DataFlow::LocalSourceNode n, DataFlow::CallNode call) { +private predicate regFromString(DataFlow::LocalSourceNode inputStr, DataFlow::CallNode call) { exists(DataFlow::Node mid | - n.flowsTo(mid) and + inputStr.flowsTo(mid) and call = API::getTopLevelMember("Regexp").getAMethodCall(["compile", "new"]) and mid = call.getArgument(0) ) @@ -183,9 +185,10 @@ private DataFlow::LocalSourceNode trackStrings(DataFlow::Node start, TypeTracker exists(TypeTracker t2 | t = StringReach::stepReached(t2, trackStrings(start, t2), result)) } +/** Holds if `strConst` flows to a regex compilation (tracked by `t`), where the resulting regular expression is stored in `reg`. */ pragma[nomagic] -private predicate regFromStringStart(DataFlow::Node start, TypeTracker t, DataFlow::CallNode nodeTo) { - regFromString(trackStrings(start, t), nodeTo) and +private predicate regFromStringStart(DataFlow::Node strConst, TypeTracker t, DataFlow::CallNode reg) { + regFromString(trackStrings(strConst, t), reg) and exists(t.continue()) } From 425ebba2783a80ec57f6a13da5517e4161406807 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 10 May 2023 14:04:41 +0200 Subject: [PATCH 4/4] Address review comments --- .../ruby/regexp/internal/RegExpTracking.qll | 128 +++++++++--------- 1 file changed, 63 insertions(+), 65 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/regexp/internal/RegExpTracking.qll b/ruby/ql/lib/codeql/ruby/regexp/internal/RegExpTracking.qll index 9df923e2d86..d4f8f17db34 100644 --- a/ruby/ql/lib/codeql/ruby/regexp/internal/RegExpTracking.qll +++ b/ruby/ql/lib/codeql/ruby/regexp/internal/RegExpTracking.qll @@ -55,23 +55,33 @@ DataFlow::Node stringSink() { /** Gets a node where regular expressions that flow to the node are used. */ DataFlow::Node regSink() { result = any(RegexExecution exec).getRegex() } -private signature module ReachInputSig { - DataFlow::LocalSourceNode start(TypeTracker t); +private signature module TypeTrackInputSig { + DataFlow::LocalSourceNode start(TypeTracker t, DataFlow::Node start); - DataFlow::Node end(); + predicate end(DataFlow::Node n); - predicate additionalStep(DataFlow::LocalSourceNode nodeFrom, DataFlow::LocalSourceNode nodeTo); + predicate additionalStep(DataFlow::Node nodeFrom, DataFlow::LocalSourceNode nodeTo); } -private module Reach { +/** + * Provides a version of type tracking where we first prune for reachable nodes, + * before doing the type tracking computation. + */ +private module TypeTrack { + private predicate additionalStep( + DataFlow::LocalSourceNode nodeFrom, DataFlow::LocalSourceNode nodeTo + ) { + Input::additionalStep(nodeFrom.getALocalUse(), nodeTo) + } + /** Gets a node that is forwards reachable by type-tracking. */ pragma[nomagic] private DataFlow::LocalSourceNode forward(TypeTracker t) { - result = Input::start(t) + result = Input::start(t, _) or exists(TypeTracker t2 | result = forward(t2).track(t2, t)) or - exists(TypeTracker t2 | t2 = t.continue() | Input::additionalStep(forward(t2), result)) + exists(TypeTracker t2 | t2 = t.continue() | additionalStep(forward(t2), result)) } bindingset[result, tbt] @@ -90,11 +100,11 @@ private module Reach { result = forwardLateInline(t) and ( t.start() and - result.flowsTo(Input::end()) + Input::end(result.getALocalUse()) or exists(TypeBackTracker t2 | result = backwards(t2).backtrack(t2, t)) or - exists(TypeBackTracker t2 | t2 = t.continue() | Input::additionalStep(result, backwards(t2))) + exists(TypeBackTracker t2 | t2 = t.continue() | additionalStep(result, backwards(t2))) ) } @@ -110,13 +120,13 @@ private module Reach { /** Holds if `n` is forwards and backwards reachable with type tracker `t`. */ pragma[nomagic] - predicate reached(DataFlow::LocalSourceNode n, TypeTracker t) { + private predicate reached(DataFlow::LocalSourceNode n, TypeTracker t) { n = forward(t) and n = backwardsInlineLate(t) } pragma[nomagic] - TypeTracker stepReached( + private TypeTracker stepReached( TypeTracker t, DataFlow::LocalSourceNode nodeFrom, DataFlow::LocalSourceNode nodeTo ) { exists(StepSummary summary | @@ -126,11 +136,20 @@ private module Reach { result = t.append(summary) ) or - Input::additionalStep(nodeFrom, nodeTo) and + additionalStep(nodeFrom, nodeTo) and reached(nodeFrom, pragma[only_bind_into](t)) and reached(nodeTo, pragma[only_bind_into](t)) and result = t.continue() } + + /** Gets a node that has been tracked from the start node `start`. */ + DataFlow::LocalSourceNode track(DataFlow::Node start, TypeTracker t) { + t.start() and + result = Input::start(t, start) and + reached(result, t) + or + exists(TypeTracker t2 | t = stepReached(t2, track(start, t2), result)) + } } /** Holds if `inputStr` is compiled to a regular expression that is returned at `call`. */ @@ -143,47 +162,40 @@ private predicate regFromString(DataFlow::LocalSourceNode inputStr, DataFlow::Ca ) } -private module StringReachInput implements ReachInputSig { - DataFlow::LocalSourceNode start(TypeTracker t) { result = strStart() and t.start() } - - DataFlow::Node end() { - result = stringSink() or - regFromString(result, _) +private module StringTypeTrackInput implements TypeTrackInputSig { + DataFlow::LocalSourceNode start(TypeTracker t, DataFlow::Node start) { + start = strStart() and t.start() and result = start } - predicate additionalStep(DataFlow::LocalSourceNode nodeFrom, DataFlow::LocalSourceNode nodeTo) { - exists(DataFlow::Node mid | nodeFrom.flowsTo(mid) | - // include taint flow through `String` summaries - TaintTrackingPrivate::summaryThroughStepTaint(mid, nodeTo, any(String::SummarizedCallable c)) - or - // string concatenations, and - exists(CfgNodes::ExprNodes::OperationCfgNode op | - op = nodeTo.asExpr() and - op.getAnOperand() = mid.asExpr() and - op.getExpr().(Ast::BinaryOperation).getOperator() = "+" - ) - or - // string interpolations - mid.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::StringlikeLiteralCfgNode).getAComponent() + predicate end(DataFlow::Node n) { + n = stringSink() or + regFromString(n, _) + } + + predicate additionalStep(DataFlow::Node nodeFrom, DataFlow::LocalSourceNode nodeTo) { + // include taint flow through `String` summaries + TaintTrackingPrivate::summaryThroughStepTaint(nodeFrom, nodeTo, + any(String::SummarizedCallable c)) + or + // string concatenations, and + exists(CfgNodes::ExprNodes::OperationCfgNode op | + op = nodeTo.asExpr() and + op.getAnOperand() = nodeFrom.asExpr() and + op.getExpr().(Ast::BinaryOperation).getOperator() = "+" ) + or + // string interpolations + nodeFrom.asExpr() = + nodeTo.asExpr().(CfgNodes::ExprNodes::StringlikeLiteralCfgNode).getAComponent() } } -private module StringReach = Reach; - /** * Gets a node that has been tracked from the string constant `start` to some node. * This is used to figure out where `start` is evaluated as a regular expression against an input string, * or where `start` is compiled into a regular expression. */ -private DataFlow::LocalSourceNode trackStrings(DataFlow::Node start, TypeTracker t) { - t.start() and - start = result and - result = strStart() and - StringReach::reached(result, t) - or - exists(TypeTracker t2 | t = StringReach::stepReached(t2, trackStrings(start, t2), result)) -} +private predicate trackStrings = TypeTrack::track/2; /** Holds if `strConst` flows to a regex compilation (tracked by `t`), where the resulting regular expression is stored in `reg`. */ pragma[nomagic] @@ -192,39 +204,25 @@ private predicate regFromStringStart(DataFlow::Node strConst, TypeTracker t, Dat exists(t.continue()) } -private module RegReachInput implements ReachInputSig { - DataFlow::LocalSourceNode start(TypeTracker t) { - result = regStart() and - t.start() +private module RegTypeTrackInput implements TypeTrackInputSig { + DataFlow::LocalSourceNode start(TypeTracker t, DataFlow::Node start) { + start = regStart() and + t.start() and + result = start or - regFromStringStart(_, t, result) + regFromStringStart(start, t, result) } - DataFlow::Node end() { result = regSink() } + predicate end(DataFlow::Node n) { n = regSink() } - predicate additionalStep(DataFlow::LocalSourceNode nodeFrom, DataFlow::LocalSourceNode nodeTo) { - none() - } + predicate additionalStep(DataFlow::Node nodeFrom, DataFlow::LocalSourceNode nodeTo) { none() } } -private module RegReach = Reach; - /** * Gets a node that has been tracked from the regular expression `start` to some node. * This is used to figure out where `start` is executed against an input string. */ -private DataFlow::LocalSourceNode trackRegs(DataFlow::Node start, TypeTracker t) { - RegReach::reached(result, t) and - ( - t.start() and - start = result and - result = regStart() - or - regFromStringStart(start, t, result) - ) - or - exists(TypeTracker t2 | t = RegReach::stepReached(t2, trackRegs(start, t2), result)) -} +private predicate trackRegs = TypeTrack::track/2; /** Gets a node that references a regular expression. */ private DataFlow::LocalSourceNode trackRegexpType(TypeTracker t) {