Skip to content

Commit cc5f59f

Browse files
authored
Merge pull request #9138 from hmac/hmac/array-inclusion-guard-local-flow
Ruby: Make StringArrayInclusion more sensitive
2 parents d444359 + 1fa2144 commit cc5f59f

File tree

13 files changed

+367
-32
lines changed

13 files changed

+367
-32
lines changed

ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ private import ExprNodes
3636
* constant value in some cases.
3737
*/
3838
private module Propagation {
39-
private ExprCfgNode getSource(VariableReadAccessCfgNode read) {
39+
ExprCfgNode getSource(VariableReadAccessCfgNode read) {
4040
exists(Ssa::WriteDefinition def |
4141
def.assigns(result) and
4242
read = def.getARead()
@@ -509,3 +509,53 @@ private module Cached {
509509
}
510510

511511
import Cached
512+
513+
/**
514+
* Holds if the control flow node `e` refers to an array constructed from the
515+
* array literal `arr`.
516+
* Example:
517+
* ```rb
518+
* [1, 2, 3]
519+
* C = [1, 2, 3]; C
520+
* x = [1, 2, 3]; x
521+
* ```
522+
*/
523+
predicate isArrayConstant(ExprCfgNode e, ArrayLiteralCfgNode arr) {
524+
// [...]
525+
e = arr
526+
or
527+
// e = [...]; e
528+
isArrayConstant(getSource(e), arr)
529+
or
530+
isArrayExpr(e.getExpr(), arr)
531+
}
532+
533+
/**
534+
* Holds if the expression `e` refers to an array constructed from the array literal `arr`.
535+
*/
536+
private predicate isArrayExpr(Expr e, ArrayLiteralCfgNode arr) {
537+
// e = [...]
538+
e = arr.getExpr()
539+
or
540+
// Like above, but handles the desugaring of array literals to Array.[] calls.
541+
e.getDesugared() = arr.getExpr()
542+
or
543+
// A = [...]; A
544+
// A = a; A
545+
isArrayExpr(e.(ConstantReadAccess).getValue(), arr)
546+
or
547+
// Recurse via CFG nodes. Necessary for example in:
548+
// a = [...]
549+
// A = a
550+
// A
551+
//
552+
// We map from A to a via ConstantReadAccess::getValue, yielding the Expr a.
553+
// To get to [...] we need to go via getSource(ExprCfgNode e), so we find a
554+
// CFG node for a and call `isArrayConstant`.
555+
//
556+
// The use of `forex` is intended to ensure that a is an array constant in all
557+
// control flow paths.
558+
// Note(hmac): I don't think this is necessary, as `getSource` will not return
559+
// results if the source is a phi node.
560+
forex(ExprCfgNode n | n = e.getAControlFlowNode() | isArrayConstant(n, arr))
561+
}

ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,9 @@ module ExprNodes {
374374
MethodCallCfgNode() { super.getExpr() instanceof MethodCall }
375375

376376
override MethodCall getExpr() { result = super.getExpr() }
377+
378+
/** Gets the name of this method call. */
379+
string getMethodName() { result = this.getExpr().getMethodName() }
377380
}
378381

379382
private class CaseExprChildMapping extends ExprChildMapping, CaseExpr {

ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
private import ruby
44
private import codeql.ruby.DataFlow
55
private import codeql.ruby.CFG
6+
private import codeql.ruby.controlflow.CfgNodes
7+
private import codeql.ruby.dataflow.SSA
8+
private import codeql.ruby.ast.internal.Constant
9+
private import codeql.ruby.InclusionTests
610

711
private predicate stringConstCompare(CfgNodes::ExprCfgNode g, CfgNode e, boolean branch) {
812
exists(CfgNodes::ExprNodes::ComparisonOperationCfgNode c |
@@ -61,35 +65,27 @@ deprecated class StringConstCompare extends DataFlow::BarrierGuard,
6165
// The value of the condition that results in the node being validated.
6266
private boolean checkedBranch;
6367

64-
StringConstCompare() {
65-
exists(CfgNodes::ExprNodes::StringLiteralCfgNode strLitNode |
66-
this.getExpr() instanceof EqExpr and checkedBranch = true
67-
or
68-
this.getExpr() instanceof CaseEqExpr and checkedBranch = true
69-
or
70-
this.getExpr() instanceof NEExpr and checkedBranch = false
71-
|
72-
this.getLeftOperand() = strLitNode and this.getRightOperand() = checkedNode
73-
or
74-
this.getLeftOperand() = checkedNode and this.getRightOperand() = strLitNode
75-
)
76-
}
68+
StringConstCompare() { stringConstCompare(this, checkedNode, checkedBranch) }
7769

7870
override predicate checks(CfgNode expr, boolean branch) {
7971
expr = checkedNode and branch = checkedBranch
8072
}
8173
}
8274

8375
private predicate stringConstArrayInclusionCall(CfgNodes::ExprCfgNode g, CfgNode e, boolean branch) {
84-
exists(CfgNodes::ExprNodes::MethodCallCfgNode mc, ArrayLiteral aLit |
85-
mc = g and
86-
mc.getExpr().getMethodName() = "include?" and
87-
[mc.getExpr().getReceiver(), mc.getExpr().getReceiver().(ConstantReadAccess).getValue()] = aLit
76+
exists(InclusionTest t |
77+
t.asExpr() = g and
78+
e = t.getContainedNode().asExpr() and
79+
branch = t.getPolarity()
8880
|
89-
forall(Expr elem | elem = aLit.getAnElement() | elem instanceof StringLiteral) and
90-
mc.getArgument(0) = e
91-
) and
92-
branch = true
81+
exists(ExprNodes::ArrayLiteralCfgNode arr |
82+
isArrayConstant(t.getContainerNode().asExpr(), arr)
83+
|
84+
forall(ExprCfgNode elem | elem = arr.getAnArgument() |
85+
elem instanceof ExprNodes::StringLiteralCfgNode
86+
)
87+
)
88+
)
9389
}
9490

9591
/**
@@ -132,16 +128,7 @@ deprecated class StringConstArrayInclusionCall extends DataFlow::BarrierGuard,
132128
CfgNodes::ExprNodes::MethodCallCfgNode {
133129
private CfgNode checkedNode;
134130

135-
StringConstArrayInclusionCall() {
136-
exists(ArrayLiteral aLit |
137-
this.getExpr().getMethodName() = "include?" and
138-
[this.getExpr().getReceiver(), this.getExpr().getReceiver().(ConstantReadAccess).getValue()] =
139-
aLit
140-
|
141-
forall(Expr elem | elem = aLit.getAnElement() | elem instanceof StringLiteral) and
142-
this.getArgument(0) = checkedNode
143-
)
144-
}
131+
StringConstArrayInclusionCall() { stringConstArrayInclusionCall(this, checkedNode, true) }
145132

146133
override predicate checks(CfgNode expr, boolean branch) { expr = checkedNode and branch = true }
147134
}

ruby/ql/test/library-tests/ast/Ast.expected

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,6 +1556,35 @@ constants/constants.rb:
15561556
# 73| getAnOperand/getLeftOperand: [ClassVariableAccess] @@fourty_six
15571557
# 73| getAnOperand/getRightOperand: [ConstantReadAccess] FOURTY_SIX
15581558
# 73| getScopeExpr: [ConstantReadAccess] Mod3
1559+
# 78| getStmt: [AssignExpr] ... = ...
1560+
# 78| getAnOperand/getLeftOperand: [LocalVariableAccess] a
1561+
# 78| getAnOperand/getRightOperand: [ArrayLiteral] [...]
1562+
# 78| getElement: [IntegerLiteral] 1
1563+
# 78| getElement: [IntegerLiteral] 2
1564+
# 78| getElement: [IntegerLiteral] 3
1565+
# 79| getStmt: [AssignExpr] ... = ...
1566+
# 79| getAnOperand/getLeftOperand: [ConstantAssignment] A
1567+
# 79| getAnOperand/getRightOperand: [ArrayLiteral] [...]
1568+
# 79| getElement: [IntegerLiteral] 1
1569+
# 79| getElement: [IntegerLiteral] 2
1570+
# 79| getElement: [IntegerLiteral] 3
1571+
# 80| getStmt: [AssignExpr] ... = ...
1572+
# 80| getAnOperand/getLeftOperand: [ConstantAssignment] B
1573+
# 80| getAnOperand/getRightOperand: [LocalVariableAccess] a
1574+
# 81| getStmt: [AssignExpr] ... = ...
1575+
# 81| getAnOperand/getLeftOperand: [ConstantAssignment] C
1576+
# 81| getAnOperand/getRightOperand: [ConstantReadAccess] A
1577+
# 82| getStmt: [AssignExpr] ... = ...
1578+
# 82| getAnOperand/getLeftOperand: [LocalVariableAccess] b
1579+
# 82| getAnOperand/getRightOperand: [ConstantReadAccess] B
1580+
# 84| getStmt: [IfExpr] if ...
1581+
# 84| getCondition: [MethodCall] call to condition
1582+
# 84| getReceiver: [SelfVariableAccess] self
1583+
# 84| getBranch/getThen: [StmtSequence] then ...
1584+
# 85| getStmt: [AssignExpr] ... = ...
1585+
# 85| getAnOperand/getLeftOperand: [LocalVariableAccess] c
1586+
# 85| getAnOperand/getRightOperand: [LocalVariableAccess] b
1587+
# 87| getStmt: [LocalVariableAccess] c
15591588
escape_sequences/escapes.rb:
15601589
# 1| [Toplevel] escapes.rb
15611590
# 6| getStmt: [StringLiteral] "\'"

ruby/ql/test/library-tests/ast/AstDesugar.expected

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,18 @@ constants/constants.rb:
336336
# 20| getComponent: [StringTextComponent] Chuck
337337
# 20| getArgument: [StringLiteral] "Dave"
338338
# 20| getComponent: [StringTextComponent] Dave
339+
# 78| [ArrayLiteral] [...]
340+
# 78| getDesugared: [MethodCall] call to []
341+
# 78| getReceiver: [ConstantReadAccess] Array
342+
# 78| getArgument: [IntegerLiteral] 1
343+
# 78| getArgument: [IntegerLiteral] 2
344+
# 78| getArgument: [IntegerLiteral] 3
345+
# 79| [ArrayLiteral] [...]
346+
# 79| getDesugared: [MethodCall] call to []
347+
# 79| getReceiver: [ConstantReadAccess] Array
348+
# 79| getArgument: [IntegerLiteral] 1
349+
# 79| getArgument: [IntegerLiteral] 2
350+
# 79| getArgument: [IntegerLiteral] 3
339351
escape_sequences/escapes.rb:
340352
# 58| [ArrayLiteral] %w(...)
341353
# 58| getDesugared: [MethodCall] call to []

ruby/ql/test/library-tests/ast/TreeSitter.expected

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,10 +1656,56 @@ constants/constants.rb:
16561656
# 73| 1: [ReservedWord] ::
16571657
# 73| 2: [Constant] FOURTY_SIX
16581658
# 74| 5: [ReservedWord] end
1659+
# 78| 13: [Assignment] Assignment
1660+
# 78| 0: [Identifier] a
1661+
# 78| 1: [ReservedWord] =
1662+
# 78| 2: [Array] Array
1663+
# 78| 0: [ReservedWord] [
1664+
# 78| 1: [Integer] 1
1665+
# 78| 2: [ReservedWord] ,
1666+
# 78| 3: [Integer] 2
1667+
# 78| 4: [ReservedWord] ,
1668+
# 78| 5: [Integer] 3
1669+
# 78| 6: [ReservedWord] ]
1670+
# 79| 14: [Assignment] Assignment
1671+
# 79| 0: [Constant] A
1672+
# 79| 1: [ReservedWord] =
1673+
# 79| 2: [Array] Array
1674+
# 79| 0: [ReservedWord] [
1675+
# 79| 1: [Integer] 1
1676+
# 79| 2: [ReservedWord] ,
1677+
# 79| 3: [Integer] 2
1678+
# 79| 4: [ReservedWord] ,
1679+
# 79| 5: [Integer] 3
1680+
# 79| 6: [ReservedWord] ]
1681+
# 80| 15: [Assignment] Assignment
1682+
# 80| 0: [Constant] B
1683+
# 80| 1: [ReservedWord] =
1684+
# 80| 2: [Identifier] a
1685+
# 81| 16: [Assignment] Assignment
1686+
# 81| 0: [Constant] C
1687+
# 81| 1: [ReservedWord] =
1688+
# 81| 2: [Constant] A
1689+
# 82| 17: [Assignment] Assignment
1690+
# 82| 0: [Identifier] b
1691+
# 82| 1: [ReservedWord] =
1692+
# 82| 2: [Constant] B
1693+
# 84| 18: [If] If
1694+
# 84| 0: [ReservedWord] if
1695+
# 84| 1: [Identifier] condition
1696+
# 84| 2: [Then] Then
1697+
# 85| 0: [Assignment] Assignment
1698+
# 85| 0: [Identifier] c
1699+
# 85| 1: [ReservedWord] =
1700+
# 85| 2: [Identifier] b
1701+
# 86| 3: [ReservedWord] end
1702+
# 87| 19: [Identifier] c
16591703
# 26| [Comment] # A call to Kernel::Array; despite beginning with an upper-case character,
16601704
# 27| [Comment] # we don't consider this to be a constant access.
16611705
# 55| [Comment] # refers to ::ModuleA::FOURTY_FOUR
16621706
# 57| [Comment] # refers to ::ModuleA::ModuleB::ClassB::FOURTY_FOUR
1707+
# 76| [Comment] # Array constants
1708+
# 87| [Comment] # not recognised
16631709
control/cases.rb:
16641710
# 1| [Program] Program
16651711
# 2| 0: [Assignment] Assignment

ruby/ql/test/library-tests/ast/ValueText.expected

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,12 @@ exprValue
109109
| constants/constants.rb:63:19:63:20 | 45 | 45 | int |
110110
| constants/constants.rb:65:19:65:35 | FOURTY_FIVE | 45 | int |
111111
| constants/constants.rb:71:18:71:19 | 46 | 46 | int |
112+
| constants/constants.rb:78:6:78:6 | 1 | 1 | int |
113+
| constants/constants.rb:78:9:78:9 | 2 | 2 | int |
114+
| constants/constants.rb:78:12:78:12 | 3 | 3 | int |
115+
| constants/constants.rb:79:6:79:6 | 1 | 1 | int |
116+
| constants/constants.rb:79:9:79:9 | 2 | 2 | int |
117+
| constants/constants.rb:79:12:79:12 | 3 | 3 | int |
112118
| control/cases.rb:2:5:2:5 | 0 | 0 | int |
113119
| control/cases.rb:3:5:3:5 | 0 | 0 | int |
114120
| control/cases.rb:4:5:4:5 | 0 | 0 | int |
@@ -1004,6 +1010,12 @@ exprCfgNodeValue
10041010
| constants/constants.rb:63:19:63:20 | 45 | 45 | int |
10051011
| constants/constants.rb:65:19:65:35 | FOURTY_FIVE | 45 | int |
10061012
| constants/constants.rb:71:18:71:19 | 46 | 46 | int |
1013+
| constants/constants.rb:78:6:78:6 | 1 | 1 | int |
1014+
| constants/constants.rb:78:9:78:9 | 2 | 2 | int |
1015+
| constants/constants.rb:78:12:78:12 | 3 | 3 | int |
1016+
| constants/constants.rb:79:6:79:6 | 1 | 1 | int |
1017+
| constants/constants.rb:79:9:79:9 | 2 | 2 | int |
1018+
| constants/constants.rb:79:12:79:12 | 3 | 3 | int |
10071019
| control/cases.rb:2:5:2:5 | 0 | 0 | int |
10081020
| control/cases.rb:3:5:3:5 | 0 | 0 | int |
10091021
| control/cases.rb:4:5:4:5 | 0 | 0 | int |

ruby/ql/test/library-tests/ast/constants/constants.expected

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ constantAccess
6161
| constants.rb:71:5:71:14 | FOURTY_SIX | write | FOURTY_SIX | ConstantAssignment |
6262
| constants.rb:73:18:73:21 | Mod3 | read | Mod3 | ConstantReadAccess |
6363
| constants.rb:73:18:73:33 | FOURTY_SIX | read | FOURTY_SIX | ConstantReadAccess |
64+
| constants.rb:78:5:78:13 | Array | read | Array | ConstantReadAccess |
65+
| constants.rb:79:1:79:1 | A | write | A | ConstantAssignment |
66+
| constants.rb:79:5:79:13 | Array | read | Array | ConstantReadAccess |
67+
| constants.rb:80:1:80:1 | B | write | B | ConstantAssignment |
68+
| constants.rb:81:1:81:1 | C | write | C | ConstantAssignment |
69+
| constants.rb:81:5:81:5 | A | read | A | ConstantReadAccess |
70+
| constants.rb:82:5:82:5 | B | read | B | ConstantReadAccess |
6471
getConst
6572
| constants.rb:1:1:15:3 | ModuleA | CONST_B | constants.rb:6:15:6:23 | "const_b" |
6673
| constants.rb:1:1:15:3 | ModuleA | FOURTY_FOUR | constants.rb:53:17:53:29 | "fourty-four" |
@@ -71,23 +78,41 @@ getConst
7178
| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | FOURTY_ONE | constants.rb:48:18:48:19 | 41 |
7279
| constants.rb:62:3:64:5 | Mod1::Mod3 | FOURTY_FIVE | constants.rb:63:19:63:20 | 45 |
7380
| constants.rb:70:3:72:5 | Mod1::Mod3::Mod5 | FOURTY_SIX | constants.rb:71:18:71:19 | 46 |
81+
| file://:0:0:0:0 | Object | A | constants.rb:79:5:79:13 | [...] |
82+
| file://:0:0:0:0 | Object | B | constants.rb:80:5:80:5 | a |
83+
| file://:0:0:0:0 | Object | C | constants.rb:81:5:81:5 | A |
7484
| file://:0:0:0:0 | Object | GREETING | constants.rb:17:12:17:64 | ... + ... |
7585
lookupConst
7686
| constants.rb:1:1:15:3 | ModuleA | CONST_B | constants.rb:6:15:6:23 | "const_b" |
7787
| constants.rb:1:1:15:3 | ModuleA | FOURTY_FOUR | constants.rb:53:17:53:29 | "fourty-four" |
88+
| constants.rb:2:5:4:7 | ModuleA::ClassA | A | constants.rb:79:5:79:13 | [...] |
89+
| constants.rb:2:5:4:7 | ModuleA::ClassA | B | constants.rb:80:5:80:5 | a |
90+
| constants.rb:2:5:4:7 | ModuleA::ClassA | C | constants.rb:81:5:81:5 | A |
7891
| constants.rb:2:5:4:7 | ModuleA::ClassA | CONST_A | constants.rb:3:19:3:27 | "const_a" |
7992
| constants.rb:2:5:4:7 | ModuleA::ClassA | GREETING | constants.rb:17:12:17:64 | ... + ... |
8093
| constants.rb:8:5:14:7 | ModuleA::ModuleB | MAX_SIZE | constants.rb:39:30:39:33 | 1024 |
94+
| constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | A | constants.rb:79:5:79:13 | [...] |
95+
| constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | B | constants.rb:80:5:80:5 | a |
96+
| constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | C | constants.rb:81:5:81:5 | A |
8197
| constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | GREETING | constants.rb:17:12:17:64 | ... + ... |
98+
| constants.rb:31:1:33:3 | ModuleA::ClassD | A | constants.rb:79:5:79:13 | [...] |
99+
| constants.rb:31:1:33:3 | ModuleA::ClassD | B | constants.rb:80:5:80:5 | a |
100+
| constants.rb:31:1:33:3 | ModuleA::ClassD | C | constants.rb:81:5:81:5 | A |
82101
| constants.rb:31:1:33:3 | ModuleA::ClassD | CONST_A | constants.rb:3:19:3:27 | "const_a" |
83102
| constants.rb:31:1:33:3 | ModuleA::ClassD | FOURTY_TWO | constants.rb:32:16:32:17 | 42 |
84103
| constants.rb:31:1:33:3 | ModuleA::ClassD | GREETING | constants.rb:17:12:17:64 | ... + ... |
85104
| constants.rb:35:1:37:3 | ModuleA::ModuleC | FOURTY_THREE | constants.rb:36:18:36:19 | 43 |
105+
| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | A | constants.rb:79:5:79:13 | [...] |
106+
| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | B | constants.rb:80:5:80:5 | a |
107+
| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | C | constants.rb:81:5:81:5 | A |
86108
| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | FOURTY_FOUR | constants.rb:56:19:56:20 | 44 |
87109
| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | FOURTY_ONE | constants.rb:48:18:48:19 | 41 |
88110
| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | GREETING | constants.rb:17:12:17:64 | ... + ... |
89111
| constants.rb:62:3:64:5 | Mod1::Mod3 | FOURTY_FIVE | constants.rb:63:19:63:20 | 45 |
90112
| constants.rb:70:3:72:5 | Mod1::Mod3::Mod5 | FOURTY_SIX | constants.rb:71:18:71:19 | 46 |
113+
| file://:0:0:0:0 | Object | A | constants.rb:79:5:79:13 | [...] |
114+
| file://:0:0:0:0 | Object | B | constants.rb:80:5:80:5 | a |
115+
| file://:0:0:0:0 | Object | C | constants.rb:81:5:81:5 | A |
91116
| file://:0:0:0:0 | Object | GREETING | constants.rb:17:12:17:64 | ... + ... |
92117
constantValue
93118
| constants.rb:17:22:17:45 | CONST_A | constants.rb:3:19:3:27 | "const_a" |
@@ -101,6 +126,8 @@ constantValue
101126
| constants.rb:57:21:57:31 | FOURTY_FOUR | constants.rb:53:17:53:29 | "fourty-four" |
102127
| constants.rb:57:21:57:31 | FOURTY_FOUR | constants.rb:56:19:56:20 | 44 |
103128
| constants.rb:65:19:65:35 | FOURTY_FIVE | constants.rb:63:19:63:20 | 45 |
129+
| constants.rb:81:5:81:5 | A | constants.rb:79:5:79:13 | [...] |
130+
| constants.rb:82:5:82:5 | B | constants.rb:80:5:80:5 | a |
104131
constantWriteAccessQualifiedName
105132
| constants.rb:1:1:15:3 | ModuleA | ModuleA |
106133
| constants.rb:2:5:4:7 | ClassA | ModuleA::ClassA |
@@ -133,3 +160,14 @@ constantWriteAccessQualifiedName
133160
| constants.rb:70:3:72:5 | Mod5 | Mod3::Mod5 |
134161
| constants.rb:71:5:71:14 | FOURTY_SIX | Mod1::Mod3::Mod5::FOURTY_SIX |
135162
| constants.rb:71:5:71:14 | FOURTY_SIX | Mod3::Mod5::FOURTY_SIX |
163+
| constants.rb:79:1:79:1 | A | A |
164+
| constants.rb:80:1:80:1 | B | B |
165+
| constants.rb:81:1:81:1 | C | C |
166+
arrayConstant
167+
| constants.rb:20:13:20:37 | call to [] | constants.rb:20:13:20:37 | call to [] |
168+
| constants.rb:78:5:78:13 | call to [] | constants.rb:78:5:78:13 | call to [] |
169+
| constants.rb:79:5:79:13 | call to [] | constants.rb:79:5:79:13 | call to [] |
170+
| constants.rb:80:5:80:5 | a | constants.rb:78:5:78:13 | call to [] |
171+
| constants.rb:81:5:81:5 | A | constants.rb:79:5:79:13 | call to [] |
172+
| constants.rb:82:5:82:5 | B | constants.rb:78:5:78:13 | call to [] |
173+
| constants.rb:85:7:85:7 | b | constants.rb:78:5:78:13 | call to [] |

ruby/ql/test/library-tests/ast/constants/constants.ql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import ruby
22
import codeql.ruby.ast.internal.Module as M
3+
import codeql.ruby.ast.internal.Constant
34

45
query predicate constantAccess(ConstantAccess a, string kind, string name, string cls) {
56
(
@@ -20,3 +21,5 @@ query predicate constantValue(ConstantReadAccess a, Expr e) { e = a.getValue() }
2021
query predicate constantWriteAccessQualifiedName(ConstantWriteAccess w, string qualifiedName) {
2122
w.getAQualifiedName() = qualifiedName
2223
}
24+
25+
query predicate arrayConstant = isArrayConstant/2;

ruby/ql/test/library-tests/ast/constants/constants.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,16 @@ module Mod3::Mod5
7272
end
7373
@@fourty_six = Mod3::FOURTY_SIX
7474
end
75+
76+
# Array constants
77+
78+
a = [1, 2, 3]
79+
A = [1, 2, 3]
80+
B = a
81+
C = A
82+
b = B
83+
84+
if condition
85+
c = b
86+
end
87+
c # not recognised

0 commit comments

Comments
 (0)