Skip to content

Commit 4cec146

Browse files
authored
Merge pull request #14853 from geoffw0/logsinks
Swift: More sinks for swift/cleartext-logging
2 parents aad8474 + 68a9154 commit 4cec146

File tree

6 files changed

+233
-56
lines changed

6 files changed

+233
-56
lines changed

swift/ql/lib/codeql/swift/StringFormat.qll

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,13 @@ class NsLog extends FormattingFunction, FreeFunction {
7878
}
7979

8080
/**
81-
* The `NSException.raise` method.
81+
* The `NSException.init` and `NSException.raise` methods.
8282
*/
8383
class NsExceptionRaise extends FormattingFunction, Method {
84-
NsExceptionRaise() { this.hasQualifiedName("NSException", "raise(_:format:arguments:)") }
84+
NsExceptionRaise() {
85+
this.hasQualifiedName("NSException", "init(name:reason:userInfo:)") or
86+
this.hasQualifiedName("NSException", "raise(_:format:arguments:)")
87+
}
8588

8689
override int getFormatParameterIndex() { result = 1 }
8790
}
@@ -91,10 +94,17 @@ class NsExceptionRaise extends FormattingFunction, Method {
9194
*/
9295
class PrintfFormat extends FormattingFunction, FreeFunction {
9396
int formatParamIndex;
97+
string modeChars;
9498

9599
PrintfFormat() {
96-
this.getShortName().matches("%printf%") and this.getParam(formatParamIndex).getName() = "format"
100+
modeChars = this.getShortName().regexpCapture("(.*)printf.*", 1) and
101+
this.getParam(formatParamIndex).getName() = "format"
97102
}
98103

99104
override int getFormatParameterIndex() { result = formatParamIndex }
105+
106+
/**
107+
* Holds if this `printf` is a variant of `sprintf`.
108+
*/
109+
predicate isSprintf() { modeChars.charAt(_) = "s" }
100110
}

swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ private module Cached {
6161
se = nodeTo.asExpr()
6262
)
6363
or
64+
// flow through autoclosure expressions (which turn value arguments into closure arguments);
65+
// if the value is tainted, it's helpful to consider the autoclosure itself to be tainted as
66+
// well for the purposes of matching sink models.
67+
nodeFrom.asExpr() = nodeTo.asExpr().(AutoClosureExpr).getExpr()
68+
or
6469
// flow through the read of a content that inherits taint
6570
exists(DataFlow::ContentSet f |
6671
readStep(nodeFrom, f, nodeTo) and

swift/ql/lib/codeql/swift/security/CleartextLoggingExtensions.qll

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import swift
44
private import codeql.swift.dataflow.DataFlow
55
private import codeql.swift.dataflow.ExternalFlow
66
private import codeql.swift.security.SensitiveExprs
7+
private import codeql.swift.StringFormat
78

89
/** A data flow sink for cleartext logging of sensitive data vulnerabilities. */
910
abstract class CleartextLoggingSink extends DataFlow::Node { }
@@ -93,6 +94,48 @@ private class CleartextLoggingFieldAdditionalFlowStep extends CleartextLoggingAd
9394
}
9495
}
9596

97+
/**
98+
* A sink that appears to be an imported C `printf` variant.
99+
*/
100+
private class PrintfCleartextLoggingSink extends CleartextLoggingSink {
101+
PrintfCleartextLoggingSink() {
102+
exists(CallExpr ce, PrintfFormat f |
103+
ce.getStaticTarget() = f and
104+
(
105+
this.asExpr() = ce.getArgument(f.getFormatParameterIndex()).getExpr() or
106+
this.asExpr() = ce.getArgument(f.getNumberOfParams() - 1).getExpr()
107+
) and
108+
not f.isSprintf()
109+
)
110+
}
111+
}
112+
113+
/**
114+
* Holds if `label` looks like the name of a logging function.
115+
*/
116+
bindingset[label]
117+
private predicate logLikeHeuristic(string label) {
118+
label.regexpMatch("(l|.*L)og([A-Z0-9].*)?") // e.g. "logMessage", "debugLog"
119+
}
120+
121+
/**
122+
* A cleartext logging sink that is determined by imprecise methods.
123+
*/
124+
class HeuristicCleartextLoggingSink extends CleartextLoggingSink {
125+
HeuristicCleartextLoggingSink() {
126+
exists(CallExpr ce, Function f, Expr e |
127+
(
128+
logLikeHeuristic(f.getShortName()) or
129+
logLikeHeuristic(f.getDeclaringDecl().(NominalTypeDecl).getName())
130+
) and
131+
ce.getStaticTarget() = f and
132+
ce.getAnArgument().getExpr() = e and
133+
e.getType().getUnderlyingType().getName() = ["String", "NSString"] and
134+
this.asExpr() = e
135+
)
136+
}
137+
}
138+
96139
private class LoggingSinks extends SinkModelCsv {
97140
override predicate row(string row) {
98141
row =
@@ -123,6 +166,8 @@ private class LoggingSinks extends SinkModelCsv {
123166
";;false;os_log(_:log:_:);;;Argument[2];log-injection",
124167
";;false;os_log(_:dso:log:_:_:);;;Argument[0,4];log-injection",
125168
";;false;os_log(_:dso:log:type:_:);;;Argument[0,4];log-injection",
169+
";NSException;true;init(name:reason:userInfo:);;;Argument[1];log-injection",
170+
";NSException;true;raise(_:format:arguments:);;;Argument[1..2];log-injection",
126171
]
127172
}
128173
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
5+
* Added additional sinks for the "Cleartext logging of sensitive information" (`swift/cleartext-logging`) query. Some of these sinks are heuristic (imprecise) in nature.

swift/ql/test/query-tests/Security/CWE-312/CleartextLoggingTest.ql

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ module CleartextLogging implements TestSig {
77
string getARelevantTag() { result = "hasCleartextLogging" }
88

99
predicate hasActualResult(Location location, string element, string tag, string value) {
10-
exists(DataFlow::Node source, DataFlow::Node sink, Expr sinkExpr |
10+
exists(DataFlow::Node source, DataFlow::Node sink |
1111
CleartextLoggingFlow::flow(source, sink) and
12-
sinkExpr = sink.asExpr() and
13-
location = sinkExpr.getLocation() and
14-
element = sinkExpr.toString() and
12+
location = sink.getLocation() and
13+
element = sink.toString() and
1514
tag = "hasCleartextLogging" and
1615
value = source.asExpr().getLocation().getStartLine().toString()
1716
)

0 commit comments

Comments
 (0)