|
3 | 3 | */ |
4 | 4 |
|
5 | 5 | import javascript |
| 6 | +private import semmle.javascript.dataflow.InferredTypes |
6 | 7 |
|
7 | 8 | deprecated |
8 | 9 | module GlobalAccessPath { |
@@ -80,6 +81,35 @@ module AccessPath { |
80 | 81 | result = base + "." + prop |
81 | 82 | } |
82 | 83 |
|
| 84 | + /** |
| 85 | + * Holds if `variable` is compared to the `length` property of something, indicating |
| 86 | + * that, if used as a dynamic property name, it represents an array index. |
| 87 | + */ |
| 88 | + private predicate isLikelyArrayIndex(SsaVariable variable) { |
| 89 | + exists(RelationalComparison cmp, DataFlow::PropRead length, Expr lengthUse | |
| 90 | + length.getPropertyName() = "length" and |
| 91 | + length.flowsToExpr(lengthUse) and |
| 92 | + cmp.hasOperands(variable.getAUse(), lengthUse) |
| 93 | + ) |
| 94 | + or |
| 95 | + isLikelyArrayIndex(variable.getDefinition().(SsaRefinementNode).getAnInput()) |
| 96 | + } |
| 97 | + |
| 98 | + /** |
| 99 | + * Holds if `prop` likely accesses a non-constant array element. |
| 100 | + */ |
| 101 | + private predicate isLikelyDynamicArrayAccess(DataFlow::PropRead prop) { |
| 102 | + // The implicit PropRead in a for-of loop is represented by its lvalue node |
| 103 | + prop = DataFlow::lvalueNode(any(ForOfStmt stmt).getLValue()) |
| 104 | + or |
| 105 | + // Match an index access x[i] where `i` is likely an array index variable. |
| 106 | + not exists(prop.getPropertyName()) and |
| 107 | + exists(SsaVariable indexVar | |
| 108 | + isLikelyArrayIndex(indexVar) and |
| 109 | + prop.getPropertyNameExpr() = indexVar.getAUse() |
| 110 | + ) |
| 111 | + } |
| 112 | + |
83 | 113 | /** |
84 | 114 | * Gets the access path relative to `root` referred to by `node`. |
85 | 115 | * |
@@ -115,6 +145,9 @@ module AccessPath { |
115 | 145 | not node.accessesGlobal(_) and |
116 | 146 | exists(DataFlow::PropRead prop | node = prop | |
117 | 147 | result = join(fromReference(prop.getBase(), root), prop.getPropertyName()) |
| 148 | + or |
| 149 | + isLikelyDynamicArrayAccess(prop) and |
| 150 | + result = join(fromReference(prop.getBase(), root), "[number]") |
118 | 151 | ) |
119 | 152 | or |
120 | 153 | exists(Closure::ClosureNamespaceAccess acc | node = acc | |
|
0 commit comments