|
8 | 8 | private import codeql.ruby.ast.Literal as AST
|
9 | 9 | private import codeql.Locations
|
10 | 10 | private import codeql.ruby.DataFlow
|
11 |
| -private import codeql.ruby.TaintTracking |
12 |
| -private import codeql.ruby.typetracking.TypeTracker |
| 11 | +private import codeql.ruby.controlflow.CfgNodes |
13 | 12 | private import codeql.ruby.ApiGraphs
|
| 13 | +private import codeql.ruby.dataflow.internal.tainttrackingforlibraries.TaintTrackingImpl |
14 | 14 |
|
15 | 15 | /**
|
16 | 16 | * A `StringlikeLiteral` containing a regular expression term, that is, either
|
@@ -992,30 +992,42 @@ private predicate isInterpretedAsRegExp(DataFlow::Node source) {
|
992 | 992 | // The argument of a call that coerces the argument to a regular expression.
|
993 | 993 | exists(DataFlow::CallNode mce |
|
994 | 994 | mce.getMethodName() = ["match", "match?"] and
|
995 |
| - source = mce.getArgument(0) |
| 995 | + source = mce.getArgument(0) and |
| 996 | + // exclude https://ruby-doc.org/core-2.4.0/Regexp.html#method-i-match |
| 997 | + not mce.getReceiver().asExpr().getExpr() instanceof AST::RegExpLiteral |
996 | 998 | )
|
997 | 999 | }
|
998 | 1000 |
|
999 |
| -/** |
1000 |
| - * Gets a node whose value may flow (inter-procedurally) to `re`, where it is interpreted |
1001 |
| - * as a part of a regular expression. |
1002 |
| - */ |
1003 |
| -private DataFlow::Node regExpSource(DataFlow::Node re, TypeBackTracker t) { |
1004 |
| - t.start() and |
1005 |
| - re = result and |
1006 |
| - isInterpretedAsRegExp(result) |
1007 |
| - or |
1008 |
| - exists(TypeBackTracker t2, DataFlow::Node succ | succ = regExpSource(re, t2) | |
1009 |
| - t2 = t.smallstep(result, succ) |
1010 |
| - or |
1011 |
| - TaintTracking::localTaintStep(result, succ) and |
1012 |
| - t = t2 |
1013 |
| - ) |
| 1001 | +private class RegExpConfiguration extends Configuration { |
| 1002 | + RegExpConfiguration() { this = "RegExpConfiguration" } |
| 1003 | + |
| 1004 | + override predicate isSource(DataFlow::Node source) { |
| 1005 | + source.asExpr() = |
| 1006 | + any(ExprCfgNode e | |
| 1007 | + e.getConstantValue().isString(_) and |
| 1008 | + not e instanceof ExprNodes::VariableReadAccessCfgNode and |
| 1009 | + not e instanceof ExprNodes::ConstantReadAccessCfgNode |
| 1010 | + ) |
| 1011 | + } |
| 1012 | + |
| 1013 | + override predicate isSink(DataFlow::Node sink) { isInterpretedAsRegExp(sink) } |
| 1014 | + |
| 1015 | + override predicate isSanitizer(DataFlow::Node node) { |
| 1016 | + // stop flow if `node` is receiver of |
| 1017 | + // https://ruby-doc.org/core-2.4.0/String.html#method-i-match |
| 1018 | + exists(DataFlow::CallNode mce | |
| 1019 | + mce.getMethodName() = ["match", "match?"] and |
| 1020 | + node = mce.getReceiver() and |
| 1021 | + mce.getArgument(0).asExpr().getExpr() instanceof AST::RegExpLiteral |
| 1022 | + ) |
| 1023 | + } |
1014 | 1024 | }
|
1015 | 1025 |
|
1016 | 1026 | /**
|
1017 | 1027 | * Gets a node whose value may flow (inter-procedurally) to `re`, where it is interpreted
|
1018 | 1028 | * as a part of a regular expression.
|
1019 | 1029 | */
|
1020 | 1030 | cached
|
1021 |
| -DataFlow::Node regExpSource(DataFlow::Node re) { result = regExpSource(re, TypeBackTracker::end()) } |
| 1031 | +DataFlow::Node regExpSource(DataFlow::Node re) { |
| 1032 | + exists(RegExpConfiguration c | c.hasFlow(result, re)) |
| 1033 | +} |
0 commit comments