Skip to content

Commit 14cc27e

Browse files
authored
Merge pull request #11910 from owen-mc/go/log-injection-sanitizer-newreplacer-replace
Add missing string replacement sanitizers to log-injection and string-break
2 parents e6aebd9 + 9a5e1f5 commit 14cc27e

9 files changed

+5096
-32
lines changed

config/identical-files.json

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll",
3030
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll",
3131
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll",
32+
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImplForStringsNewReplacer.qll",
3233
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll",
3334
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
3435
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",

go/ql/lib/semmle/go/StringOps.qll

+109
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44

55
import go
6+
private import semmle.go.dataflow.DataFlowForStringsNewReplacer
67

78
/** Provides predicates and classes for working with string operations. */
89
module StringOps {
@@ -162,6 +163,114 @@ module StringOps {
162163
}
163164
}
164165

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+
165274
/** Provides predicates and classes for working with Printf-style formatters. */
166275
module Formatting {
167276
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Provides a library for local (intra-procedural) and global (inter-procedural)
3+
* data flow analysis: deciding whether data can flow from a _source_ to a
4+
* _sink_.
5+
*
6+
* Unless configured otherwise, _flow_ means that the exact value of
7+
* the source may reach the sink. We do not track flow across pointer
8+
* dereferences or array indexing. To track these types of flow, where the
9+
* exact value may not be preserved, import
10+
* `semmle.code.go.dataflow.TaintTracking`.
11+
*
12+
* To use global (interprocedural) data flow, extend the class
13+
* `DataFlow::Configuration` as documented on that class. To use local
14+
* (intraprocedural) data flow, invoke `DataFlow::localFlow` or
15+
* `DataFlow::LocalFlowStep` with arguments of type `DataFlow::Node`.
16+
*/
17+
18+
import go
19+
20+
/**
21+
* Provides a library for local (intra-procedural) and global (inter-procedural)
22+
* data flow analysis.
23+
*/
24+
module DataFlowForStringsNewReplacer {
25+
import semmle.go.dataflow.internal.DataFlowImplForStringsNewReplacer
26+
import Properties
27+
}

0 commit comments

Comments
 (0)