Skip to content

Commit 8b5114d

Browse files
committed
python: Add standard customization setup
- modernize the sanitizer, but do not make it less specific
1 parent 20d5454 commit 8b5114d

File tree

3 files changed

+90
-14
lines changed

3 files changed

+90
-14
lines changed
Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,35 @@
1+
/**
2+
* Provides a taint-tracking configuration for tracking untrusted user input used in log entries.
3+
*
4+
* Note, for performance reasons: only import this file if
5+
* `LogInjection::Configuration` is needed, otherwise
6+
* `LogInjectionCustomizations` should be imported instead.
7+
*/
8+
19
import python
2-
import semmle.python.Concepts
3-
import semmle.python.Concepts
410
import semmle.python.dataflow.new.DataFlow
511
import semmle.python.dataflow.new.TaintTracking
6-
import semmle.python.dataflow.new.RemoteFlowSources
712

813
/**
9-
* A taint-tracking configuration for tracking untrusted user input used in log entries.
14+
* Provides a taint-tracking configuration for tracking untrusted user input used in log entries.
1015
*/
11-
class LogInjectionFlowConfig extends TaintTracking::Configuration {
12-
LogInjectionFlowConfig() { this = "LogInjectionFlowConfig" }
16+
module LogInjection {
17+
import LogInjectionCustomizations::LogInjection
18+
19+
/**
20+
* A taint-tracking configuration for tracking untrusted user input used in log entries.
21+
*/
22+
class Configuration extends TaintTracking::Configuration {
23+
Configuration() { this = "LogInjection" }
24+
25+
override predicate isSource(DataFlow::Node source) { source instanceof Source }
1326

14-
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
27+
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
1528

16-
override predicate isSink(DataFlow::Node sink) { sink = any(Logging write).getAnInput() }
29+
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
1730

18-
override predicate isSanitizer(DataFlow::Node node) {
19-
exists(CallNode call |
20-
node.asCfgNode() = call.getFunction().(AttrNode).getObject("replace") and
21-
call.getArg(0).getNode().(StrConst).getText() in ["\r\n", "\n"]
22-
)
31+
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
32+
guard instanceof SanitizerGuard
33+
}
2334
}
2435
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* Provides default sources, sinks and sanitizers for detecting
3+
* "command injection"
4+
* vulnerabilities, as well as extension points for adding your own.
5+
*/
6+
7+
private import python
8+
private import semmle.python.dataflow.new.DataFlow
9+
private import semmle.python.Concepts
10+
private import semmle.python.dataflow.new.RemoteFlowSources
11+
private import semmle.python.dataflow.new.BarrierGuards
12+
13+
/**
14+
* Provides default sources, sinks and sanitizers for detecting
15+
* "command injection"
16+
* vulnerabilities, as well as extension points for adding your own.
17+
*/
18+
module LogInjection {
19+
/**
20+
* A data flow source for "command injection" vulnerabilities.
21+
*/
22+
abstract class Source extends DataFlow::Node { }
23+
24+
/**
25+
* A data flow sink for "command injection" vulnerabilities.
26+
*/
27+
abstract class Sink extends DataFlow::Node { }
28+
29+
/**
30+
* A sanitizer for "command injection" vulnerabilities.
31+
*/
32+
abstract class Sanitizer extends DataFlow::Node { }
33+
34+
/**
35+
* A sanitizer guard for "command injection" vulnerabilities.
36+
*/
37+
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
38+
39+
/**
40+
* A source of remote user input, considered as a flow source.
41+
*/
42+
class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
43+
44+
/**
45+
* A logging operation, considered as a flow sink.
46+
*/
47+
class LoggingAsSink extends Sink {
48+
LoggingAsSink() { this = any(Logging write).getAnInput() }
49+
}
50+
51+
/**
52+
* A comparison with a constant string, considered as a sanitizer-guard.
53+
*/
54+
class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { }
55+
56+
/**
57+
* A call to replace line breaks functions as a sanitizer.
58+
*/
59+
class ReplaceLineBreaksSanitizer extends Sanitizer, DataFlow::CallCfgNode {
60+
ReplaceLineBreaksSanitizer() {
61+
this.getFunction().(DataFlow::AttrRead).getAttributeName() = "replace" and
62+
this.getArg(0).asExpr().(StrConst).getText() in ["\r\n", "\n"]
63+
}
64+
}
65+
}

python/ql/src/Security/CWE-117/LogInjection.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import python
1414
import semmle.python.security.dataflow.LogInjection
1515
import DataFlow::PathGraph
1616

17-
from LogInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
17+
from LogInjection::Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
1818
where config.hasFlowPath(source, sink)
1919
select sink.getNode(), source, sink, "$@ flows to log entry.", source.getNode(),
2020
"User-provided value"

0 commit comments

Comments
 (0)