Skip to content

Commit 374f7ab

Browse files
committed
JS: address comments
1 parent c4d7672 commit 374f7ab

File tree

4 files changed

+22
-11
lines changed

4 files changed

+22
-11
lines changed

javascript/ql/src/Security/CWE-020/IncorrectSuffixCheck.qhelp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
The <code>indexOf</code> and <code>lastIndexOf</code> methods are sometimes used to check
1010
if a substring occurs at a certain position in a string. However, if the returned index
11-
is compared to an expression that might evaluate to -1, the check can may pass in some
11+
is compared to an expression that might evaluate to -1, the check may pass in some
1212
cases where the substring was not found at all.
1313

1414
</p>
@@ -26,7 +26,7 @@
2626

2727
Use <code>String.prototype.endsWith</code> if it is available.
2828
Otherwise, explicitly handle the -1 case, either by checking the relative
29-
lengths of the strings, or check if the returned index is -1.
29+
lengths of the strings, or by checking if the returned index is -1.
3030

3131
</p>
3232

@@ -47,7 +47,7 @@
4747

4848
However, if <code>y</code> is one character longer than <code>x</code>, the right-hand side
4949
<code>x.length - y.length</code> becomes -1, which then equals the return value
50-
of <code>lastIndexOf</code>. This will make the test pass, evne though <code>x</code> does not
50+
of <code>lastIndexOf</code>. This will make the test pass, even though <code>x</code> does not
5151
end with <code>y</code>.
5252

5353
</p>

javascript/ql/src/Security/CWE-020/IncorrectSuffixCheck.ql

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,17 @@ class IndexOfCall extends DataFlow::MethodCallNode {
3636
result.getArgument(0).getALocalSource() = this.getArgument(0).getALocalSource() and
3737
result.getMethodName() = this.getMethodName()
3838
}
39+
40+
/**
41+
* Gets an expression that refers to the return value of this call.
42+
*/
43+
Expr getAUse() {
44+
this.flowsToExpr(result)
45+
}
3946
}
4047

4148
/**
42-
* Gets a source of the given string value.
49+
* Gets a source of the given string value, or one of its operands if it is a concatenation.
4350
*/
4451
DataFlow::SourceNode getStringSource(DataFlow::Node node) {
4552
result = node.getALocalSource()
@@ -65,7 +72,7 @@ class LiteralLengthExpr extends DotExpr {
6572
}
6673

6774
/**
68-
* Holds if `node` is derived from the length of the given `indexOf`-operand.
75+
* Holds if `length` is derived from the length of the given `indexOf`-operand.
6976
*/
7077
predicate isDerivedFromLength(DataFlow::Node length, DataFlow::Node operand) {
7178
exists (IndexOfCall call | operand = call.getAnOperand() |
@@ -84,9 +91,7 @@ predicate isDerivedFromLength(DataFlow::Node length, DataFlow::Node operand) {
8491
length = lengthExpr.flow())
8592
)
8693
or
87-
exists (DataFlow::Node mid |
88-
isDerivedFromLength(mid, operand) and
89-
length = mid.getASuccessor())
94+
isDerivedFromLength(length.getAPredecessor(), operand)
9095
or
9196
exists (SubExpr sub |
9297
isDerivedFromLength(sub.getAnOperand().flow(), operand) and
@@ -101,7 +106,7 @@ class UnsafeIndexOfComparison extends EqualityTest {
101106
DataFlow::Node testedValue;
102107

103108
UnsafeIndexOfComparison() {
104-
hasOperands(indexOf.asExpr(), testedValue.asExpr()) and
109+
hasOperands(indexOf.getAUse(), testedValue.asExpr()) and
105110
isDerivedFromLength(testedValue, indexOf.getReceiver()) and
106111
isDerivedFromLength(testedValue, indexOf.getArgument(0)) and
107112

@@ -118,13 +123,13 @@ class UnsafeIndexOfComparison extends EqualityTest {
118123

119124
// Check for indexOf being -1
120125
not exists (EqualityTest test, Expr minusOne |
121-
test.hasOperands(indexOf.getAnEquivalentIndexOfCall().asExpr(), minusOne) and
126+
test.hasOperands(indexOf.getAnEquivalentIndexOfCall().getAUse(), minusOne) and
122127
minusOne.getIntValue() = -1
123128
) and
124129

125130
// Check for indexOf being >1, or >=0, etc
126131
not exists (RelationalComparison test |
127-
test.getGreaterOperand() = indexOf.getAnEquivalentIndexOfCall().asExpr() and
132+
test.getGreaterOperand() = indexOf.getAnEquivalentIndexOfCall().getAUse() and
128133
exists (int value | value = test.getLesserOperand().getIntValue() |
129134
value >= 0
130135
or

javascript/ql/test/query-tests/Security/CWE-020/IncorrectSuffixCheck.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
| tst.js:39:10:39:49 | x.lastI ... .length | This suffix check is missing a length comparison to correctly handle lastIndexOf returning -1. |
88
| tst.js:55:32:55:71 | x.index ... gth - 1 | This suffix check is missing a length comparison to correctly handle indexOf returning -1. |
99
| tst.js:67:32:67:71 | x.index ... gth - 1 | This suffix check is missing a length comparison to correctly handle indexOf returning -1. |
10+
| tst.js:76:25:76:57 | index = ... gth - 1 | This suffix check is missing a length comparison to correctly handle indexOf returning -1. |

javascript/ql/test/query-tests/Security/CWE-020/tst.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,8 @@ function indexOfCheckBad(x, y) {
7070
function endsWithSlash(x) {
7171
return x.indexOf("/") === x.length - 1; // OK - even though it also matches the empty string
7272
}
73+
74+
function withIndexOfCheckBad(x, y) {
75+
let index = x.indexOf(y);
76+
return index !== 0 && index === x.length - y.length - 1; // NOT OK
77+
}

0 commit comments

Comments
 (0)