|
1 | 1 | import python
|
2 | 2 | import semmle.python.dataflow.new.DataFlow
|
3 |
| -import semmle.python.dataflow.new.DataFlow2 |
4 | 3 | import semmle.python.dataflow.new.TaintTracking
|
5 |
| -import semmle.python.dataflow.new.TaintTracking2 |
6 | 4 | import semmle.python.dataflow.new.RemoteFlowSources
|
7 |
| -import semmle.python.security.dataflow.ChainedConfigs12 |
8 | 5 | import experimental.semmle.python.Concepts
|
9 | 6 | import semmle.python.Concepts
|
10 | 7 |
|
11 |
| -/** |
12 |
| - * A taint-tracking configuration for detecting string-to-dict conversions. |
13 |
| - */ |
14 |
| -class RFSToDictConfig extends TaintTracking::Configuration { |
15 |
| - RFSToDictConfig() { this = "RFSToDictConfig" } |
16 |
| - |
17 |
| - override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } |
18 |
| - |
19 |
| - override predicate isSink(DataFlow::Node sink) { |
20 |
| - exists(Decoding decoding | decoding.getFormat() = "JSON" and sink = decoding.getOutput()) |
| 8 | +module NoSQLInjection { |
| 9 | + class Configuration extends TaintTracking::Configuration { |
| 10 | + Configuration() { this = "NoSQLInjection" } |
| 11 | + |
| 12 | + override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { |
| 13 | + source instanceof RemoteFlowSource and |
| 14 | + state instanceof RemoteInput |
| 15 | + } |
| 16 | + |
| 17 | + override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { |
| 18 | + sink = any(NoSQLQuery noSQLQuery).getQuery() and |
| 19 | + state instanceof ConvertedToDict |
| 20 | + } |
| 21 | + |
| 22 | + override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { |
| 23 | + // Block `RemoteInput` paths here, since they change state to `ConvertedToDict` |
| 24 | + exists(Decoding decoding | decoding.getFormat() = "JSON" and node = decoding.getOutput()) and |
| 25 | + state instanceof RemoteInput |
| 26 | + } |
| 27 | + |
| 28 | + override predicate isAdditionalFlowStep( |
| 29 | + DataFlow::Node nodeFrom, DataFlow::FlowState stateFrom, DataFlow::Node nodeTo, |
| 30 | + DataFlow::FlowState stateTo |
| 31 | + ) { |
| 32 | + exists(Decoding decoding | decoding.getFormat() = "JSON" | |
| 33 | + nodeFrom = decoding.getAnInput() and |
| 34 | + nodeTo = decoding.getOutput() |
| 35 | + ) and |
| 36 | + stateFrom instanceof RemoteInput and |
| 37 | + stateTo instanceof ConvertedToDict |
| 38 | + } |
| 39 | + |
| 40 | + override predicate isSanitizer(DataFlow::Node sanitizer) { |
| 41 | + sanitizer = any(NoSQLSanitizer noSQLSanitizer).getAnInput() |
| 42 | + } |
21 | 43 | }
|
22 | 44 |
|
23 |
| - override predicate isSanitizer(DataFlow::Node sanitizer) { |
24 |
| - sanitizer = any(NoSQLSanitizer noSQLSanitizer).getAnInput() |
| 45 | + /** A flow state signifying remote input. */ |
| 46 | + class RemoteInput extends DataFlow::FlowState { |
| 47 | + RemoteInput() { this = "RemoteInput" } |
25 | 48 | }
|
26 |
| -} |
27 | 49 |
|
28 |
| -/** |
29 |
| - * A taint-tracking configuration for detecting NoSQL injections (previously converted to a dict). |
30 |
| - */ |
31 |
| -class FromDataDictToSink extends TaintTracking2::Configuration { |
32 |
| - FromDataDictToSink() { this = "FromDataDictToSink" } |
33 |
| - |
34 |
| - override predicate isSource(DataFlow::Node source) { |
35 |
| - exists(Decoding decoding | decoding.getFormat() = "JSON" and source = decoding.getOutput()) |
| 50 | + /** A flow state signifying remote input converted to a dictionary. */ |
| 51 | + class ConvertedToDict extends DataFlow::FlowState { |
| 52 | + ConvertedToDict() { this = "ConvertedToDict" } |
36 | 53 | }
|
37 |
| - |
38 |
| - override predicate isSink(DataFlow::Node sink) { sink = any(NoSQLQuery noSQLQuery).getQuery() } |
39 |
| - |
40 |
| - override predicate isSanitizer(DataFlow::Node sanitizer) { |
41 |
| - sanitizer = any(NoSQLSanitizer noSQLSanitizer).getAnInput() |
42 |
| - } |
43 |
| -} |
44 |
| - |
45 |
| -/** |
46 |
| - * A predicate checking string-to-dict conversion and its arrival to a NoSQL injection sink. |
47 |
| - */ |
48 |
| -predicate noSQLInjectionFlow(CustomPathNode source, CustomPathNode sink) { |
49 |
| - exists( |
50 |
| - RFSToDictConfig config, DataFlow::PathNode mid1, DataFlow2::PathNode mid2, |
51 |
| - FromDataDictToSink config2 |
52 |
| - | |
53 |
| - config.hasFlowPath(source.asNode1(), mid1) and |
54 |
| - config2.hasFlowPath(mid2, sink.asNode2()) and |
55 |
| - mid1.getNode().asCfgNode() = mid2.getNode().asCfgNode() |
56 |
| - ) |
57 | 54 | }
|
0 commit comments