Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions unified/ql/lib/ext/format-string.model.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
extensions:
- addsTo:
pack: codeql/unified-all
extensible: legacySinkModel
data: # namespace, type, subtypes, name, signature, ext, input, kind, provenance
- ["", "String", true, "init(_:format:)", "", "", "Argument[format:]", "format-string", "manual"]
- ["", "String", true, "init(arguments:format:)", "", "", "Argument[format:]", "format-string", "manual"]
- ["", "String", true, "init(_:format:locale:)", "", "", "Argument[format:]", "format-string", "manual"]
- ["", "String", true, "init(arguments:format:locale:)", "", "", "Argument[format:]", "format-string", "manual"]
- ["", "String", true, "localizedStringWithFormat(_:_:)", "", "", "Argument[0]", "format-string", "manual"]
- ["", "NSString", true, "init(_:format:)", "", "", "Argument[format:]", "format-string", "manual"]
- ["", "NSString", true, "init(arguments:format:)", "", "", "Argument[format:]", "format-string", "manual"]
- ["", "NSString", true, "init(_:format:locale:)", "", "", "Argument[format:]", "format-string", "manual"]
- ["", "NSString", true, "init(arguments:format:locale:)", "", "", "Argument[format:]", "format-string", "manual"]
- ["", "NSString", true, "localizedStringWithFormat(_:_:)", "", "", "Argument[0]", "format-string", "manual"]
- ["", "NSMutableString", true, "init(_:format:)", "", "", "Argument[format:]", "format-string", "manual"]
- ["", "NSMutableString", true, "init(arguments:format:)", "", "", "Argument[format:]", "format-string", "manual"]
- ["", "NSMutableString", true, "init(_:format:locale:)", "", "", "Argument[format:]", "format-string", "manual"]
- ["", "NSMutableString", true, "init(arguments:format:locale:)", "", "", "Argument[format:]", "format-string", "manual"]
- ["", "NSMutableString", true, "localizedStringWithFormat(_:_:)", "", "", "Argument[0]", "format-string", "manual"]
- ["", "NSMutableString", true, "appendFormat(_:_:)", "", "", "Argument[0]", "format-string", "manual"]
- ["", "StringProtocol", true, "appendingFormat(_:_:)", "", "", "Argument[0]", "format-string", "manual"]
- ["", "NSException", true, "init(name:reason:userInfo:)", "", "", "Argument[reason:]", "format-string", "manual"]
- ["", "NSException", true, "raise(_:arguments:format:)", "", "", "Argument[format:]", "format-string", "manual"]
- ["", "", false, "NSLog(_:_:)", "", "", "Argument[0]", "format-string", "manual"]
- ["", "", false, "NSLogv(_:_:)", "", "", "Argument[0]", "format-string", "manual"]
- ["", "", false, "printf(_:_:)", "", "", "Argument[0]", "format-string", "manual"]
- ["", "", false, "fprintf(_:_:_:)", "", "", "Argument[1]", "format-string", "manual"]
- ["", "", false, "sprintf(_:_:_:)", "", "", "Argument[1]", "format-string", "manual"]
- ["", "", false, "snprintf(_:_:_:_:)", "", "", "Argument[2]", "format-string", "manual"]
- ["", "", false, "asprintf(_:_:_:)", "", "", "Argument[1]", "format-string", "manual"]
- ["", "", false, "dprintf(_:_:_:)", "", "", "Argument[1]", "format-string", "manual"]
- ["", "", false, "vprintf(_:_:)", "", "", "Argument[0]", "format-string", "manual"]
- ["", "", false, "vfprintf(_:_:_:)", "", "", "Argument[1]", "format-string", "manual"]
- ["", "", false, "vsprintf(_:_:_:)", "", "", "Argument[1]", "format-string", "manual"]
- ["", "", false, "vsnprintf(_:_:_:_:)", "", "", "Argument[2]", "format-string", "manual"]
- ["", "", false, "vasprintf(_:_:_:)", "", "", "Argument[1]", "format-string", "manual"]
- ["", "", false, "vdprintf(_:_:_:)", "", "", "Argument[1]", "format-string", "manual"]
- ["", "", false, "vasprintf_l(_:_:_:_:)", "", "", "Argument[2]", "format-string", "manual"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Passing untrusted format strings to functions that use <code>printf</code> style formatting can lead to buffer overflows and data representation problems. An attacker may be able to exploit this weakness to crash the program or obtain sensitive information from its internal state.</p>

</overview>
<recommendation>

<p>Use a constant string literal for the format string to prevent the possibility of data flow from
an untrusted source. This also helps to prevent errors where the format arguments do not match the format string.</p>

<p>If the format string cannot be constant, ensure that it comes from a secure data source or is compiled into the source code. If you need to include a string value from the user, use an appropriate specifier (such as <code>%@</code>) in the format string and include the user provided value as a format argument.
</p>

</recommendation>
<example>

<p>In this example, the format string includes a user-controlled <code>inputString</code>:</p>

<sample src="UncontrolledFormatStringBad.swift" />

<p>To fix it, make <code>inputString</code> a format argument rather than part of the format string, as in the following code:</p>

<sample src="UncontrolledFormatStringGood.swift" />

</example>
<references>

<li>
OWASP:
<a href="https://community.owasp.org/attacks/Format_string_attack">Format string attack</a>.
</li>

</references>
</qhelp>
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* @name Uncontrolled format string
* @description Using external input in format strings can lead to exceptions or information leaks.
* @kind path-problem
* @problem.severity error
* @security-severity 7.3
* @precision high
* @id unified/swift/uncontrolled-format-string
* @tags security
* external/cwe/cwe-134
*/

import unified

/**
* A taint configuration for tainted data that reaches a format string.
*/
module TaintedFormatConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) { Models::isSource(node, _) }

predicate isSink(DataFlow::Node node) {
// TODO: the MaD format currently has the varargs argument first in the
// signature, rather than in its normal position.
// TODO: we currently model `printf(_:_:)` etc explicitly, rather than with a
// more flexible pattern as in the original Swift's `StringFormat.qll`.
// TODO: Port the variadic-call heuristic from original Swift's
// `UncontrolledFormatStringExtensions.qll` once variadic parameters are extracted.
Models::isSink(node, "format-string")
}

predicate isBarrier(DataFlow::Node node) {
// TODO: Port the numeric-type barrier from original Swift's UncontrolledFormatStringExtensions.qll.
none()
}

predicate observeDiffInformedIncrementalMode() { any() }
}

/**
* Detect taint flow of tainted data that reaches a format string.
*/
module TaintedFormatFlow = TaintTracking::Global<TaintedFormatConfig>;

import TaintedFormatFlow::PathGraph

from TaintedFormatFlow::PathNode sourceNode, TaintedFormatFlow::PathNode sinkNode
where TaintedFormatFlow::flowPath(sourceNode, sinkNode)
select sinkNode.getNode(), sourceNode, sinkNode, "This format string depends on $@.",
sourceNode.getNode(), "this user-provided value"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

print(String(format: "User input: " + inputString)) // vulnerable
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

print(String(format: "User input: %@", inputString)) // fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#select
| UncontrolledFormatString.swift:81:16:81:21 | format | UncontrolledFormatString.swift:93:24:93:77 | String(...) | UncontrolledFormatString.swift:81:16:81:21 | format | This format string depends on $@. | UncontrolledFormatString.swift:93:24:93:77 | String(...) | this user-provided value |
| UncontrolledFormatString.swift:102:24:102:30 | tainted | UncontrolledFormatString.swift:93:24:93:77 | String(...) | UncontrolledFormatString.swift:102:24:102:30 | tainted | This format string depends on $@. | UncontrolledFormatString.swift:93:24:93:77 | String(...) | this user-provided value |
| UncontrolledFormatString.swift:103:24:103:30 | tainted | UncontrolledFormatString.swift:93:24:93:77 | String(...) | UncontrolledFormatString.swift:103:24:103:30 | tainted | This format string depends on $@. | UncontrolledFormatString.swift:93:24:93:77 | String(...) | this user-provided value |
| UncontrolledFormatString.swift:105:24:105:30 | tainted | UncontrolledFormatString.swift:93:24:93:77 | String(...) | UncontrolledFormatString.swift:105:24:105:30 | tainted | This format string depends on $@. | UncontrolledFormatString.swift:93:24:93:77 | String(...) | this user-provided value |
| UncontrolledFormatString.swift:107:24:107:30 | tainted | UncontrolledFormatString.swift:93:24:93:77 | String(...) | UncontrolledFormatString.swift:107:24:107:30 | tainted | This format string depends on $@. | UncontrolledFormatString.swift:93:24:93:77 | String(...) | this user-provided value |
| UncontrolledFormatString.swift:110:26:110:50 | NSString(...) | UncontrolledFormatString.swift:93:24:93:77 | String(...) | UncontrolledFormatString.swift:110:26:110:50 | NSString(...) | This format string depends on $@. | UncontrolledFormatString.swift:93:24:93:77 | String(...) | this user-provided value |
| UncontrolledFormatString.swift:113:33:113:57 | NSString(...) | UncontrolledFormatString.swift:93:24:93:77 | String(...) | UncontrolledFormatString.swift:113:33:113:57 | NSString(...) | This format string depends on $@. | UncontrolledFormatString.swift:93:24:93:77 | String(...) | this user-provided value |
| UncontrolledFormatString.swift:120:65:120:71 | tainted | UncontrolledFormatString.swift:93:24:93:77 | String(...) | UncontrolledFormatString.swift:120:65:120:71 | tainted | This format string depends on $@. | UncontrolledFormatString.swift:93:24:93:77 | String(...) | this user-provided value |
| UncontrolledFormatString.swift:121:61:121:67 | tainted | UncontrolledFormatString.swift:93:24:93:77 | String(...) | UncontrolledFormatString.swift:121:61:121:67 | tainted | This format string depends on $@. | UncontrolledFormatString.swift:93:24:93:77 | String(...) | this user-provided value |
| UncontrolledFormatString.swift:133:39:133:45 | tainted | UncontrolledFormatString.swift:93:24:93:77 | String(...) | UncontrolledFormatString.swift:133:39:133:45 | tainted | This format string depends on $@. | UncontrolledFormatString.swift:93:24:93:77 | String(...) | this user-provided value |
| UncontrolledFormatString.swift:138:20:138:44 | NSString(...) | UncontrolledFormatString.swift:93:24:93:77 | String(...) | UncontrolledFormatString.swift:138:20:138:44 | NSString(...) | This format string depends on $@. | UncontrolledFormatString.swift:93:24:93:77 | String(...) | this user-provided value |
| UncontrolledFormatString.swift:166:42:166:48 | tainted | UncontrolledFormatString.swift:164:24:164:77 | String(...) | UncontrolledFormatString.swift:166:42:166:48 | tainted | This format string depends on $@. | UncontrolledFormatString.swift:164:24:164:77 | String(...) | this user-provided value |
| UncontrolledFormatString.swift:168:40:168:64 | NSString(...) | UncontrolledFormatString.swift:164:24:164:77 | String(...) | UncontrolledFormatString.swift:168:40:168:64 | NSString(...) | This format string depends on $@. | UncontrolledFormatString.swift:164:24:164:77 | String(...) | this user-provided value |
| UncontrolledFormatString.swift:170:47:170:71 | NSString(...) | UncontrolledFormatString.swift:164:24:164:77 | String(...) | UncontrolledFormatString.swift:170:47:170:71 | NSString(...) | This format string depends on $@. | UncontrolledFormatString.swift:164:24:164:77 | String(...) | this user-provided value |
| UncontrolledFormatString.swift:173:11:173:17 | tainted | UncontrolledFormatString.swift:164:24:164:77 | String(...) | UncontrolledFormatString.swift:173:11:173:17 | tainted | This format string depends on $@. | UncontrolledFormatString.swift:164:24:164:77 | String(...) | this user-provided value |
| UncontrolledFormatString.swift:176:20:176:26 | tainted | UncontrolledFormatString.swift:164:24:164:77 | String(...) | UncontrolledFormatString.swift:176:20:176:26 | tainted | This format string depends on $@. | UncontrolledFormatString.swift:164:24:164:77 | String(...) | this user-provided value |
| UncontrolledFormatString.swift:178:17:178:23 | tainted | UncontrolledFormatString.swift:164:24:164:77 | String(...) | UncontrolledFormatString.swift:178:17:178:23 | tainted | This format string depends on $@. | UncontrolledFormatString.swift:164:24:164:77 | String(...) | this user-provided value |
| UncontrolledFormatString.swift:180:23:180:29 | tainted | UncontrolledFormatString.swift:164:24:164:77 | String(...) | UncontrolledFormatString.swift:180:23:180:29 | tainted | This format string depends on $@. | UncontrolledFormatString.swift:164:24:164:77 | String(...) | this user-provided value |
| UncontrolledFormatString.swift:182:31:182:37 | tainted | UncontrolledFormatString.swift:164:24:164:77 | String(...) | UncontrolledFormatString.swift:182:31:182:37 | tainted | This format string depends on $@. | UncontrolledFormatString.swift:164:24:164:77 | String(...) | this user-provided value |
edges
| UncontrolledFormatString.swift:79:14:79:19 | format | UncontrolledFormatString.swift:80:22:82:5 | FunctionExpr [format] | provenance | |
| UncontrolledFormatString.swift:80:22:82:5 | FunctionExpr [format] | UncontrolledFormatString.swift:81:16:81:21 | format | provenance | |
| UncontrolledFormatString.swift:93:9:93:15 | tainted | UncontrolledFormatString.swift:102:24:102:30 | tainted | provenance | |
| UncontrolledFormatString.swift:93:9:93:15 | tainted | UncontrolledFormatString.swift:103:24:103:30 | tainted | provenance | |
| UncontrolledFormatString.swift:93:9:93:15 | tainted | UncontrolledFormatString.swift:105:24:105:30 | tainted | provenance | |
| UncontrolledFormatString.swift:93:9:93:15 | tainted | UncontrolledFormatString.swift:107:24:107:30 | tainted | provenance | |
| UncontrolledFormatString.swift:93:9:93:15 | tainted | UncontrolledFormatString.swift:110:43:110:49 | tainted | provenance | |
| UncontrolledFormatString.swift:93:9:93:15 | tainted | UncontrolledFormatString.swift:113:50:113:56 | tainted | provenance | |
| UncontrolledFormatString.swift:93:9:93:15 | tainted | UncontrolledFormatString.swift:118:11:118:17 | tainted | provenance | |
| UncontrolledFormatString.swift:93:9:93:15 | tainted | UncontrolledFormatString.swift:120:65:120:71 | tainted | provenance | |
| UncontrolledFormatString.swift:93:9:93:15 | tainted | UncontrolledFormatString.swift:121:61:121:67 | tainted | provenance | |
| UncontrolledFormatString.swift:93:9:93:15 | tainted | UncontrolledFormatString.swift:133:39:133:45 | tainted | provenance | |
| UncontrolledFormatString.swift:93:9:93:15 | tainted | UncontrolledFormatString.swift:138:37:138:43 | tainted | provenance | |
| UncontrolledFormatString.swift:93:24:93:77 | String(...) | UncontrolledFormatString.swift:93:9:93:15 | tainted | provenance | |
| UncontrolledFormatString.swift:110:43:110:49 | tainted | UncontrolledFormatString.swift:110:26:110:50 | NSString(...) | provenance | |
| UncontrolledFormatString.swift:113:50:113:56 | tainted | UncontrolledFormatString.swift:113:33:113:57 | NSString(...) | provenance | |
| UncontrolledFormatString.swift:118:11:118:17 | tainted | UncontrolledFormatString.swift:79:14:79:19 | format | provenance | |
| UncontrolledFormatString.swift:138:37:138:43 | tainted | UncontrolledFormatString.swift:138:20:138:44 | NSString(...) | provenance | |
| UncontrolledFormatString.swift:164:9:164:15 | tainted | UncontrolledFormatString.swift:166:42:166:48 | tainted | provenance | |
| UncontrolledFormatString.swift:164:9:164:15 | tainted | UncontrolledFormatString.swift:168:57:168:63 | tainted | provenance | |
| UncontrolledFormatString.swift:164:9:164:15 | tainted | UncontrolledFormatString.swift:170:64:170:70 | tainted | provenance | |
| UncontrolledFormatString.swift:164:9:164:15 | tainted | UncontrolledFormatString.swift:173:11:173:17 | tainted | provenance | |
| UncontrolledFormatString.swift:164:9:164:15 | tainted | UncontrolledFormatString.swift:176:20:176:26 | tainted | provenance | |
| UncontrolledFormatString.swift:164:9:164:15 | tainted | UncontrolledFormatString.swift:178:17:178:23 | tainted | provenance | |
| UncontrolledFormatString.swift:164:9:164:15 | tainted | UncontrolledFormatString.swift:180:23:180:29 | tainted | provenance | |
| UncontrolledFormatString.swift:164:9:164:15 | tainted | UncontrolledFormatString.swift:182:31:182:37 | tainted | provenance | |
| UncontrolledFormatString.swift:164:24:164:77 | String(...) | UncontrolledFormatString.swift:164:9:164:15 | tainted | provenance | |
| UncontrolledFormatString.swift:168:57:168:63 | tainted | UncontrolledFormatString.swift:168:40:168:64 | NSString(...) | provenance | |
| UncontrolledFormatString.swift:170:64:170:70 | tainted | UncontrolledFormatString.swift:170:47:170:71 | NSString(...) | provenance | |
nodes
| UncontrolledFormatString.swift:79:14:79:19 | format | semmle.label | format |
| UncontrolledFormatString.swift:80:22:82:5 | FunctionExpr [format] | semmle.label | FunctionExpr [format] |
| UncontrolledFormatString.swift:81:16:81:21 | format | semmle.label | format |
| UncontrolledFormatString.swift:93:9:93:15 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:93:24:93:77 | String(...) | semmle.label | String(...) |
| UncontrolledFormatString.swift:102:24:102:30 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:103:24:103:30 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:105:24:105:30 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:107:24:107:30 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:110:26:110:50 | NSString(...) | semmle.label | NSString(...) |
| UncontrolledFormatString.swift:110:43:110:49 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:113:33:113:57 | NSString(...) | semmle.label | NSString(...) |
| UncontrolledFormatString.swift:113:50:113:56 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:118:11:118:17 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:120:65:120:71 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:121:61:121:67 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:133:39:133:45 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:138:20:138:44 | NSString(...) | semmle.label | NSString(...) |
| UncontrolledFormatString.swift:138:37:138:43 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:164:9:164:15 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:164:24:164:77 | String(...) | semmle.label | String(...) |
| UncontrolledFormatString.swift:166:42:166:48 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:168:40:168:64 | NSString(...) | semmle.label | NSString(...) |
| UncontrolledFormatString.swift:168:57:168:63 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:170:47:170:71 | NSString(...) | semmle.label | NSString(...) |
| UncontrolledFormatString.swift:170:64:170:70 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:173:11:173:17 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:176:20:176:26 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:178:17:178:23 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:180:23:180:29 | tainted | semmle.label | tainted |
| UncontrolledFormatString.swift:182:31:182:37 | tainted | semmle.label | tainted |
subpaths
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
query: queries/security/CWE-134/UncontrolledFormatString.ql
postprocess:
- utils/test/InlineExpectationsTestQuery.ql
Loading
Loading