|
14 | 14 | * external/cwe/cwe-036 |
15 | 15 | * external/cwe/cwe-073 |
16 | 16 | * external/cwe/cwe-099 |
17 | | - * |
18 | | - * The query detects cases where a user-controlled path is used in an unsafe manner, |
19 | | - * meaning it is not both normalized and _afterwards_ checked. |
20 | | - * |
21 | | - * It does so by dividing the problematic situation into two cases: |
22 | | - * 1. The file path is never normalized. |
23 | | - * This is easily detected by using normalization as a sanitizer. |
24 | | - * |
25 | | - * 2. The file path is normalized at least once, but never checked afterwards. |
26 | | - * This is detected by finding the earliest normalization and then ensuring that |
27 | | - * no checks happen later. Since we start from the earliest normalization, |
28 | | - * we know that the absence of checks means that no normalization has a |
29 | | - * check after it. (No checks after a second normalization would be ok if |
30 | | - * there was a check between the first and the second.) |
31 | | - * |
32 | | - * Note that one could make the dual split on whether the file path is ever checked. This does |
33 | | - * not work as nicely, however, since checking is modelled as a `BarrierGuard` rather than |
34 | | - * as a `Sanitizer`. That means that only some dataflow paths out of a check will be removed, |
35 | | - * and so identifying the last check is not possible simply by finding a dataflow path from it |
36 | | - * to a sink. |
37 | 17 | */ |
38 | 18 |
|
39 | 19 | import python |
40 | | -import semmle.python.dataflow.new.DataFlow |
41 | | -import semmle.python.dataflow.new.DataFlow2 |
42 | | -import semmle.python.dataflow.new.TaintTracking |
43 | | -import semmle.python.dataflow.new.TaintTracking2 |
44 | | -import semmle.python.Concepts |
45 | | -import semmle.python.dataflow.new.RemoteFlowSources |
46 | | -import ChainedConfigs12 |
| 20 | +import semmle.python.security.dataflow.PathInjection |
47 | 21 |
|
48 | | -// --------------------------------------------------------------------------- |
49 | | -// Case 1. The path is never normalized. |
50 | | -// --------------------------------------------------------------------------- |
51 | | -/** Configuration to find paths from sources to sinks that contain no normalization. */ |
52 | | -class PathNotNormalizedConfiguration extends TaintTracking::Configuration { |
53 | | - PathNotNormalizedConfiguration() { this = "PathNotNormalizedConfiguration" } |
54 | | - |
55 | | - override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } |
56 | | - |
57 | | - override predicate isSink(DataFlow::Node sink) { |
58 | | - sink = any(FileSystemAccess e).getAPathArgument() |
59 | | - } |
60 | | - |
61 | | - override predicate isSanitizer(DataFlow::Node node) { node instanceof Path::PathNormalization } |
62 | | -} |
63 | | - |
64 | | -predicate pathNotNormalized(CustomPathNode source, CustomPathNode sink) { |
65 | | - any(PathNotNormalizedConfiguration config).hasFlowPath(source.asNode1(), sink.asNode1()) |
66 | | -} |
67 | | - |
68 | | -// --------------------------------------------------------------------------- |
69 | | -// Case 2. The path is normalized at least once, but never checked afterwards. |
70 | | -// --------------------------------------------------------------------------- |
71 | | -/** Configuration to find paths from sources to normalizations that contain no prior normalizations. */ |
72 | | -class FirstNormalizationConfiguration extends TaintTracking::Configuration { |
73 | | - FirstNormalizationConfiguration() { this = "FirstNormalizationConfiguration" } |
74 | | - |
75 | | - override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } |
76 | | - |
77 | | - override predicate isSink(DataFlow::Node sink) { sink instanceof Path::PathNormalization } |
78 | | - |
79 | | - override predicate isSanitizerOut(DataFlow::Node node) { node instanceof Path::PathNormalization } |
80 | | -} |
81 | | - |
82 | | -/** Configuration to find paths from normalizations to sinks that do not go through a check. */ |
83 | | -class NormalizedPathNotCheckedConfiguration extends TaintTracking2::Configuration { |
84 | | - NormalizedPathNotCheckedConfiguration() { this = "NormalizedPathNotCheckedConfiguration" } |
85 | | - |
86 | | - override predicate isSource(DataFlow::Node source) { source instanceof Path::PathNormalization } |
87 | | - |
88 | | - override predicate isSink(DataFlow::Node sink) { |
89 | | - sink = any(FileSystemAccess e).getAPathArgument() |
90 | | - } |
91 | | - |
92 | | - override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { |
93 | | - guard instanceof Path::SafeAccessCheck |
94 | | - } |
95 | | -} |
96 | | - |
97 | | -predicate pathNotCheckedAfterNormalization(CustomPathNode source, CustomPathNode sink) { |
98 | | - exists( |
99 | | - FirstNormalizationConfiguration config, DataFlow::PathNode mid1, DataFlow2::PathNode mid2, |
100 | | - NormalizedPathNotCheckedConfiguration config2 |
101 | | - | |
102 | | - config.hasFlowPath(source.asNode1(), mid1) and |
103 | | - config2.hasFlowPath(mid2, sink.asNode2()) and |
104 | | - mid1.getNode().asCfgNode() = mid2.getNode().asCfgNode() |
105 | | - ) |
106 | | -} |
107 | | - |
108 | | -// --------------------------------------------------------------------------- |
109 | | -// Query: Either case 1 or case 2. |
110 | | -// --------------------------------------------------------------------------- |
111 | 22 | from CustomPathNode source, CustomPathNode sink |
112 | | -where |
113 | | - pathNotNormalized(source, sink) |
114 | | - or |
115 | | - pathNotCheckedAfterNormalization(source, sink) |
| 23 | +where pathInjection(source, sink) |
116 | 24 | select sink, source, sink, "This path depends on $@.", source, "a user-provided value" |
0 commit comments