|
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