Skip to content

Commit 7acc568

Browse files
committed
JS: Port exception steps to a universal summary
1 parent 5ed362f commit 7acc568

File tree

5 files changed

+39
-6
lines changed

5 files changed

+39
-6
lines changed

javascript/ql/lib/semmle/javascript/StandardLibrary.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ private class ArrayIterationCallbackAsPartialInvoke extends DataFlow::PartialInv
6969
* A flow step propagating the exception thrown from a callback to a method whose name coincides
7070
* a built-in Array iteration method, such as `forEach` or `map`.
7171
*/
72-
private class IteratorExceptionStep extends DataFlow::SharedFlowStep {
72+
private class IteratorExceptionStep extends DataFlow::LegacyFlowStep {
7373
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
7474
exists(DataFlow::MethodCallNode call |
7575
call.getMethodName() = ["forEach", "each", "map", "filter", "some", "every", "fold", "reduce"] and

javascript/ql/lib/semmle/javascript/internal/flow_summaries/AllFlowSummaries.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
private import AmbiguousCoreMethods
22
private import Arrays
33
private import AsyncAwait
4+
private import ExceptionFlow
45
private import ForOfLoops
56
private import Generators
67
private import Iterators
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Contains a summary for propagating exceptions out of callbacks
3+
*/
4+
5+
private import javascript
6+
private import FlowSummaryUtil
7+
private import semmle.javascript.dataflow.internal.AdditionalFlowInternal
8+
private import semmle.javascript.dataflow.FlowSummary
9+
private import semmle.javascript.internal.flow_summaries.Promises
10+
11+
/**
12+
* Summary that propagates exceptions out of callbacks back to the caller.
13+
*/
14+
private class ExceptionFlowSummary extends SummarizedCallable {
15+
ExceptionFlowSummary() { this = "Exception propagator" }
16+
17+
override DataFlow::CallNode getACall() {
18+
not exists(result.getACallee()) and
19+
// Avoid a few common cases where the exception should not propagate back
20+
not result.getCalleeName() =
21+
["then", "catch", "finally", "addEventListener", EventEmitter::on()] and
22+
not result = promiseConstructorRef().getAnInvocation() and
23+
// Restrict to cases where a callback is known to flow in, as lambda flow in DataFlowImplCommon blows up otherwise
24+
exists(result.getABoundCallbackParameter(_, _))
25+
}
26+
27+
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
28+
preservesValue = true and
29+
input = "Argument[0..].ReturnValue[exception]" and
30+
output = "ReturnValue[exception]"
31+
}
32+
}

javascript/ql/lib/semmle/javascript/internal/flow_summaries/Promises.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ private import javascript
66
private import semmle.javascript.dataflow.FlowSummary
77
private import FlowSummaryUtil
88

9-
private DataFlow::SourceNode promiseConstructorRef() {
9+
DataFlow::SourceNode promiseConstructorRef() {
1010
result = Promises::promiseConstructorRef()
1111
or
1212
result = DataFlow::moduleImport("bluebird")

javascript/ql/test/library-tests/TripleDot/exceptions.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ function e1() {
1010
throw source('e1.2');
1111
});
1212
} catch (err) {
13-
sink(err); // $ hasValueFlow=e1.2 hasValueFlow=e1.1
13+
sink(err); // $ hasValueFlow=e1.2 MISSING: hasValueFlow=e1.1
1414
}
1515
}
1616

@@ -24,7 +24,7 @@ function e2() {
2424
throw source('e2.2');
2525
});
2626
} catch (err) {
27-
sink(err); // $ MISSING: hasValueFlow=e2.2
27+
sink(err); // $ hasValueFlow=e2.2
2828
}
2929
}
3030

@@ -55,11 +55,11 @@ function e4() {
5555
try {
5656
thrower([source("e4.1")]);
5757
} catch (e) {
58-
sink(e); // $ hasValueFlow=e4.1
58+
sink(e); // $ MISSING: hasValueFlow=e4.1
5959
}
6060
try {
6161
thrower(["safe"]);
6262
} catch (e) {
63-
sink(e); // $ SPURIOUS: hasValueFlow=e4.1
63+
sink(e);
6464
}
6565
}

0 commit comments

Comments
 (0)