|
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