mirror of
https://github.com/github/codeql.git
synced 2026-05-02 04:05:14 +02:00
Merge pull request #7568 from aibaars/ruby-pattern-matching-taint
Ruby: taint steps for pattern matches
This commit is contained in:
@@ -97,7 +97,10 @@ private class TPattern =
|
||||
* ```
|
||||
*/
|
||||
class CasePattern extends AstNode, TPattern {
|
||||
CasePattern() { casePattern(toGenerated(this)) }
|
||||
CasePattern() {
|
||||
casePattern(toGenerated(this)) or
|
||||
synthChild(any(HashPattern p), _, this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,7 +210,7 @@ class FindPattern extends CasePattern, TFindPattern {
|
||||
CasePattern getAnElement() { result = this.getElement(_) }
|
||||
|
||||
/**
|
||||
* Gets the variable for the prefix of this list pattern, if any. For example `init` in:
|
||||
* Gets the variable for the prefix of this find pattern, if any. For example `init` in:
|
||||
* ```rb
|
||||
* in List[*init, "a", Integer => x, *tail]
|
||||
* ```
|
||||
@@ -217,7 +220,7 @@ class FindPattern extends CasePattern, TFindPattern {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the variable for the suffix of this list pattern, if any. For example `tail` in:
|
||||
* Gets the variable for the suffix of this find pattern, if any. For example `tail` in:
|
||||
* ```rb
|
||||
* in List[*init, "a", Integer => x, *tail]
|
||||
* ```
|
||||
@@ -267,7 +270,10 @@ class HashPattern extends CasePattern, THashPattern {
|
||||
StringlikeLiteral getKey(int n) { toGenerated(result) = this.keyValuePair(n).getKey() }
|
||||
|
||||
/** Gets the value of the `n`th pair. */
|
||||
CasePattern getValue(int n) { toGenerated(result) = this.keyValuePair(n).getValue() }
|
||||
CasePattern getValue(int n) {
|
||||
toGenerated(result) = this.keyValuePair(n).getValue() or
|
||||
synthChild(this, n, result)
|
||||
}
|
||||
|
||||
/** Gets the value for a given key name. */
|
||||
CasePattern getValueByKey(string key) {
|
||||
@@ -383,6 +389,7 @@ class ParenthesizedPattern extends CasePattern, TParenthesizedPattern {
|
||||
|
||||
ParenthesizedPattern() { this = TParenthesizedPattern(g) }
|
||||
|
||||
/** Gets the underlying pattern. */
|
||||
final CasePattern getPattern() { toGenerated(result) = g.getChild() }
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "ParenthesizedPattern" }
|
||||
|
||||
@@ -115,6 +115,8 @@ class VariableAccess extends Expr instanceof VariableAccessImpl {
|
||||
implicitWriteAccess(toGenerated(this))
|
||||
or
|
||||
this = any(SimpleParameterSynthImpl p).getDefininingAccess()
|
||||
or
|
||||
this = any(HashPattern p).getValue(_)
|
||||
}
|
||||
|
||||
final override string toString() { result = VariableAccessImpl.super.toString() }
|
||||
|
||||
@@ -7,6 +7,7 @@ private import codeql.ruby.ast.internal.Expr
|
||||
private import codeql.ruby.ast.internal.Variable
|
||||
private import codeql.ruby.ast.internal.Pattern
|
||||
private import codeql.ruby.ast.internal.Scope
|
||||
private import codeql.ruby.ast.internal.TreeSitter
|
||||
private import codeql.ruby.AST
|
||||
|
||||
/** A synthesized AST node kind. */
|
||||
@@ -921,3 +922,35 @@ private module ForLoopDesugar {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ```rb
|
||||
* { a: }
|
||||
* ```
|
||||
* desugars to,
|
||||
* ```rb
|
||||
* { a: a }
|
||||
* ```
|
||||
*/
|
||||
private module ImplicitHashValueSynthesis {
|
||||
private Ruby::AstNode keyWithoutValue(HashPattern parent, int i) {
|
||||
exists(Ruby::KeywordPattern pair |
|
||||
result = pair.getKey() and
|
||||
result = toGenerated(parent.getKey(i)) and
|
||||
not exists(pair.getValue())
|
||||
)
|
||||
}
|
||||
|
||||
private class ImplicitHashValueSynthesis extends Synthesis {
|
||||
final override predicate child(AstNode parent, int i, Child child) {
|
||||
exists(TVariableReal variable |
|
||||
access(keyWithoutValue(parent, i), variable) and
|
||||
child = SynthChild(LocalVariableAccessRealKind(variable))
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate location(AstNode n, Location l) {
|
||||
exists(HashPattern p, int i | n = p.getValue(i) and l = keyWithoutValue(p, i).getLocation())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ predicate implicitAssignmentNode(Ruby::AstNode n) {
|
||||
or
|
||||
n = any(Ruby::HashPattern parent).getChild(_).(Ruby::HashSplatParameter).getName()
|
||||
or
|
||||
n = any(Ruby::KeywordPattern parent | not exists(parent.getValue())).getKey()
|
||||
or
|
||||
n = any(Ruby::ExceptionVariable ev).getChild()
|
||||
or
|
||||
n = any(Ruby::For for).getPattern()
|
||||
@@ -104,11 +106,23 @@ private predicate scopeDefinesParameterVariable(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private string variableNameInScope(Ruby::AstNode i, Scope::Range scope) {
|
||||
scope = scopeOf(i) and
|
||||
(
|
||||
result = i.(Ruby::Identifier).getValue()
|
||||
or
|
||||
exists(Ruby::KeywordPattern p | i = p.getKey() and not exists(p.getValue()) |
|
||||
result = i.(Ruby::String).getChild(0).(Ruby::StringContent).getValue() or
|
||||
result = i.(Ruby::HashKeySymbol).getValue()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `name` is assigned in `scope` at `i`. */
|
||||
private predicate scopeAssigns(Scope::Range scope, string name, Ruby::Identifier i) {
|
||||
private predicate scopeAssigns(Scope::Range scope, string name, Ruby::AstNode i) {
|
||||
(explicitAssignmentNode(i, _) or implicitAssignmentNode(i)) and
|
||||
name = i.getValue() and
|
||||
scope = scopeOf(i)
|
||||
name = variableNameInScope(i, scope)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -132,11 +146,11 @@ private module Cached {
|
||||
other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn()
|
||||
)
|
||||
} or
|
||||
TLocalVariableReal(Scope::Range scope, string name, Ruby::Identifier i) {
|
||||
TLocalVariableReal(Scope::Range scope, string name, Ruby::AstNode i) {
|
||||
scopeDefinesParameterVariable(scope, name, i)
|
||||
or
|
||||
i =
|
||||
min(Ruby::Identifier other |
|
||||
min(Ruby::AstNode other |
|
||||
scopeAssigns(scope, name, other)
|
||||
|
|
||||
other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn()
|
||||
@@ -295,13 +309,18 @@ private module Cached {
|
||||
i = any(Ruby::WhileModifier x).getBody()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasScopeAndName(VariableReal variable, Scope::Range scope, string name) {
|
||||
variable.getNameImpl() = name and
|
||||
scope = variable.getDeclaringScopeImpl()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate access(Ruby::Identifier access, VariableReal variable) {
|
||||
exists(string name |
|
||||
variable.getNameImpl() = name and
|
||||
name = access.getValue()
|
||||
predicate access(Ruby::AstNode access, VariableReal variable) {
|
||||
exists(string name, Scope::Range scope |
|
||||
pragma[only_bind_into](name) = variableNameInScope(access, scope)
|
||||
|
|
||||
variable.getDeclaringScopeImpl() = scopeOf(access) and
|
||||
hasScopeAndName(variable, scope, name) and
|
||||
not access.getLocation().strictlyBefore(variable.getLocationImpl()) and
|
||||
// In case of overlapping parameter names, later parameters should not
|
||||
// be considered accesses to the first parameter
|
||||
@@ -310,15 +329,15 @@ private module Cached {
|
||||
else any()
|
||||
or
|
||||
exists(Scope::Range declScope |
|
||||
variable.getDeclaringScopeImpl() = declScope and
|
||||
inherits(scopeOf(access), name, declScope)
|
||||
hasScopeAndName(variable, declScope, pragma[only_bind_into](name)) and
|
||||
inherits(scope, name, declScope)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private class Access extends Ruby::Token {
|
||||
Access() {
|
||||
access(this, _) or
|
||||
access(this.(Ruby::Identifier), _) or
|
||||
this instanceof Ruby::GlobalVariable or
|
||||
this instanceof Ruby::InstanceVariable or
|
||||
this instanceof Ruby::ClassVariable or
|
||||
@@ -371,7 +390,7 @@ private predicate inherits(Scope::Range scope, string name, Scope::Range outer)
|
||||
(
|
||||
scopeDefinesParameterVariable(outer, name, _)
|
||||
or
|
||||
exists(Ruby::Identifier i |
|
||||
exists(Ruby::AstNode i |
|
||||
scopeAssigns(outer, name, i) and
|
||||
i.getLocation().strictlyBefore(scope.getLocation())
|
||||
)
|
||||
@@ -420,7 +439,7 @@ private class VariableRealAdapter extends VariableImpl, TVariableReal instanceof
|
||||
class LocalVariableReal extends VariableReal, TLocalVariableReal {
|
||||
private Scope::Range scope;
|
||||
private string name;
|
||||
private Ruby::Identifier i;
|
||||
private Ruby::AstNode i;
|
||||
|
||||
LocalVariableReal() { this = TLocalVariableReal(scope, name, i) }
|
||||
|
||||
|
||||
@@ -66,16 +66,16 @@ class ExitNode extends CfgNode, TExitNode {
|
||||
*/
|
||||
class AstCfgNode extends CfgNode, TElementNode {
|
||||
private Splits splits;
|
||||
private AstNode n;
|
||||
AstNode e;
|
||||
|
||||
AstCfgNode() { this = TElementNode(_, n, splits) }
|
||||
AstCfgNode() { this = TElementNode(_, e, splits) }
|
||||
|
||||
final override AstNode getNode() { result = n }
|
||||
final override AstNode getNode() { result = e }
|
||||
|
||||
override Location getLocation() { result = n.getLocation() }
|
||||
override Location getLocation() { result = e.getLocation() }
|
||||
|
||||
final override string toString() {
|
||||
exists(string s | s = n.toString() |
|
||||
exists(string s | s = e.toString() |
|
||||
result = "[" + this.getSplitsString() + "] " + s
|
||||
or
|
||||
not exists(this.getSplitsString()) and result = s
|
||||
@@ -94,7 +94,7 @@ class AstCfgNode extends CfgNode, TElementNode {
|
||||
|
||||
/** A control-flow node that wraps an AST expression. */
|
||||
class ExprCfgNode extends AstCfgNode {
|
||||
Expr e;
|
||||
override Expr e;
|
||||
|
||||
ExprCfgNode() { e = this.getNode() }
|
||||
|
||||
@@ -159,7 +159,7 @@ private AstNode desugar(AstNode n) {
|
||||
/**
|
||||
* A class for mapping parent-child AST nodes to parent-child CFG nodes.
|
||||
*/
|
||||
abstract private class ExprChildMapping extends Expr {
|
||||
abstract private class ChildMapping extends AstNode {
|
||||
/**
|
||||
* Holds if `child` is a (possibly nested) child of this expression
|
||||
* for which we would like to find a matching CFG child.
|
||||
@@ -167,17 +167,7 @@ abstract private class ExprChildMapping extends Expr {
|
||||
abstract predicate relevantChild(AstNode child);
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachesBasicBlock(AstNode child, CfgNode cfn, BasicBlock bb) {
|
||||
this.relevantChild(child) and
|
||||
cfn = this.getAControlFlowNode() and
|
||||
bb.getANode() = cfn
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
this.reachesBasicBlock(child, cfn, mid) and
|
||||
bb = mid.getAPredecessor() and
|
||||
not mid.getANode().getNode() = child
|
||||
)
|
||||
}
|
||||
abstract predicate reachesBasicBlock(AstNode child, CfgNode cfn, BasicBlock bb);
|
||||
|
||||
/**
|
||||
* Holds if there is a control-flow path from `cfn` to `cfnChild`, where `cfn`
|
||||
@@ -193,6 +183,44 @@ abstract private class ExprChildMapping extends Expr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class for mapping parent-child AST nodes to parent-child CFG nodes.
|
||||
*/
|
||||
abstract private class ExprChildMapping extends Expr, ChildMapping {
|
||||
pragma[nomagic]
|
||||
override predicate reachesBasicBlock(AstNode child, CfgNode cfn, BasicBlock bb) {
|
||||
this.relevantChild(child) and
|
||||
cfn = this.getAControlFlowNode() and
|
||||
bb.getANode() = cfn
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
this.reachesBasicBlock(child, cfn, mid) and
|
||||
bb = mid.getAPredecessor() and
|
||||
not mid.getANode().getNode() = child
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class for mapping parent-child AST nodes to parent-child CFG nodes.
|
||||
*/
|
||||
abstract private class NonExprChildMapping extends ChildMapping {
|
||||
NonExprChildMapping() { not this instanceof Expr }
|
||||
|
||||
pragma[nomagic]
|
||||
override predicate reachesBasicBlock(AstNode child, CfgNode cfn, BasicBlock bb) {
|
||||
this.relevantChild(child) and
|
||||
cfn.getNode() = this and
|
||||
bb.getANode() = cfn
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
this.reachesBasicBlock(child, cfn, mid) and
|
||||
bb = mid.getASuccessor() and
|
||||
not mid.getANode().getNode() = child
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides classes for control-flow nodes that wrap AST expressions. */
|
||||
module ExprNodes {
|
||||
private class LiteralChildMapping extends ExprChildMapping, Literal {
|
||||
@@ -475,10 +503,6 @@ module ExprNodes {
|
||||
final ExprCfgNode getBlock() { e.hasCfgChild(e.(MethodCall).getBlock(), this, result) }
|
||||
}
|
||||
|
||||
private class CaseExprChildMapping extends ExprChildMapping, CaseExpr {
|
||||
override predicate relevantChild(AstNode e) { e = this.getValue() }
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps a `MethodCall` AST expression. */
|
||||
class MethodCallCfgNode extends CallCfgNode {
|
||||
MethodCallCfgNode() { super.getExpr() instanceof MethodCall }
|
||||
@@ -486,6 +510,10 @@ module ExprNodes {
|
||||
override MethodCall getExpr() { result = super.getExpr() }
|
||||
}
|
||||
|
||||
private class CaseExprChildMapping extends ExprChildMapping, CaseExpr {
|
||||
override predicate relevantChild(AstNode e) { e = this.getValue() or e = this.getABranch() }
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps a `CaseExpr` AST expression. */
|
||||
class CaseExprCfgNode extends ExprCfgNode {
|
||||
override CaseExprChildMapping e;
|
||||
@@ -494,6 +522,169 @@ module ExprNodes {
|
||||
|
||||
/** Gets the expression being compared, if any. */
|
||||
final ExprCfgNode getValue() { e.hasCfgChild(e.getValue(), this, result) }
|
||||
|
||||
/**
|
||||
* Gets the `n`th branch of this case expression, either a `when` clause, an `in` clause, or an `else` branch.
|
||||
*/
|
||||
final AstCfgNode getBranch(int n) { e.hasCfgChild(e.getBranch(n), this, result) }
|
||||
}
|
||||
|
||||
private class InClauseChildMapping extends NonExprChildMapping, InClause {
|
||||
override predicate relevantChild(AstNode e) {
|
||||
e = this.getPattern() or
|
||||
e = this.getCondition() or
|
||||
e = this.getBody()
|
||||
}
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps an `InClause` AST expression. */
|
||||
class InClauseCfgNode extends AstCfgNode {
|
||||
override InClauseChildMapping e;
|
||||
|
||||
/** Gets the pattern in this `in`-clause. */
|
||||
final AstCfgNode getPattern() { e.hasCfgChild(e.getPattern(), this, result) }
|
||||
|
||||
/** Gets the pattern guard condition in this `in` clause, if any. */
|
||||
final ExprCfgNode getCondition() { e.hasCfgChild(e.getCondition(), this, result) }
|
||||
|
||||
/** Gets the body of this `in`-clause. */
|
||||
final ExprCfgNode getBody() { e.hasCfgChild(e.getBody(), this, result) }
|
||||
}
|
||||
|
||||
private class WhenClauseChildMapping extends NonExprChildMapping, WhenClause {
|
||||
override predicate relevantChild(AstNode e) { e = this.getBody() }
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps a `WhenClause` AST expression. */
|
||||
class WhenClauseCfgNode extends AstCfgNode {
|
||||
override WhenClauseChildMapping e;
|
||||
|
||||
/** Gets the body of this `when`-clause. */
|
||||
final ExprCfgNode getBody() { e.hasCfgChild(e.getBody(), this, result) }
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps a `CasePattern`. */
|
||||
class CasePatternCfgNode extends AstCfgNode {
|
||||
override CasePattern e;
|
||||
}
|
||||
|
||||
private class ArrayPatternChildMapping extends NonExprChildMapping, ArrayPattern {
|
||||
override predicate relevantChild(AstNode e) {
|
||||
e = this.getPrefixElement(_) or
|
||||
e = this.getSuffixElement(_) or
|
||||
e = this.getRestVariableAccess()
|
||||
}
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps an `ArrayPattern` node. */
|
||||
class ArrayPatternCfgNode extends CasePatternCfgNode {
|
||||
override ArrayPatternChildMapping e;
|
||||
|
||||
/** Gets the `n`th element of this list pattern's prefix. */
|
||||
final CasePatternCfgNode getPrefixElement(int n) {
|
||||
e.hasCfgChild(e.getPrefixElement(n), this, result)
|
||||
}
|
||||
|
||||
/** Gets the `n`th element of this list pattern's suffix. */
|
||||
final CasePatternCfgNode getSuffixElement(int n) {
|
||||
e.hasCfgChild(e.getSuffixElement(n), this, result)
|
||||
}
|
||||
|
||||
/** Gets the variable of the rest token, if any. */
|
||||
final VariableWriteAccessCfgNode getRestVariableAccess() {
|
||||
e.hasCfgChild(e.getRestVariableAccess(), this, result)
|
||||
}
|
||||
}
|
||||
|
||||
private class FindPatternChildMapping extends NonExprChildMapping, FindPattern {
|
||||
override predicate relevantChild(AstNode e) {
|
||||
e = this.getElement(_) or
|
||||
e = this.getPrefixVariableAccess() or
|
||||
e = this.getSuffixVariableAccess()
|
||||
}
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps a `FindPattern` node. */
|
||||
class FindPatternCfgNode extends CasePatternCfgNode {
|
||||
override FindPatternChildMapping e;
|
||||
|
||||
/** Gets the `n`th element of this find pattern. */
|
||||
final CasePatternCfgNode getElement(int n) { e.hasCfgChild(e.getElement(n), this, result) }
|
||||
|
||||
/** Gets the variable for the prefix of this find pattern, if any. */
|
||||
final VariableWriteAccessCfgNode getPrefixVariableAccess() {
|
||||
e.hasCfgChild(e.getPrefixVariableAccess(), this, result)
|
||||
}
|
||||
|
||||
/** Gets the variable for the suffix of this find pattern, if any. */
|
||||
final VariableWriteAccessCfgNode getSuffixVariableAccess() {
|
||||
e.hasCfgChild(e.getSuffixVariableAccess(), this, result)
|
||||
}
|
||||
}
|
||||
|
||||
private class HashPatternChildMapping extends NonExprChildMapping, HashPattern {
|
||||
override predicate relevantChild(AstNode e) {
|
||||
e = this.getValue(_) or
|
||||
e = this.getRestVariableAccess()
|
||||
}
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps a `HashPattern` node. */
|
||||
class HashPatternCfgNode extends CasePatternCfgNode {
|
||||
override HashPatternChildMapping e;
|
||||
|
||||
/** Gets the value of the `n`th pair. */
|
||||
final CasePatternCfgNode getValue(int n) { e.hasCfgChild(e.getValue(n), this, result) }
|
||||
|
||||
/** Gets the variable of the keyword rest token, if any. */
|
||||
final VariableWriteAccessCfgNode getRestVariableAccess() {
|
||||
e.hasCfgChild(e.getRestVariableAccess(), this, result)
|
||||
}
|
||||
}
|
||||
|
||||
private class AlternativePatternChildMapping extends NonExprChildMapping, AlternativePattern {
|
||||
override predicate relevantChild(AstNode e) { e = this.getAnAlternative() }
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps an `AlternativePattern` node. */
|
||||
class AlternativePatternCfgNode extends CasePatternCfgNode {
|
||||
override AlternativePatternChildMapping e;
|
||||
|
||||
/** Gets the `n`th alternative. */
|
||||
final CasePatternCfgNode getAlternative(int n) {
|
||||
e.hasCfgChild(e.getAlternative(n), this, result)
|
||||
}
|
||||
}
|
||||
|
||||
private class AsPatternChildMapping extends NonExprChildMapping, AsPattern {
|
||||
override predicate relevantChild(AstNode e) {
|
||||
e = this.getPattern() or e = this.getVariableAccess()
|
||||
}
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps an `AsPattern` node. */
|
||||
class AsPatternCfgNode extends CasePatternCfgNode {
|
||||
override AsPatternChildMapping e;
|
||||
|
||||
/** Gets the underlying pattern. */
|
||||
final CasePatternCfgNode getPattern() { e.hasCfgChild(e.getPattern(), this, result) }
|
||||
|
||||
/** Gets the variable access for this pattern. */
|
||||
final VariableWriteAccessCfgNode getVariableAccess() {
|
||||
e.hasCfgChild(e.getVariableAccess(), this, result)
|
||||
}
|
||||
}
|
||||
|
||||
private class ParenthesizedPatternChildMapping extends NonExprChildMapping, ParenthesizedPattern {
|
||||
override predicate relevantChild(AstNode e) { e = this.getPattern() }
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps a `ParenthesizedPattern` node. */
|
||||
class ParenthesizedPatternCfgNode extends CasePatternCfgNode {
|
||||
override ParenthesizedPatternChildMapping e;
|
||||
|
||||
/** Gets the underlying pattern. */
|
||||
final CasePatternCfgNode getPattern() { e.hasCfgChild(e.getPattern(), this, result) }
|
||||
}
|
||||
|
||||
private class ConditionalExprChildMapping extends ExprChildMapping, ConditionalExpr {
|
||||
@@ -593,6 +784,13 @@ module ExprNodes {
|
||||
final override VariableReadAccess getExpr() { result = ExprCfgNode.super.getExpr() }
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps a `VariableWriteAccess` AST expression. */
|
||||
class VariableWriteAccessCfgNode extends ExprCfgNode {
|
||||
override VariableWriteAccess e;
|
||||
|
||||
final override VariableWriteAccess getExpr() { result = ExprCfgNode.super.getExpr() }
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps a `InstanceVariableWriteAccess` AST expression. */
|
||||
class InstanceVariableWriteAccessCfgNode extends ExprCfgNode {
|
||||
override InstanceVariableWriteAccess e;
|
||||
|
||||
@@ -711,7 +711,9 @@ module Trees {
|
||||
last(this.getPattern(), last, c) and
|
||||
c.(MatchingCompletion).getValue() = false
|
||||
or
|
||||
last(this.getVariableAccess(), last, c)
|
||||
last(this.getVariableAccess(), last, any(SimpleCompletion x)) and
|
||||
c.(MatchingCompletion).getValue() = true and
|
||||
not c instanceof NestedCompletion
|
||||
}
|
||||
|
||||
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
|
||||
|
||||
@@ -126,15 +126,14 @@ module LocalFlow {
|
||||
or
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::ConditionalExprCfgNode).getBranch(_)
|
||||
or
|
||||
exists(CfgNode n, Stmt stmt, CaseExpr c |
|
||||
c = nodeTo.asExpr().getExpr() and
|
||||
n = nodeFrom.asExpr() and
|
||||
n = nodeTo.asExpr().getAPredecessor() and
|
||||
stmt = n.getNode()
|
||||
exists(CfgNodes::AstCfgNode branch |
|
||||
branch = nodeTo.asExpr().(CfgNodes::ExprNodes::CaseExprCfgNode).getBranch(_)
|
||||
|
|
||||
stmt = c.getElseBranch() or
|
||||
stmt = c.getABranch().(InClause).getBody() or
|
||||
stmt = c.getABranch().(WhenClause).getBody()
|
||||
nodeFrom.asExpr() = branch.(CfgNodes::ExprNodes::InClauseCfgNode).getBody()
|
||||
or
|
||||
nodeFrom.asExpr() = branch.(CfgNodes::ExprNodes::WhenClauseCfgNode).getBody()
|
||||
or
|
||||
nodeFrom.asExpr() = branch
|
||||
)
|
||||
or
|
||||
exists(CfgNodes::ExprCfgNode exprTo, ReturningStatementNode n |
|
||||
|
||||
@@ -24,12 +24,62 @@ predicate defaultTaintSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
bindingset[node]
|
||||
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { none() }
|
||||
|
||||
private CfgNodes::ExprNodes::VariableWriteAccessCfgNode variablesInPattern(
|
||||
CfgNodes::ExprNodes::CasePatternCfgNode p
|
||||
) {
|
||||
result = p
|
||||
or
|
||||
p =
|
||||
any(CfgNodes::ExprNodes::AsPatternCfgNode ap |
|
||||
result = variablesInPattern(ap.getPattern()) or
|
||||
result = ap.getVariableAccess()
|
||||
)
|
||||
or
|
||||
p =
|
||||
any(CfgNodes::ExprNodes::ParenthesizedPatternCfgNode pp |
|
||||
result = variablesInPattern(pp.getPattern())
|
||||
)
|
||||
or
|
||||
p =
|
||||
any(CfgNodes::ExprNodes::AlternativePatternCfgNode ap |
|
||||
result = variablesInPattern(ap.getAlternative(_))
|
||||
)
|
||||
or
|
||||
p =
|
||||
any(CfgNodes::ExprNodes::ArrayPatternCfgNode ap |
|
||||
result = variablesInPattern(ap.getPrefixElement(_)) or
|
||||
result = variablesInPattern(ap.getSuffixElement(_)) or
|
||||
result = ap.getRestVariableAccess()
|
||||
)
|
||||
or
|
||||
p =
|
||||
any(CfgNodes::ExprNodes::FindPatternCfgNode fp |
|
||||
result = variablesInPattern(fp.getElement(_)) or
|
||||
result = fp.getPrefixVariableAccess() or
|
||||
result = fp.getSuffixVariableAccess()
|
||||
)
|
||||
or
|
||||
p =
|
||||
any(CfgNodes::ExprNodes::HashPatternCfgNode hp |
|
||||
result = variablesInPattern(hp.getValue(_)) or
|
||||
result = hp.getRestVariableAccess()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the additional step from `nodeFrom` to `nodeTo` should be included
|
||||
* in all global taint flow configurations.
|
||||
*/
|
||||
cached
|
||||
predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// value of `case` expression into variables in patterns
|
||||
exists(CfgNodes::ExprNodes::CaseExprCfgNode case, CfgNodes::ExprNodes::InClauseCfgNode clause |
|
||||
nodeFrom.asExpr() = case.getValue() and
|
||||
clause = case.getBranch(_) and
|
||||
nodeTo.(SsaDefinitionNode).getDefinition().getControlFlowNode() =
|
||||
variablesInPattern(clause.getPattern())
|
||||
)
|
||||
or
|
||||
// operation involving `nodeFrom`
|
||||
exists(CfgNodes::ExprNodes::OperationCfgNode op |
|
||||
op = nodeTo.asExpr() and
|
||||
|
||||
Reference in New Issue
Block a user