Merge pull request #12329 from geoffw0/network

Swift: Modernize the cleartext-* queries
This commit is contained in:
Geoffrey White
2023-02-28 13:04:10 +00:00
committed by GitHub
11 changed files with 449 additions and 301 deletions

View File

@@ -93,6 +93,7 @@ private module Frameworks {
private import codeql.swift.frameworks.StandardLibrary.WebView
private import codeql.swift.frameworks.Alamofire.Alamofire
private import codeql.swift.security.CleartextLoggingExtensions
private import codeql.swift.security.CleartextStorageDatabaseExtensions
private import codeql.swift.security.PathInjectionExtensions
private import codeql.swift.security.PredicateInjectionExtensions
}

View File

@@ -0,0 +1,141 @@
/**
* Provides classes and predicates for reasoning about cleartext database
* storage vulnerabilities.
*/
import swift
import codeql.swift.security.SensitiveExprs
import codeql.swift.dataflow.DataFlow
import codeql.swift.dataflow.ExternalFlow
/**
* A dataflow sink for cleartext database storage vulnerabilities. That is,
* a `DataFlow::Node` that is something stored in a local database.
*/
abstract class CleartextStorageDatabaseSink extends DataFlow::Node { }
/**
* A sanitizer for cleartext database storage vulnerabilities.
*/
abstract class CleartextStorageDatabaseSanitizer extends DataFlow::Node { }
/**
* A unit class for adding additional taint steps.
*/
class CleartextStorageDatabaseAdditionalTaintStep extends Unit {
/**
* Holds if the step from `node1` to `node2` should be considered a taint
* step for paths related to cleartext database storage vulnerabilities.
*/
abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo);
}
/**
* A `DataFlow::Node` that is an expression stored with the Core Data library.
*/
private class CoreDataStore extends CleartextStorageDatabaseSink {
CoreDataStore() {
// values written into Core Data objects through `set*Value` methods are a sink.
exists(CallExpr call |
call.getStaticTarget()
.(MethodDecl)
.hasQualifiedName("NSManagedObject",
["setValue(_:forKey:)", "setPrimitiveValue(_:forKey:)"]) and
call.getArgument(0).getExpr() = this.asExpr()
)
or
// any write into a class derived from `NSManagedObject` is a sink. For
// example in `coreDataObj.data = sensitive` the post-update node corresponding
// with `coreDataObj.data` is a sink.
// (ideally this would be only members with the `@NSManaged` attribute)
exists(ClassOrStructDecl cd, Expr e |
cd.getABaseTypeDecl*().getName() = "NSManagedObject" and
this.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = e and
e.getFullyConverted().getType() = cd.getType() and
not e.(DeclRefExpr).getDecl() instanceof SelfParamDecl
)
}
}
/**
* A `DataFlow::Node` that is an expression stored with the Realm database
* library.
*/
private class RealmStore extends CleartextStorageDatabaseSink instanceof DataFlow::PostUpdateNode {
RealmStore() {
// any write into a class derived from `RealmSwiftObject` is a sink. For
// example in `realmObj.data = sensitive` the post-update node corresponding
// with `realmObj.data` is a sink.
exists(ClassOrStructDecl cd, Expr e |
cd.getABaseTypeDecl*().getName() = "RealmSwiftObject" and
this.getPreUpdateNode().asExpr() = e and
e.getFullyConverted().getType() = cd.getType() and
not e.(DeclRefExpr).getDecl() instanceof SelfParamDecl
)
}
}
private class CleartextStorageDatabaseSinks extends SinkModelCsv {
override predicate row(string row) {
row =
[
// GRDB sinks
";Database;true;allStatements(sql:arguments:);;;Argument[1];database-store",
";Database;true;execute(sql:arguments:);;;Argument[1];database-store",
";SQLRequest;true;init(sql:arguments:adapter:cached:);;;Argument[1];database-store",
";SQL;true;init(sql:arguments:);;;Argument[1];database-store",
";SQL;true;append(sql:arguments:);;;Argument[1];database-store",
";SQLStatementCursor;true;init(database:sql:arguments:prepFlags:);;;Argument[2];database-store",
";TableRecord;true;select(sql:arguments:);;;Argument[1];database-store",
";TableRecord;true;select(sql:arguments:as:);;;Argument[1];database-store",
";TableRecord;true;filter(sql:arguments:);;;Argument[1];database-store",
";TableRecord;true;order(sql:arguments:);;;Argument[1];database-store",
";Row;true;fetchCursor(_:sql:arguments:adapter:);;;Argument[2];database-store",
";Row;true;fetchAll(_:sql:arguments:adapter:);;;Argument[2];database-store",
";Row;true;fetchSet(_:sql:arguments:adapter:);;;Argument[2];database-store",
";Row;true;fetchOne(_:sql:arguments:adapter:);;;Argument[2];database-store",
";DatabaseValueConvertible;true;fetchCursor(_:sql:arguments:adapter:);;;Argument[2];database-store",
";DatabaseValueConvertible;true;fetchAll(_:sql:arguments:adapter:);;;Argument[2];database-store",
";DatabaseValueConvertible;true;fetchSet(_:sql:arguments:adapter:);;;Argument[2];database-store",
";DatabaseValueConvertible;true;fetchOne(_:sql:arguments:adapter:);;;Argument[2];database-store",
";FetchableRecord;true;fetchCursor(_:sql:arguments:adapter:);;;Argument[2];database-store",
";FetchableRecord;true;fetchAll(_:sql:arguments:adapter:);;;Argument[2];database-store",
";FetchableRecord;true;fetchSet(_:sql:arguments:adapter:);;;Argument[2];database-store",
";FetchableRecord;true;fetchOne(_:sql:arguments:adapter:);;;Argument[2];database-store",
";FetchableRecord;true;fetchCursor(_:arguments:adapter:);;;Argument[1];database-store",
";FetchableRecord;true;fetchAll(_:arguments:adapter:);;;Argument[1];database-store",
";FetchableRecord;true;fetchSet(_:arguments:adapter:);;;Argument[1];database-store",
";FetchableRecord;true;fetchOne(_:arguments:adapter:);;;Argument[1];database-store",
";Statement;true;execute(arguments:);;;Argument[0];database-store",
";CommonTableExpression;true;init(recursive:named:columns:sql:arguments:);;;Argument[4];database-store",
";Statement;true;setArguments(_:);;;Argument[0];database-store"
]
}
}
/**
* An encryption sanitizer for cleartext database storage vulnerabilities.
*/
private class CleartextStorageDatabaseEncryptionSanitizer extends CleartextStorageDatabaseSanitizer {
CleartextStorageDatabaseEncryptionSanitizer() { this.asExpr() instanceof EncryptedExpr }
}
/**
* An additional taint step for cleartext database storage vulnerabilities.
* Needed until we have proper content flow through arrays.
*/
private class CleartextStorageDatabaseArrayAdditionalTaintStep extends CleartextStorageDatabaseAdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
exists(ArrayExpr arr |
nodeFrom.asExpr() = arr.getAnElement() and
nodeTo.asExpr() = arr
)
}
}
/**
* A sink defined in a CSV model.
*/
private class DefaultCleartextStorageDatabaseSink extends CleartextStorageDatabaseSink {
DefaultCleartextStorageDatabaseSink() { sinkNode(this, "database-store") }
}

View File

@@ -0,0 +1,49 @@
/**
* Provides a taint-tracking configuration for reasoning about cleartext
* database storage vulnerabilities.
*/
import swift
import codeql.swift.security.SensitiveExprs
import codeql.swift.dataflow.DataFlow
import codeql.swift.dataflow.TaintTracking
import codeql.swift.security.CleartextStorageDatabaseExtensions
/**
* A taint configuration from sensitive information to expressions that are
* transmitted over a network.
*/
class CleartextStorageConfig extends TaintTracking::Configuration {
CleartextStorageConfig() { this = "CleartextStorageConfig" }
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof SensitiveExpr }
override predicate isSink(DataFlow::Node node) { node instanceof CleartextStorageDatabaseSink }
override predicate isSanitizer(DataFlow::Node sanitizer) {
sanitizer instanceof CleartextStorageDatabaseSanitizer
}
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
any(CleartextStorageDatabaseAdditionalTaintStep s).step(nodeFrom, nodeTo)
}
override predicate isSanitizerIn(DataFlow::Node node) {
// make sources barriers so that we only report the closest instance
isSource(node)
}
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
// flow out from fields of an `NSManagedObject` or `RealmSwiftObject` at the sink,
// for example in `realmObj.data = sensitive`.
isSink(node) and
exists(ClassOrStructDecl cd, Decl cx |
cd.getABaseTypeDecl*().getName() = ["NSManagedObject", "RealmSwiftObject"] and
cx.asNominalTypeDecl() = cd and
c.getAReadContent().(DataFlow::Content::FieldContent).getField() = cx.getAMember()
)
or
// any default implicit reads
super.allowImplicitRead(node, c)
}
}

View File

@@ -0,0 +1,88 @@
/**
* Provides classes and predicates for reasoning about cleartext preferences
* storage vulnerabilities.
*/
import swift
import codeql.swift.security.SensitiveExprs
import codeql.swift.dataflow.DataFlow
import codeql.swift.dataflow.ExternalFlow
/**
* A dataflow sink for cleartext preferences storage vulnerabilities. That is,
* a `DataFlow::Node` of something that gets stored in an application
* preference store.
*/
abstract class CleartextStoragePreferencesSink extends DataFlow::Node {
abstract string getStoreName();
}
/**
* A sanitizer for cleartext preferences storage vulnerabilities.
*/
abstract class CleartextStoragePreferencesSanitizer extends DataFlow::Node { }
/**
* A unit class for adding additional taint steps.
*/
class CleartextStoragePreferencesAdditionalTaintStep extends Unit {
/**
* Holds if the step from `node1` to `node2` should be considered a taint
* step for paths related to cleartext preferences storage vulnerabilities.
*/
abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo);
}
/** The `DataFlow::Node` of an expression that gets written to the user defaults database */
private class UserDefaultsStore extends CleartextStoragePreferencesSink {
UserDefaultsStore() {
exists(CallExpr call |
call.getStaticTarget().(MethodDecl).hasQualifiedName("UserDefaults", "set(_:forKey:)") and
call.getArgument(0).getExpr() = this.asExpr()
)
}
override string getStoreName() { result = "the user defaults database" }
}
/** The `DataFlow::Node` of an expression that gets written to the iCloud-backed NSUbiquitousKeyValueStore */
private class NSUbiquitousKeyValueStore extends CleartextStoragePreferencesSink {
NSUbiquitousKeyValueStore() {
exists(CallExpr call |
call.getStaticTarget()
.(MethodDecl)
.hasQualifiedName("NSUbiquitousKeyValueStore", "set(_:forKey:)") and
call.getArgument(0).getExpr() = this.asExpr()
)
}
override string getStoreName() { result = "iCloud" }
}
/**
* A more complicated case, this is a macOS-only way of writing to
* NSUserDefaults by modifying the `NSUserDefaultsController.values: Any`
* object via reflection (`perform(Selector)`) or the `NSKeyValueCoding`,
* `NSKeyValueBindingCreation` APIs. (TODO)
*/
private class NSUserDefaultsControllerStore extends CleartextStoragePreferencesSink {
NSUserDefaultsControllerStore() { none() }
override string getStoreName() { result = "the user defaults database" }
}
/**
* An encryption sanitizer for cleartext preferences storage vulnerabilities.
*/
private class CleartextStoragePreferencesEncryptionSanitizer extends CleartextStoragePreferencesSanitizer {
CleartextStoragePreferencesEncryptionSanitizer() { this.asExpr() instanceof EncryptedExpr }
}
/**
* A sink defined in a CSV model.
*/
private class DefaultCleartextStoragePreferencesSink extends CleartextStoragePreferencesSink {
DefaultCleartextStoragePreferencesSink() { sinkNode(this, "preferences-store") }
override string getStoreName() { result = "a preferences store" }
}

View File

@@ -0,0 +1,35 @@
/**
* Provides a taint-tracking configuration for reasoning about cleartext
* preferences storage vulnerabilities.
*/
import swift
import codeql.swift.security.SensitiveExprs
import codeql.swift.dataflow.DataFlow
import codeql.swift.dataflow.TaintTracking
import codeql.swift.security.CleartextStoragePreferencesExtensions
/**
* A taint configuration from sensitive information to expressions that are
* stored as preferences.
*/
class CleartextStorageConfig extends TaintTracking::Configuration {
CleartextStorageConfig() { this = "CleartextStorageConfig" }
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof SensitiveExpr }
override predicate isSink(DataFlow::Node node) { node instanceof CleartextStoragePreferencesSink }
override predicate isSanitizer(DataFlow::Node sanitizer) {
sanitizer instanceof CleartextStoragePreferencesSanitizer
}
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
any(CleartextStoragePreferencesAdditionalTaintStep s).step(nodeFrom, nodeTo)
}
override predicate isSanitizerIn(DataFlow::Node node) {
// make sources barriers so that we only report the closest instance
this.isSource(node)
}
}

View File

@@ -0,0 +1,95 @@
/**
* Provides classes and predicates for reasoning about cleartext transmission
* vulnerabilities.
*/
import swift
import codeql.swift.security.SensitiveExprs
import codeql.swift.dataflow.DataFlow
import codeql.swift.dataflow.ExternalFlow
/**
* A dataflow sink for cleartext transmission vulnerabilities. That is,
* a `DataFlow::Node` of something that is transmitted over a network.
*/
abstract class CleartextTransmissionSink extends DataFlow::Node { }
/**
* A sanitizer for cleartext transmission vulnerabilities.
*/
abstract class CleartextTransmissionSanitizer extends DataFlow::Node { }
/**
* A unit class for adding additional taint steps.
*/
class CleartextTransmissionAdditionalTaintStep extends Unit {
/**
* Holds if the step from `node1` to `node2` should be considered a taint
* step for paths related to cleartext transmission vulnerabilities.
*/
abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo);
}
/**
* An `Expr` that is transmitted with `NWConnection.send`.
*/
private class NWConnectionSendSink extends CleartextTransmissionSink {
NWConnectionSendSink() {
// `content` arg to `NWConnection.send` is a sink
exists(CallExpr call |
call.getStaticTarget()
.(MethodDecl)
.hasQualifiedName("NWConnection", "send(content:contentContext:isComplete:completion:)") and
call.getArgument(0).getExpr() = this.asExpr()
)
}
}
/**
* An `Expr` that is used to form a `URL`. Such expressions are very likely to
* be transmitted over a network, because that's what URLs are for.
*/
private class UrlSink extends CleartextTransmissionSink {
UrlSink() {
// `string` arg in `URL.init` is a sink
// (we assume here that the URL goes on to be used in a network operation)
exists(CallExpr call |
call.getStaticTarget()
.(MethodDecl)
.hasQualifiedName("URL", ["init(string:)", "init(string:relativeTo:)"]) and
call.getArgument(0).getExpr() = this.asExpr()
)
}
}
/**
* An `Expr` that transmitted through the Alamofire library.
*/
private class AlamofireTransmittedSink extends CleartextTransmissionSink {
AlamofireTransmittedSink() {
// sinks are the first argument containing the URL, and the `parameters`
// and `headers` arguments to appropriate methods of `Session`.
exists(CallExpr call, string fName |
call.getStaticTarget().(MethodDecl).hasQualifiedName("Session", fName) and
fName.regexpMatch("(request|streamRequest|download)\\(.*") and
(
call.getArgument(0).getExpr() = this.asExpr() or
call.getArgumentWithLabel(["headers", "parameters"]).getExpr() = this.asExpr()
)
)
}
}
/**
* An encryption sanitizer for cleartext transmission vulnerabilities.
*/
private class CleartextTransmissionEncryptionSanitizer extends CleartextTransmissionSanitizer {
CleartextTransmissionEncryptionSanitizer() { this.asExpr() instanceof EncryptedExpr }
}
/**
* A sink defined in a CSV model.
*/
private class DefaultCleartextTransmissionSink extends CleartextTransmissionSink {
DefaultCleartextTransmissionSink() { sinkNode(this, "transmission") }
}

View File

@@ -0,0 +1,35 @@
/**
* Provides a taint-tracking configuration for reasoning about cleartext
* transmission vulnerabilities.
*/
import swift
import codeql.swift.security.SensitiveExprs
import codeql.swift.dataflow.DataFlow
import codeql.swift.dataflow.TaintTracking
import codeql.swift.security.CleartextTransmissionExtensions
/**
* A taint configuration from sensitive information to expressions that are
* transmitted over a network.
*/
class CleartextTransmissionConfig extends TaintTracking::Configuration {
CleartextTransmissionConfig() { this = "CleartextTransmissionConfig" }
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof SensitiveExpr }
override predicate isSink(DataFlow::Node node) { node instanceof CleartextTransmissionSink }
override predicate isSanitizer(DataFlow::Node sanitizer) {
sanitizer instanceof CleartextTransmissionSanitizer
}
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
any(CleartextTransmissionAdditionalTaintStep s).step(nodeFrom, nodeTo)
}
override predicate isSanitizerIn(DataFlow::Node node) {
// make sources barriers so that we only report the closest instance
isSource(node)
}
}

View File

@@ -6,7 +6,7 @@
import swift
import codeql.swift.dataflow.DataFlow
private import codeql.swift.dataflow.ExternalFlow
import codeql.swift.dataflow.ExternalFlow
/**
* A dataflow sink for SQL injection vulnerabilities.

View File

@@ -12,160 +12,10 @@
*/
import swift
import codeql.swift.security.SensitiveExprs
import codeql.swift.dataflow.DataFlow
import codeql.swift.dataflow.TaintTracking
import codeql.swift.security.CleartextStorageDatabaseQuery
import DataFlow::PathGraph
/**
* A `DataFlow::Node` that is something stored in a local database.
*/
abstract class Stored extends DataFlow::Node { }
/**
* A `DataFlow::Node` that is an expression stored with the Core Data library.
*/
class CoreDataStore extends Stored {
CoreDataStore() {
// values written into Core Data objects through `set*Value` methods are a sink.
exists(CallExpr call |
call.getStaticTarget()
.(MethodDecl)
.hasQualifiedName("NSManagedObject",
["setValue(_:forKey:)", "setPrimitiveValue(_:forKey:)"]) and
call.getArgument(0).getExpr() = this.asExpr()
)
or
// any write into a class derived from `NSManagedObject` is a sink. For
// example in `coreDataObj.data = sensitive` the post-update node corresponding
// with `coreDataObj.data` is a sink.
// (ideally this would be only members with the `@NSManaged` attribute)
exists(ClassOrStructDecl cd, Expr e |
cd.getABaseTypeDecl*().getName() = "NSManagedObject" and
this.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = e and
e.getFullyConverted().getType() = cd.getType() and
not e.(DeclRefExpr).getDecl() instanceof SelfParamDecl
)
}
}
/**
* A `DataFlow::Node` that is an expression stored with the Realm database
* library.
*/
class RealmStore extends Stored instanceof DataFlow::PostUpdateNode {
RealmStore() {
// any write into a class derived from `RealmSwiftObject` is a sink. For
// example in `realmObj.data = sensitive` the post-update node corresponding
// with `realmObj.data` is a sink.
exists(ClassOrStructDecl cd, Expr e |
cd.getABaseTypeDecl*().getName() = "RealmSwiftObject" and
this.getPreUpdateNode().asExpr() = e and
e.getFullyConverted().getType() = cd.getType() and
not e.(DeclRefExpr).getDecl() instanceof SelfParamDecl
)
}
}
/**
* A `DataFlow::Node` that is an expression stored with the GRDB library.
*/
class GrdbStore extends Stored {
GrdbStore() {
exists(CallExpr call, MethodDecl method |
call.getStaticTarget() = method and
call.getArgumentWithLabel("arguments").getExpr() = this.asExpr()
|
method
.hasQualifiedName("Database",
["allStatements(sql:arguments:)", "execute(sql:arguments:)",])
or
method.hasQualifiedName("SQLRequest", "init(sql:arguments:adapter:cached:)")
or
method.hasQualifiedName("SQL", ["init(sql:arguments:)", "append(sql:arguments:)"])
or
method.hasQualifiedName("SQLStatementCursor", "init(database:sql:arguments:prepFlags:)")
or
method
.hasQualifiedName("TableRecord",
[
"select(sql:arguments:)", "select(sql:arguments:as:)", "filter(sql:arguments:)",
"order(sql:arguments:)"
])
or
method
.hasQualifiedName(["Row", "DatabaseValueConvertible", "FetchableRecord"],
[
"fetchCursor(_:sql:arguments:adapter:)", "fetchAll(_:sql:arguments:adapter:)",
"fetchSet(_:sql:arguments:adapter:)", "fetchOne(_:sql:arguments:adapter:)"
])
or
method
.hasQualifiedName("FetchableRecord",
[
"fetchCursor(_:arguments:adapter:)", "fetchAll(_:arguments:adapter:)",
"fetchSet(_:arguments:adapter:)", "fetchOne(_:arguments:adapter:)",
])
or
method.hasQualifiedName("Statement", ["execute(arguments:)"])
or
method
.hasQualifiedName("CommonTableExpression", "init(recursive:named:columns:sql:arguments:)")
)
or
exists(CallExpr call, MethodDecl method |
call.getStaticTarget() = method and
call.getArgument(0).getExpr() = this.asExpr()
|
method.hasQualifiedName("Statement", "setArguments(_:)")
)
}
}
/**
* A taint configuration from sensitive information to expressions that are
* transmitted over a network.
*/
class CleartextStorageConfig extends TaintTracking::Configuration {
CleartextStorageConfig() { this = "CleartextStorageConfig" }
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof SensitiveExpr }
override predicate isSink(DataFlow::Node node) { node instanceof Stored }
override predicate isSanitizerIn(DataFlow::Node node) {
// make sources barriers so that we only report the closest instance
isSource(node)
}
override predicate isSanitizer(DataFlow::Node node) {
// encryption barrier
node.asExpr() instanceof EncryptedExpr
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
// Needed until we have proper content flow through arrays
exists(ArrayExpr arr |
node1.asExpr() = arr.getAnElement() and
node2.asExpr() = arr
)
}
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
// flow out from fields of an `NSManagedObject` or `RealmSwiftObject` at the sink,
// for example in `realmObj.data = sensitive`.
isSink(node) and
exists(ClassOrStructDecl cd, Decl cx |
cd.getABaseTypeDecl*().getName() = ["NSManagedObject", "RealmSwiftObject"] and
cx.asNominalTypeDecl() = cd and
c.getAReadContent().(DataFlow::Content::FieldContent).getField() = cx.getAMember()
)
or
// any default implicit reads
super.allowImplicitRead(node, c)
}
}
/**
* Gets a prettier node to use in the results.
*/

View File

@@ -12,88 +12,10 @@
*/
import swift
import codeql.swift.security.SensitiveExprs
import codeql.swift.dataflow.DataFlow
import codeql.swift.dataflow.TaintTracking
import codeql.swift.security.CleartextTransmissionQuery
import DataFlow::PathGraph
/**
* An `Expr` that is transmitted over a network.
*/
abstract class Transmitted extends Expr { }
/**
* An `Expr` that is transmitted with `NWConnection.send`.
*/
class NWConnectionSend extends Transmitted {
NWConnectionSend() {
// `content` arg to `NWConnection.send` is a sink
exists(CallExpr call |
call.getStaticTarget()
.(MethodDecl)
.hasQualifiedName("NWConnection", "send(content:contentContext:isComplete:completion:)") and
call.getArgument(0).getExpr() = this
)
}
}
/**
* An `Expr` that is used to form a `URL`. Such expressions are very likely to
* be transmitted over a network, because that's what URLs are for.
*/
class Url extends Transmitted {
Url() {
// `string` arg in `URL.init` is a sink
// (we assume here that the URL goes on to be used in a network operation)
exists(CallExpr call |
call.getStaticTarget()
.(MethodDecl)
.hasQualifiedName("URL", ["init(string:)", "init(string:relativeTo:)"]) and
call.getArgument(0).getExpr() = this
)
}
}
/**
* An `Expr` that transmitted through the Alamofire library.
*/
class AlamofireTransmitted extends Transmitted {
AlamofireTransmitted() {
// sinks are the first argument containing the URL, and the `parameters`
// and `headers` arguments to appropriate methods of `Session`.
exists(CallExpr call, string fName |
call.getStaticTarget().(MethodDecl).hasQualifiedName("Session", fName) and
fName.regexpMatch("(request|streamRequest|download)\\(.*") and
(
call.getArgument(0).getExpr() = this or
call.getArgumentWithLabel(["headers", "parameters"]).getExpr() = this
)
)
}
}
/**
* A taint configuration from sensitive information to expressions that are
* transmitted over a network.
*/
class CleartextTransmissionConfig extends TaintTracking::Configuration {
CleartextTransmissionConfig() { this = "CleartextTransmissionConfig" }
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof SensitiveExpr }
override predicate isSink(DataFlow::Node node) { node.asExpr() instanceof Transmitted }
override predicate isSanitizerIn(DataFlow::Node node) {
// make sources barriers so that we only report the closest instance
isSource(node)
}
override predicate isSanitizer(DataFlow::Node node) {
// encryption barrier
node.asExpr() instanceof EncryptedExpr
}
}
from CleartextTransmissionConfig config, DataFlow::PathNode sourceNode, DataFlow::PathNode sinkNode
where config.hasFlowPath(sourceNode, sinkNode)
select sinkNode.getNode(), sourceNode, sinkNode,

View File

@@ -11,78 +11,10 @@
*/
import swift
import codeql.swift.security.SensitiveExprs
import codeql.swift.dataflow.DataFlow
import codeql.swift.dataflow.TaintTracking
import codeql.swift.security.CleartextStoragePreferencesQuery
import DataFlow::PathGraph
/**
* A `DataFlow::Node` of something that gets stored in an application preference store.
*/
abstract class Stored extends DataFlow::Node {
abstract string getStoreName();
}
/** The `DataFlow::Node` of an expression that gets written to the user defaults database */
class UserDefaultsStore extends Stored {
UserDefaultsStore() {
exists(CallExpr call |
call.getStaticTarget().(MethodDecl).hasQualifiedName("UserDefaults", "set(_:forKey:)") and
call.getArgument(0).getExpr() = this.asExpr()
)
}
override string getStoreName() { result = "the user defaults database" }
}
/** The `DataFlow::Node` of an expression that gets written to the iCloud-backed NSUbiquitousKeyValueStore */
class NSUbiquitousKeyValueStore extends Stored {
NSUbiquitousKeyValueStore() {
exists(CallExpr call |
call.getStaticTarget()
.(MethodDecl)
.hasQualifiedName("NSUbiquitousKeyValueStore", "set(_:forKey:)") and
call.getArgument(0).getExpr() = this.asExpr()
)
}
override string getStoreName() { result = "iCloud" }
}
/**
* A more complicated case, this is a macOS-only way of writing to
* NSUserDefaults by modifying the `NSUserDefaultsController.values: Any`
* object via reflection (`perform(Selector)`) or the `NSKeyValueCoding`,
* `NSKeyValueBindingCreation` APIs. (TODO)
*/
class NSUserDefaultsControllerStore extends Stored {
NSUserDefaultsControllerStore() { none() }
override string getStoreName() { result = "the user defaults database" }
}
/**
* A taint configuration from sensitive information to expressions that are
* stored as preferences.
*/
class CleartextStorageConfig extends TaintTracking::Configuration {
CleartextStorageConfig() { this = "CleartextStorageConfig" }
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof SensitiveExpr }
override predicate isSink(DataFlow::Node node) { node instanceof Stored }
override predicate isSanitizerIn(DataFlow::Node node) {
// make sources barriers so that we only report the closest instance
this.isSource(node)
}
override predicate isSanitizer(DataFlow::Node node) {
// encryption barrier
node.asExpr() instanceof EncryptedExpr
}
}
/**
* Gets a prettier node to use in the results.
*/
@@ -97,6 +29,6 @@ from CleartextStorageConfig config, DataFlow::PathNode sourceNode, DataFlow::Pat
where config.hasFlowPath(sourceNode, sinkNode)
select cleanupNode(sinkNode.getNode()), sourceNode, sinkNode,
"This operation stores '" + sinkNode.getNode().toString() + "' in " +
sinkNode.getNode().(Stored).getStoreName() +
sinkNode.getNode().(CleartextStoragePreferencesSink).getStoreName() +
". It may contain unencrypted sensitive data from $@.", sourceNode,
sourceNode.getNode().toString()