|
3 | 3 | */
|
4 | 4 |
|
5 | 5 | import go
|
| 6 | +private import semmle.go.dataflow.DataFlowForStringsNewReplacer |
6 | 7 |
|
7 | 8 | /** Provides predicates and classes for working with string operations. */
|
8 | 9 | module StringOps {
|
@@ -162,6 +163,114 @@ module StringOps {
|
162 | 163 | }
|
163 | 164 | }
|
164 | 165 |
|
| 166 | + /** |
| 167 | + * An expression that is equivalent to `strings.ReplaceAll(s, old, new)`. |
| 168 | + * |
| 169 | + * Extend this class to refine existing API models. If you want to model new APIs, |
| 170 | + * extend `StringOps::ReplaceAll::Range` instead. |
| 171 | + */ |
| 172 | + class ReplaceAll extends DataFlow::Node instanceof ReplaceAll::Range { |
| 173 | + /** |
| 174 | + * Gets the `old` in `strings.ReplaceAll(s, old, new)`. |
| 175 | + */ |
| 176 | + string getReplacedString() { result = super.getReplacedString() } |
| 177 | + } |
| 178 | + |
| 179 | + /** Provides predicates and classes for working with prefix checks. */ |
| 180 | + module ReplaceAll { |
| 181 | + /** |
| 182 | + * An expression that is equivalent to `strings.ReplaceAll(s, old, new)`. |
| 183 | + * |
| 184 | + * Extend this class to model new APIs. If you want to refine existing API models, extend |
| 185 | + * `StringOps::ReplaceAll` instead. |
| 186 | + */ |
| 187 | + abstract class Range extends DataFlow::Node { |
| 188 | + /** |
| 189 | + * Gets the `old` in `strings.ReplaceAll(s, old, new)`. |
| 190 | + */ |
| 191 | + abstract string getReplacedString(); |
| 192 | + } |
| 193 | + |
| 194 | + /** |
| 195 | + * A call to `strings.ReplaceAll` or `strings.Replace` with a negative `n` |
| 196 | + * so that all instances are replaced. |
| 197 | + */ |
| 198 | + private class StringsReplaceAll extends Range, DataFlow::CallNode { |
| 199 | + StringsReplaceAll() { |
| 200 | + exists(string name | this.getTarget().hasQualifiedName("strings", name) | |
| 201 | + name = "ReplaceAll" |
| 202 | + or |
| 203 | + name = "Replace" and |
| 204 | + this.getArgument(3).getNumericValue() < 0 |
| 205 | + ) |
| 206 | + } |
| 207 | + |
| 208 | + override string getReplacedString() { result = this.getArgument(1).getStringValue() } |
| 209 | + } |
| 210 | + |
| 211 | + /** |
| 212 | + * A call to `strings.NewReplacer`. |
| 213 | + */ |
| 214 | + private class StringsNewReplacerCall extends DataFlow::CallNode { |
| 215 | + StringsNewReplacerCall() { this.getTarget().hasQualifiedName("strings", "NewReplacer") } |
| 216 | + |
| 217 | + /** |
| 218 | + * Gets an argument to this call corresponding to a string that will be |
| 219 | + * replaced. |
| 220 | + */ |
| 221 | + DataFlow::Node getAReplacedArgument() { |
| 222 | + exists(int n | n % 2 = 0 and result = this.getArgument(n)) |
| 223 | + } |
| 224 | + } |
| 225 | + |
| 226 | + /** |
| 227 | + * A configuration for tracking flow from a call to `strings.NewReplacer` to |
| 228 | + * the receiver of a call to `strings.Replacer.Replace` or |
| 229 | + * `strings.Replacer.WriteString`. |
| 230 | + */ |
| 231 | + private class StringsNewReplacerConfiguration extends DataFlowForStringsNewReplacer::Configuration { |
| 232 | + StringsNewReplacerConfiguration() { this = "StringsNewReplacerConfiguration" } |
| 233 | + |
| 234 | + override predicate isSource(DataFlow::Node source) { |
| 235 | + source instanceof StringsNewReplacerCall |
| 236 | + } |
| 237 | + |
| 238 | + override predicate isSink(DataFlow::Node sink) { |
| 239 | + exists(DataFlow::MethodCallNode call | |
| 240 | + sink = call.getReceiver() and |
| 241 | + call.getTarget().hasQualifiedName("strings", "Replacer", ["Replace", "WriteString"]) |
| 242 | + ) |
| 243 | + } |
| 244 | + } |
| 245 | + |
| 246 | + /** |
| 247 | + * A call to `strings.Replacer.Replace` or `strings.Replacer.WriteString`. |
| 248 | + */ |
| 249 | + private class StringsReplacerReplaceOrWriteString extends Range { |
| 250 | + string replacedString; |
| 251 | + |
| 252 | + StringsReplacerReplaceOrWriteString() { |
| 253 | + exists( |
| 254 | + StringsNewReplacerConfiguration config, StringsNewReplacerCall source, |
| 255 | + DataFlow::Node sink, DataFlow::MethodCallNode call |
| 256 | + | |
| 257 | + config.hasFlow(source, sink) and |
| 258 | + sink = call.getReceiver() and |
| 259 | + replacedString = source.getAReplacedArgument().getStringValue() and |
| 260 | + ( |
| 261 | + call.getTarget().hasQualifiedName("strings", "Replacer", "Replace") and |
| 262 | + this = call.getResult() |
| 263 | + or |
| 264 | + call.getTarget().hasQualifiedName("strings", "Replacer", "WriteString") and |
| 265 | + this = call.getArgument(1) |
| 266 | + ) |
| 267 | + ) |
| 268 | + } |
| 269 | + |
| 270 | + override string getReplacedString() { result = replacedString } |
| 271 | + } |
| 272 | + } |
| 273 | + |
165 | 274 | /** Provides predicates and classes for working with Printf-style formatters. */
|
166 | 275 | module Formatting {
|
167 | 276 | /**
|
|
0 commit comments