Skip to content

Commit d2f3574

Browse files
authored
Merge pull request #2165 from erik-krogh/dosHigh
Approved by asger-semmle
2 parents f1004b1 + 1ae8e25 commit d2f3574

File tree

3 files changed

+20
-13
lines changed

3 files changed

+20
-13
lines changed

change-notes/1.23/analysis-javascript.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
| **Query** | **Tags** | **Purpose** |
2121
|---------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
2222
| Unused index variable (`js/unused-index-variable`) | correctness | Highlights loops that iterate over an array, but do not use the index variable to access array elements, indicating a possible typo or logic error. Results are shown on LGTM by default. |
23-
| Loop bound injection (`js/loop-bound-injection`) | security, external/cwe/cwe-834 | Highlights loops where a user-controlled object with an arbitrary .length value can trick the server to loop indefinitely. Results are not shown on LGTM by default. |
23+
| Loop bound injection (`js/loop-bound-injection`) | security, external/cwe/cwe-834 | Highlights loops where a user-controlled object with an arbitrary .length value can trick the server to loop indefinitely. Results are shown on LGTM by default. |
2424
| Suspicious method name (`js/suspicious-method-name-declaration`) | correctness, typescript, methods | Highlights suspiciously named methods where the developer likely meant to write a constructor or function. Results are shown on LGTM by default. |
2525
| Shell command built from environment values (`js/shell-command-injection-from-environment`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights shell commands that may change behavior inadvertently depending on the execution environment, indicating a possible violation of [CWE-78](https://cwe.mitre.org/data/definitions/78.html). Results are shown on LGTM by default.|
2626
| Use of returnless function (`js/use-of-returnless-function`) | maintainability, correctness | Highlights calls where the return value is used, but the callee never returns a value. Results are shown on LGTM by default. |

javascript/ql/src/Security/CWE-834/LoopBoundInjection.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @id js/loop-bound-injection
88
* @tags security
99
* external/cwe/cwe-834
10-
* @precision medium
10+
* @precision high
1111
*/
1212

1313
import javascript

javascript/ql/src/semmle/javascript/security/dataflow/LoopBoundInjectionCustomizations.qll

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -79,21 +79,26 @@ module LoopBoundInjection {
7979
*/
8080
abstract class Sink extends DataFlow::Node { }
8181

82+
/**
83+
* Holds if there exists an array access indexed by the variable `var` where it is likely that
84+
* the array access will cause a crash if `var` grows unbounded.
85+
*/
86+
predicate hasCrashingArrayAccess(Variable var) {
87+
exists(DataFlow::PropRead arrayRead, Expr throws |
88+
arrayRead.getPropertyNameExpr() = var.getAnAccess() and
89+
arrayRead.flowsToExpr(throws) and
90+
isCrashingWithNullValues(throws)
91+
)
92+
}
93+
8294
/**
8395
* An object that that is being iterated in a `for` loop, such as `for (..; .. sink.length; ...) ...`
8496
*/
8597
private class LoopSink extends Sink {
8698
LoopSink() {
8799
exists(ArrayIterationLoop loop |
88100
this = loop.getLengthRead().getBase() and
89-
// In the DoS we are looking for arrayRead will evaluate to undefined,
90-
// this may cause an exception to be thrown, thus bailing out of the loop.
91-
// A DoS cannot happen if such an exception is thrown.
92-
not exists(DataFlow::PropRead arrayRead, Expr throws |
93-
arrayRead.getPropertyNameExpr() = loop.getIndexVariable().getAnAccess() and
94-
arrayRead.flowsToExpr(throws) and
95-
isCrashingWithNullValues(throws)
96-
) and
101+
not hasCrashingArrayAccess(loop.getIndexVariable()) and
97102
// The existence of some kind of early-exit usually indicates that the loop will stop early and no DoS happens.
98103
not exists(BreakStmt br | br.getTarget() = loop) and
99104
not exists(ReturnStmt ret |
@@ -187,21 +192,23 @@ module LoopBoundInjection {
187192
call = LodashUnderscore::member(name).getACall() and
188193
call.getArgument(0) = this and
189194
// Here it is just assumed that the array element is the first parameter in the callback function.
190-
not exists(DataFlow::FunctionNode func, DataFlow::ParameterNode e |
195+
not exists(DataFlow::FunctionNode func |
191196
func.flowsTo(call.getAnArgument()) and
192-
e = func.getParameter(0) and
193197
(
194198
// Looking for obvious null-pointers happening on the array elements in the iteration.
195199
// Similar to what is done in the loop iteration sink.
196200
exists(Expr throws |
197-
e.flowsToExpr(throws) and
201+
func.getParameter(0).flowsToExpr(throws) and
198202
isCrashingWithNullValues(throws)
199203
)
200204
or
201205
// Similar to the loop sink - the existence of an early-exit usually means that no DoS can happen.
202206
exists(ThrowStmt throw |
203207
throw.getTarget() = func.asExpr()
204208
)
209+
or
210+
// A crash happens looking up the index variable.
211+
hasCrashingArrayAccess(func.getParameter(1).getParameter().getVariable())
205212
)
206213
)
207214
)

0 commit comments

Comments
 (0)