|
1 | 1 | import python |
2 | 2 | import semmle.python.dataflow.new.DataFlow |
3 | 3 | import semmle.python.dataflow.new.TaintTracking |
4 | | -import semmle.python.dataflow.new.RemoteFlowSources |
5 | 4 | import semmle.python.Concepts |
| 5 | +private import NoSQLInjectionCustomizations::NoSqlInjection as C |
6 | 6 |
|
7 | | -module NoSqlInjection { |
8 | | - class Configuration extends TaintTracking::Configuration { |
9 | | - Configuration() { this = "NoSQLInjection" } |
| 7 | +module Config implements DataFlow::StateConfigSig { |
| 8 | + class FlowState = C::FlowState; |
10 | 9 |
|
11 | | - override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { |
12 | | - source instanceof RemoteFlowSource and |
13 | | - state instanceof RemoteInput |
14 | | - } |
15 | | - |
16 | | - override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { |
17 | | - sink = any(NoSqlQuery noSqlQuery).getQuery() and |
18 | | - state instanceof ConvertedToDict |
19 | | - } |
20 | | - |
21 | | - override predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) { |
22 | | - // Block `RemoteInput` paths here, since they change state to `ConvertedToDict` |
23 | | - exists(Decoding decoding | decoding.getFormat() = "JSON" and node = decoding.getOutput()) and |
24 | | - state instanceof RemoteInput |
25 | | - } |
| 10 | + predicate isSource(DataFlow::Node source, FlowState state) { |
| 11 | + source instanceof C::StringSource and |
| 12 | + state instanceof C::StringInput |
| 13 | + or |
| 14 | + source instanceof C::DictSource and |
| 15 | + state instanceof C::DictInput |
| 16 | + } |
26 | 17 |
|
27 | | - override predicate isAdditionalTaintStep( |
28 | | - DataFlow::Node nodeFrom, DataFlow::FlowState stateFrom, DataFlow::Node nodeTo, |
29 | | - DataFlow::FlowState stateTo |
30 | | - ) { |
31 | | - exists(Decoding decoding | decoding.getFormat() = "JSON" | |
32 | | - nodeFrom = decoding.getAnInput() and |
33 | | - nodeTo = decoding.getOutput() |
34 | | - ) and |
35 | | - stateFrom instanceof RemoteInput and |
36 | | - stateTo instanceof ConvertedToDict |
37 | | - } |
| 18 | + predicate isSink(DataFlow::Node source, FlowState state) { |
| 19 | + source instanceof C::StringSink and |
| 20 | + state instanceof C::StringInput |
| 21 | + or |
| 22 | + source instanceof C::DictSink and |
| 23 | + state instanceof C::DictInput |
| 24 | + } |
38 | 25 |
|
39 | | - override predicate isSanitizer(DataFlow::Node sanitizer) { |
40 | | - sanitizer = any(NoSqlSanitizer noSqlSanitizer).getAnInput() |
41 | | - } |
| 26 | + predicate isBarrier(DataFlow::Node node, FlowState state) { |
| 27 | + // Block `StringInput` paths here, since they change state to `DictInput` |
| 28 | + exists(C::StringToDictConversion c | node = c.getOutput()) and |
| 29 | + state instanceof C::StringInput |
42 | 30 | } |
43 | 31 |
|
44 | | - /** A flow state signifying remote input. */ |
45 | | - class RemoteInput extends DataFlow::FlowState { |
46 | | - RemoteInput() { this = "RemoteInput" } |
| 32 | + predicate isAdditionalFlowStep( |
| 33 | + DataFlow::Node nodeFrom, FlowState stateFrom, DataFlow::Node nodeTo, FlowState stateTo |
| 34 | + ) { |
| 35 | + exists(C::StringToDictConversion c | |
| 36 | + nodeFrom = c.getAnInput() and |
| 37 | + nodeTo = c.getOutput() |
| 38 | + ) and |
| 39 | + stateFrom instanceof C::StringInput and |
| 40 | + stateTo instanceof C::DictInput |
47 | 41 | } |
48 | 42 |
|
49 | | - /** A flow state signifying remote input converted to a dictionary. */ |
50 | | - class ConvertedToDict extends DataFlow::FlowState { |
51 | | - ConvertedToDict() { this = "ConvertedToDict" } |
| 43 | + predicate isBarrier(DataFlow::Node node) { |
| 44 | + node = any(NoSqlSanitizer noSqlSanitizer).getAnInput() |
52 | 45 | } |
53 | 46 | } |
| 47 | + |
| 48 | +module Flow = TaintTracking::GlobalWithState<Config>; |
0 commit comments