Skip to content

Commit 820f81f

Browse files
committed
JS: Migrate UnsafeDynamicMethodAccess
1 parent a9e89ed commit 820f81f

File tree

2 files changed

+66
-29
lines changed

2 files changed

+66
-29
lines changed

javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeDynamicMethodAccessCustomizations.qll

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,61 @@ import semmle.javascript.frameworks.Express
1010
import PropertyInjectionShared
1111

1212
module UnsafeDynamicMethodAccess {
13-
private import DataFlow::FlowLabel
13+
private newtype TFlowState =
14+
TTaint() or
15+
TUnsafeFunction()
16+
17+
/** A flow state to associate with a tracked value. */
18+
class FlowState extends TFlowState {
19+
/** Gets a string representation fo this flow state */
20+
string toString() {
21+
this = TTaint() and result = "taint"
22+
or
23+
this = TUnsafeFunction() and result = "unsafe-function"
24+
}
25+
26+
deprecated DataFlow::FlowLabel toFlowLabel() {
27+
this = TTaint() and result.isTaint()
28+
or
29+
this = TUnsafeFunction() and result instanceof UnsafeFunction
30+
}
31+
}
32+
33+
/** Predicates for working with flow states. */
34+
module FlowState {
35+
deprecated FlowState fromFlowLabel(DataFlow::FlowLabel label) { result.toFlowLabel() = label }
36+
37+
/** A tainted value. */
38+
FlowState taint() { result = TTaint() }
39+
40+
/** A reference to an unsafe function, such as `eval`, obtained by reading from a tainted property name. */
41+
FlowState unsafeFunction() { result = TUnsafeFunction() }
42+
}
1443

1544
/**
1645
* A data flow source for unsafe dynamic method access.
1746
*/
1847
abstract class Source extends DataFlow::Node {
1948
/**
20-
* Gets the flow label relevant for this source.
49+
* Gets a flow state relevant for this source.
2150
*/
22-
DataFlow::FlowLabel getFlowLabel() { result = taint() }
51+
FlowState getAFlowState() { result = FlowState::taint() }
52+
53+
/** DEPRECATED. Use `getAFlowState()` instead. */
54+
deprecated DataFlow::FlowLabel getFlowLabel() { result = this.getAFlowState().toFlowLabel() }
2355
}
2456

2557
/**
2658
* A data flow sink for unsafe dynamic method access.
2759
*/
2860
abstract class Sink extends DataFlow::Node {
2961
/**
30-
* Gets the flow label relevant for this sink
62+
* Gets a flow state relevant for this sink.
3163
*/
32-
abstract DataFlow::FlowLabel getFlowLabel();
64+
FlowState getAFlowState() { result = FlowState::taint() }
65+
66+
/** DEPRECATED. Use `getAFlowState()` instead. */
67+
deprecated DataFlow::FlowLabel getFlowLabel() { result = this.getAFlowState().toFlowLabel() }
3368
}
3469

3570
/**
@@ -38,16 +73,20 @@ module UnsafeDynamicMethodAccess {
3873
abstract class Sanitizer extends DataFlow::Node { }
3974

4075
/**
76+
* DEPRECATED. Use `FlowState::unsafeFunction()` instead.
77+
*
4178
* Gets the flow label describing values that may refer to an unsafe
4279
* function as a result of an attacker-controlled property name.
4380
*/
44-
UnsafeFunction unsafeFunction() { any() }
81+
deprecated UnsafeFunction unsafeFunction() { any() }
4582

4683
/**
84+
* DEPRECATED. Use `FlowState::unsafeFunction()` instead.
85+
*
4786
* A flow label describing values that may refer to an unsafe
4887
* function as a result of an attacker-controlled property name.
4988
*/
50-
abstract class UnsafeFunction extends DataFlow::FlowLabel {
89+
abstract deprecated class UnsafeFunction extends DataFlow::FlowLabel {
5190
UnsafeFunction() { this = "UnsafeFunction" }
5291
}
5392

@@ -67,6 +106,6 @@ module UnsafeDynamicMethodAccess {
67106
class CalleeAsSink extends Sink {
68107
CalleeAsSink() { this = any(DataFlow::InvokeNode node).getCalleeNode() }
69108

70-
override DataFlow::FlowLabel getFlowLabel() { result = unsafeFunction() }
109+
override FlowState getAFlowState() { result = FlowState::unsafeFunction() }
71110
}
72111
}

javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeDynamicMethodAccessQuery.qll

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,24 @@ import javascript
1111
import PropertyInjectionShared
1212
private import DataFlow::FlowLabel
1313
import UnsafeDynamicMethodAccessCustomizations::UnsafeDynamicMethodAccess
14+
private import UnsafeDynamicMethodAccessCustomizations::UnsafeDynamicMethodAccess as UnsafeDynamicMethodAccess
1415

1516
// Materialize flow labels
16-
private class ConcreteUnsafeFunction extends UnsafeFunction {
17+
deprecated private class ConcreteUnsafeFunction extends UnsafeFunction {
1718
ConcreteUnsafeFunction() { this = this }
1819
}
1920

2021
/**
2122
* A taint-tracking configuration for reasoning about unsafe dynamic method access.
2223
*/
2324
module UnsafeDynamicMethodAccessConfig implements DataFlow::StateConfigSig {
24-
class FlowState = DataFlow::FlowLabel;
25+
class FlowState = UnsafeDynamicMethodAccess::FlowState;
2526

26-
predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) {
27-
source.(Source).getFlowLabel() = label
27+
predicate isSource(DataFlow::Node source, FlowState label) {
28+
source.(Source).getAFlowState() = label
2829
}
2930

30-
predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) {
31-
sink.(Sink).getFlowLabel() = label
32-
}
31+
predicate isSink(DataFlow::Node sink, FlowState label) { sink.(Sink).getAFlowState() = label }
3332

3433
predicate isBarrier(DataFlow::Node node) {
3534
node instanceof Sanitizer
@@ -38,44 +37,42 @@ module UnsafeDynamicMethodAccessConfig implements DataFlow::StateConfigSig {
3837
not StringConcatenation::isCoercion(node)
3938
}
4039

41-
predicate isBarrier(DataFlow::Node node, DataFlow::FlowLabel label) {
40+
predicate isBarrier(DataFlow::Node node, FlowState label) {
4241
TaintTracking::defaultSanitizer(node) and
43-
label.isTaint()
42+
label = FlowState::taint()
4443
}
4544

4645
/** An additional flow step for use in both this configuration and the legacy configuration. */
4746
additional predicate additionalFlowStep(
48-
DataFlow::Node src, DataFlow::FlowLabel srclabel, DataFlow::Node dst,
49-
DataFlow::FlowLabel dstlabel
47+
DataFlow::Node src, FlowState srclabel, DataFlow::Node dst, FlowState dstlabel
5048
) {
5149
// Reading a property of the global object or of a function
5250
exists(DataFlow::PropRead read |
5351
PropertyInjection::hasUnsafeMethods(read.getBase().getALocalSource()) and
5452
src = read.getPropertyNameExpr().flow() and
5553
dst = read and
56-
srclabel.isTaint() and
57-
dstlabel = unsafeFunction()
54+
srclabel = FlowState::taint() and
55+
dstlabel = FlowState::unsafeFunction()
5856
)
5957
or
6058
// Reading a chain of properties from any object with a prototype can lead to Function
6159
exists(PropertyProjection proj |
6260
not PropertyInjection::isPrototypeLessObject(proj.getObject().getALocalSource()) and
6361
src = proj.getASelector() and
6462
dst = proj and
65-
srclabel.isTaint() and
66-
dstlabel = unsafeFunction()
63+
srclabel = FlowState::taint() and
64+
dstlabel = FlowState::unsafeFunction()
6765
)
6866
}
6967

7068
predicate isAdditionalFlowStep(
71-
DataFlow::Node src, DataFlow::FlowLabel srclabel, DataFlow::Node dst,
72-
DataFlow::FlowLabel dstlabel
69+
DataFlow::Node src, FlowState srclabel, DataFlow::Node dst, FlowState dstlabel
7370
) {
7471
additionalFlowStep(src, srclabel, dst, dstlabel)
7572
or
7673
// We're not using a taint-tracking config because taint steps would then apply to all flow states.
7774
// So we use a plain data flow config and manually add the default taint steps.
78-
srclabel.isTaint() and
75+
srclabel = FlowState::taint() and
7976
TaintTracking::defaultTaintStep(src, dst) and
8077
srclabel = dstlabel
8178
}
@@ -93,11 +90,11 @@ deprecated class Configuration extends TaintTracking::Configuration {
9390
Configuration() { this = "UnsafeDynamicMethodAccess" }
9491

9592
override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) {
96-
UnsafeDynamicMethodAccessConfig::isSource(source, label)
93+
UnsafeDynamicMethodAccessConfig::isSource(source, FlowState::fromFlowLabel(label))
9794
}
9895

9996
override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) {
100-
UnsafeDynamicMethodAccessConfig::isSink(sink, label)
97+
UnsafeDynamicMethodAccessConfig::isSink(sink, FlowState::fromFlowLabel(label))
10198
}
10299

103100
override predicate isSanitizer(DataFlow::Node node) {
@@ -117,6 +114,7 @@ deprecated class Configuration extends TaintTracking::Configuration {
117114
DataFlow::Node src, DataFlow::Node dst, DataFlow::FlowLabel srclabel,
118115
DataFlow::FlowLabel dstlabel
119116
) {
120-
UnsafeDynamicMethodAccessConfig::additionalFlowStep(src, srclabel, dst, dstlabel)
117+
UnsafeDynamicMethodAccessConfig::additionalFlowStep(src, FlowState::fromFlowLabel(srclabel),
118+
dst, FlowState::fromFlowLabel(dstlabel))
121119
}
122120
}

0 commit comments

Comments
 (0)