|
12 | 12 |
|
13 | 13 | import swift |
14 | 14 | import codeql.swift.dataflow.DataFlow |
15 | | -import codeql.swift.dataflow.TaintTracking |
| 15 | +import codeql.swift.security.StringLengthConflationQuery |
16 | 16 | import DataFlow::PathGraph |
17 | 17 |
|
18 | | -/** |
19 | | - * A flow state for this query, which is a type of Swift string encoding. |
20 | | - */ |
21 | | -class StringLengthConflationFlowState extends string { |
22 | | - string equivClass; |
23 | | - string singular; |
24 | | - |
25 | | - StringLengthConflationFlowState() { |
26 | | - this = "String" and singular = "a String" and equivClass = "String" |
27 | | - or |
28 | | - this = "NSString" and singular = "an NSString" and equivClass = "NSString" |
29 | | - or |
30 | | - this = "String.utf8" and singular = "a String.utf8" and equivClass = "String.utf8" |
31 | | - or |
32 | | - this = "String.utf16" and singular = "a String.utf16" and equivClass = "NSString" |
33 | | - or |
34 | | - this = "String.unicodeScalars" and |
35 | | - singular = "a String.unicodeScalars" and |
36 | | - equivClass = "String.unicodeScalars" |
37 | | - } |
38 | | - |
39 | | - /** |
40 | | - * Gets the equivalence class for this flow state. If these are equal, |
41 | | - * they should be treated as equivalent. |
42 | | - */ |
43 | | - string getEquivClass() { result = equivClass } |
44 | | - |
45 | | - /** |
46 | | - * Gets text for the singular form of this flow state. |
47 | | - */ |
48 | | - string getSingular() { result = singular } |
49 | | -} |
50 | | - |
51 | | -/** |
52 | | - * A configuration for tracking string lengths originating from source that is |
53 | | - * a `String` or an `NSString` object, to a sink of a different kind that |
54 | | - * expects an incompatible measure of length. |
55 | | - */ |
56 | | -class StringLengthConflationConfiguration extends TaintTracking::Configuration { |
57 | | - StringLengthConflationConfiguration() { this = "StringLengthConflationConfiguration" } |
58 | | - |
59 | | - override predicate isSource(DataFlow::Node node, string flowstate) { |
60 | | - exists(MemberRefExpr memberRef, string className, string varName | |
61 | | - memberRef.getBase().getType().(NominalType).getABaseType*().getName() = className and |
62 | | - memberRef.getMember().(VarDecl).getName() = varName and |
63 | | - node.asExpr() = memberRef and |
64 | | - ( |
65 | | - // result of a call to `String.count` |
66 | | - className = "String" and |
67 | | - varName = "count" and |
68 | | - flowstate = "String" |
69 | | - or |
70 | | - // result of a call to `NSString.length` |
71 | | - className = ["NSString", "NSMutableString"] and |
72 | | - varName = "length" and |
73 | | - flowstate = "NSString" |
74 | | - or |
75 | | - // result of a call to `String.utf8.count` |
76 | | - className = "String.UTF8View" and |
77 | | - varName = "count" and |
78 | | - flowstate = "String.utf8" |
79 | | - or |
80 | | - // result of a call to `String.utf16.count` |
81 | | - className = "String.UTF16View" and |
82 | | - varName = "count" and |
83 | | - flowstate = "String.utf16" |
84 | | - or |
85 | | - // result of a call to `String.unicodeScalars.count` |
86 | | - className = "String.UnicodeScalarView" and |
87 | | - varName = "count" and |
88 | | - flowstate = "String.unicodeScalars" |
89 | | - ) |
90 | | - ) |
91 | | - } |
92 | | - |
93 | | - /** |
94 | | - * Holds if `node` is a sink and `flowstate` is the *correct* flow state for |
95 | | - * that sink. We actually want to report incorrect flow states. |
96 | | - */ |
97 | | - predicate isSinkImpl(DataFlow::Node node, string flowstate) { |
98 | | - exists(AbstractFunctionDecl funcDecl, CallExpr call, string funcName, int arg | |
99 | | - ( |
100 | | - // arguments to method calls... |
101 | | - exists(string className, ClassOrStructDecl c | |
102 | | - ( |
103 | | - // `NSRange.init` |
104 | | - className = "NSRange" and |
105 | | - funcName = "init(location:length:)" and |
106 | | - arg = [0, 1] |
107 | | - or |
108 | | - // `NSString.character` |
109 | | - className = ["NSString", "NSMutableString"] and |
110 | | - funcName = "character(at:)" and |
111 | | - arg = 0 |
112 | | - or |
113 | | - // `NSString.character` |
114 | | - className = ["NSString", "NSMutableString"] and |
115 | | - funcName = "substring(from:)" and |
116 | | - arg = 0 |
117 | | - or |
118 | | - // `NSString.character` |
119 | | - className = ["NSString", "NSMutableString"] and |
120 | | - funcName = "substring(to:)" and |
121 | | - arg = 0 |
122 | | - or |
123 | | - // `NSMutableString.insert` |
124 | | - className = "NSMutableString" and |
125 | | - funcName = "insert(_:at:)" and |
126 | | - arg = 1 |
127 | | - ) and |
128 | | - c.getName() = className and |
129 | | - c.getABaseTypeDecl*().(ClassOrStructDecl).getAMember() = funcDecl and |
130 | | - call.getStaticTarget() = funcDecl and |
131 | | - flowstate = "NSString" |
132 | | - ) |
133 | | - or |
134 | | - // arguments to function calls... |
135 | | - // `NSMakeRange` |
136 | | - funcName = "NSMakeRange(_:_:)" and |
137 | | - arg = [0, 1] and |
138 | | - call.getStaticTarget() = funcDecl and |
139 | | - flowstate = "NSString" |
140 | | - or |
141 | | - // arguments to method calls... |
142 | | - ( |
143 | | - // `String.dropFirst`, `String.dropLast`, `String.removeFirst`, `String.removeLast` |
144 | | - funcName = ["dropFirst(_:)", "dropLast(_:)", "removeFirst(_:)", "removeLast(_:)"] and |
145 | | - arg = 0 |
146 | | - or |
147 | | - // `String.prefix`, `String.suffix` |
148 | | - funcName = ["prefix(_:)", "suffix(_:)"] and |
149 | | - arg = 0 |
150 | | - or |
151 | | - // `String.Index.init` |
152 | | - funcName = "init(encodedOffset:)" and |
153 | | - arg = 0 |
154 | | - or |
155 | | - // `String.index` |
156 | | - funcName = ["index(_:offsetBy:)", "index(_:offsetBy:limitBy:)"] and |
157 | | - arg = [0, 1] |
158 | | - or |
159 | | - // `String.formIndex` |
160 | | - funcName = ["formIndex(_:offsetBy:)", "formIndex(_:offsetBy:limitBy:)"] and |
161 | | - arg = [0, 1] |
162 | | - ) and |
163 | | - call.getStaticTarget() = funcDecl and |
164 | | - flowstate = "String" |
165 | | - ) and |
166 | | - // match up `funcName`, `arg`, `node`. |
167 | | - funcDecl.getName() = funcName and |
168 | | - call.getArgument(arg).getExpr() = node.asExpr() |
169 | | - ) |
170 | | - } |
171 | | - |
172 | | - override predicate isSink(DataFlow::Node node, string flowstate) { |
173 | | - // Permit any *incorrect* flowstate, as those are the results the query |
174 | | - // should report. |
175 | | - exists(string correctFlowState | |
176 | | - isSinkImpl(node, correctFlowState) and |
177 | | - flowstate.(StringLengthConflationFlowState).getEquivClass() != |
178 | | - correctFlowState.(StringLengthConflationFlowState).getEquivClass() |
179 | | - ) |
180 | | - } |
181 | | -} |
182 | | - |
183 | 18 | from |
184 | 19 | StringLengthConflationConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink, |
185 | 20 | StringLengthConflationFlowState sourceFlowState, StringLengthConflationFlowState sinkFlowstate, |
|
0 commit comments