From 9f1332bdb209949121c230b1cf1dab0f9aaed86f Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 27 Sep 2023 18:17:40 +0100 Subject: [PATCH 1/6] Swift: Add a summary query for query sinks. --- swift/ql/src/queries/Summary/QuerySinks.ql | 84 ++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 swift/ql/src/queries/Summary/QuerySinks.ql diff --git a/swift/ql/src/queries/Summary/QuerySinks.ql b/swift/ql/src/queries/Summary/QuerySinks.ql new file mode 100644 index 000000000000..a86b64f26b63 --- /dev/null +++ b/swift/ql/src/queries/Summary/QuerySinks.ql @@ -0,0 +1,84 @@ +/** + * @name Query Sinks + * @description List all query sinks found in the database. Query sinks are + * potential results depending on what data flows to them and + * other context. + * @kind problem + * @problem.severity info + * @id swift/summary/query-sinks + * @tags summary + */ + +/* + * Most queries compute data flow to one of the following sinks: + * - custom per-query sinks (listed by this query, `swift/summary/query-sinks`). + * - regular expression evaluation (see `swift/summary/regex-evals`). + */ + +import swift +import codeql.swift.dataflow.DataFlow +import codeql.swift.security.PathInjectionQuery +import codeql.swift.security.UnsafeWebViewFetchQuery +import codeql.swift.security.SqlInjectionQuery +import codeql.swift.security.UnsafeJsEvalQuery +import codeql.swift.security.UncontrolledFormatStringQuery +import codeql.swift.security.StringLengthConflationQuery +import codeql.swift.security.ConstantPasswordQuery +import codeql.swift.security.CleartextStorageDatabaseQuery +import codeql.swift.security.CleartextTransmissionQuery +import codeql.swift.security.CleartextLoggingQuery +import codeql.swift.security.CleartextStoragePreferencesQuery +import codeql.swift.security.HardcodedEncryptionKeyQuery +import codeql.swift.security.ECBEncryptionQuery +import codeql.swift.security.WeakSensitiveDataHashingQuery +import codeql.swift.security.XXEQuery +import codeql.swift.security.InsecureTLSQuery +import codeql.swift.security.ConstantSaltQuery +import codeql.swift.security.InsufficientHashIterationsQuery +import codeql.swift.security.PredicateInjectionQuery +import codeql.swift.security.StaticInitializationVectorQuery + +string queryForSink(DataFlow::Node sink) { + PathInjectionConfig::isSink(sink) and result = "swift/path-injection" + or + UnsafeWebViewFetchConfig::isSink(sink) and result = "swift/unsafe-webview-fetch" + or + SqlInjectionConfig::isSink(sink) and result = "swift/sql-injection" + or + UnsafeJsEvalConfig::isSink(sink) and result = "swift/unsafe-js-eval" + or + TaintedFormatConfig::isSink(sink) and result = "swift/uncontrolled-format-string" + or + StringLengthConflationConfig::isSink(sink) and result = "swift/string-length-conflation" + or + ConstantPasswordConfig::isSink(sink) and result = "swift/constant-password" + or + CleartextStorageDatabaseConfig::isSink(sink) and result = "swift/cleartext-storage-database" + or + CleartextTransmissionConfig::isSink(sink) and result = "swift/cleartext-transmission" + or + CleartextLoggingConfig::isSink(sink) and result = "swift/cleartext-logging" + or + CleartextStoragePreferencesConfig::isSink(sink) and result = "swift/cleartext-storage-preferences" + or + HardcodedKeyConfig::isSink(sink) and result = "swift/hardcoded-key" + or + EcbEncryptionConfig::isSink(sink) and result = "swift/ecb-encryption" + or + WeakHashingConfig::isSink(sink) and result = "swift/weak-sensitive-data-hashing" + or + XxeConfig::isSink(sink) and result = "swift/xxe" + or + InsecureTlsConfig::isSink(sink) and result = "swift/insecure-tls" + or + ConstantSaltConfig::isSink(sink) and result = "swift/constant-salt" + or + InsufficientHashIterationsConfig::isSink(sink) and result = "swift/insufficient-hash-iterations" + or + PredicateInjectionConfig::isSink(sink) and result = "swift/predicate-injection" + or + StaticInitializationVectorConfig::isSink(sink) and result = "swift/static-initialization-vector" +} + +from DataFlow::Node n +select n, "Sink for " + queryForSink(n) From ee83ad730b7e0faa6b79c442b4175d369544672e Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 27 Sep 2023 18:13:23 +0100 Subject: [PATCH 2/6] Swift: Add a summary query for regex evals. --- swift/ql/src/queries/Summary/RegexEvals.ql | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 swift/ql/src/queries/Summary/RegexEvals.ql diff --git a/swift/ql/src/queries/Summary/RegexEvals.ql b/swift/ql/src/queries/Summary/RegexEvals.ql new file mode 100644 index 000000000000..30c68e3b13ae --- /dev/null +++ b/swift/ql/src/queries/Summary/RegexEvals.ql @@ -0,0 +1,15 @@ +/** + * @name Regular Expression Evaluations + * @description List all regular expression evaluations found in the database. + * @kind problem + * @problem.severity info + * @id swift/summary/regex-evals + * @tags summary + */ + +import swift +import codeql.swift.regex.Regex + +from RegexEval e +select e, + "Regular expression evaluation with " + count(e.getARegex()).toString() + " associated regex(s)" From ec573bdda85d99871cc408db306b91b3b7c61cb4 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 27 Sep 2023 18:22:48 +0100 Subject: [PATCH 3/6] Swift: Clean up and improve consistency. --- swift/ql/src/queries/Summary/FlowSources.ql | 12 ++++++++++-- swift/ql/src/queries/Summary/SummaryStats.ql | 5 ++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/swift/ql/src/queries/Summary/FlowSources.ql b/swift/ql/src/queries/Summary/FlowSources.ql index 04c08aa468e3..f0c610080bdd 100644 --- a/swift/ql/src/queries/Summary/FlowSources.ql +++ b/swift/ql/src/queries/Summary/FlowSources.ql @@ -9,13 +9,21 @@ * @tags summary */ +/* + * Most queries compute data flow from one of the following sources: + * - flow sources (listed by this query, `swift/summary/flow-sources`). + * - sensitive expressions (see `swift/summary/sensitive-expressions`). + * - constant values. + * - custom per-query sources. + */ + import swift import codeql.swift.dataflow.FlowSources string sourceClass(FlowSource s) { - s instanceof LocalFlowSource and result = "LocalFlowSource" + s instanceof LocalFlowSource and result = "Local flow source" or - s instanceof RemoteFlowSource and result = "RemoteFlowSource" + s instanceof RemoteFlowSource and result = "Remote flow source" } from FlowSource s diff --git a/swift/ql/src/queries/Summary/SummaryStats.ql b/swift/ql/src/queries/Summary/SummaryStats.ql index 49710791af89..d652cc37996c 100644 --- a/swift/ql/src/queries/Summary/SummaryStats.ql +++ b/swift/ql/src/queries/Summary/SummaryStats.ql @@ -1,5 +1,5 @@ /** - * @name Summary statistics + * @name Summary Statistics * @description A table of summary statistics about a database. * @kind table * @id swift/summary/summary-statistics @@ -59,6 +59,9 @@ predicate statistic(string what, string value) { what = "Taint reach (per million nodes)" and value = taintReach().toString() or what = "Regular expression evals" and value = count(RegexEval e).toString() + or + what = "Regular expression evals with associated regex" and + value = count(RegexEval e | exists(e.getARegex())).toString() } from string what, string value From 87696e58ab2b9bf1104962faa7ab607a0f86413f Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 27 Sep 2023 18:28:07 +0100 Subject: [PATCH 4/6] Swift: Break the 'taint reach' metric off into its own query (it's expensive to compute). --- swift/ql/src/queries/Summary/SummaryStats.ql | 30 ------------ swift/ql/src/queries/Summary/TaintReach.ql | 48 ++++++++++++++++++++ 2 files changed, 48 insertions(+), 30 deletions(-) create mode 100644 swift/ql/src/queries/Summary/TaintReach.ql diff --git a/swift/ql/src/queries/Summary/SummaryStats.ql b/swift/ql/src/queries/Summary/SummaryStats.ql index d652cc37996c..aab8cf6610de 100644 --- a/swift/ql/src/queries/Summary/SummaryStats.ql +++ b/swift/ql/src/queries/Summary/SummaryStats.ql @@ -9,32 +9,8 @@ import swift import codeql.swift.dataflow.FlowSources import codeql.swift.security.SensitiveExprs -import codeql.swift.dataflow.DataFlow -import codeql.swift.dataflow.TaintTracking import codeql.swift.regex.Regex -/** - * A taint configuration for tainted data reaching any node. - */ -module TaintReachConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node node) { node instanceof FlowSource } - - predicate isSink(DataFlow::Node node) { any() } -} - -module TaintReachFlow = TaintTracking::Global; - -/** - * Gets the total number of dataflow nodes that taint reaches (from any source). - */ -int taintedNodesCount() { result = count(DataFlow::Node n | TaintReachFlow::flowTo(n)) } - -/** - * Gets the proportion of dataflow nodes that taint reaches (from any source), - * expressed as a count per million nodes. - */ -float taintReach() { result = (taintedNodesCount() * 1000000.0) / count(DataFlow::Node n) } - predicate statistic(string what, string value) { what = "Files" and value = count(File f).toString() or @@ -52,12 +28,6 @@ predicate statistic(string what, string value) { or what = "Sensitive expressions" and value = count(SensitiveExpr e).toString() or - what = "Dataflow nodes (total)" and value = count(DataFlow::Node n).toString() - or - what = "Dataflow nodes (tainted)" and value = taintedNodesCount().toString() - or - what = "Taint reach (per million nodes)" and value = taintReach().toString() - or what = "Regular expression evals" and value = count(RegexEval e).toString() or what = "Regular expression evals with associated regex" and diff --git a/swift/ql/src/queries/Summary/TaintReach.ql b/swift/ql/src/queries/Summary/TaintReach.ql new file mode 100644 index 000000000000..b6fc1e57fb90 --- /dev/null +++ b/swift/ql/src/queries/Summary/TaintReach.ql @@ -0,0 +1,48 @@ +/** + * @name Taint Reach + * @description Calculates 'taint reach', a measure of how much of a database + * is reached from flow sources, via taint flow. This can be + * expensive to compute on large databases. + * @kind table + * @id swift/summary/taint-reach + * @tags summary + */ + +import swift +import codeql.swift.dataflow.FlowSources +import codeql.swift.dataflow.DataFlow +import codeql.swift.dataflow.TaintTracking + +/** + * A taint configuration for tainted data reaching any node. + */ +module TaintReachConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { node instanceof FlowSource } + + predicate isSink(DataFlow::Node node) { any() } +} + +module TaintReachFlow = TaintTracking::Global; + +/** + * Gets the total number of dataflow nodes that taint reaches (from any source). + */ +int taintedNodesCount() { result = count(DataFlow::Node n | TaintReachFlow::flowTo(n)) } + +/** + * Gets the proportion of dataflow nodes that taint reaches (from any source), + * expressed as a count per million nodes. + */ +float taintReach() { result = (taintedNodesCount() * 1000000.0) / count(DataFlow::Node n) } + +predicate statistic(string what, string value) { + what = "Dataflow nodes (total)" and value = count(DataFlow::Node n).toString() + or + what = "Dataflow nodes (tainted)" and value = taintedNodesCount().toString() + or + what = "Taint reach (per million nodes)" and value = taintReach().toString() +} + +from string what, string value +where statistic(what, value) +select what, value From e4e4c5be61a582d528cfc530ed7a2ecb36192ca7 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 27 Sep 2023 19:12:04 +0100 Subject: [PATCH 5/6] Swift: Make QL-for-QL happy. --- swift/ql/src/queries/Summary/RegexEvals.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/ql/src/queries/Summary/RegexEvals.ql b/swift/ql/src/queries/Summary/RegexEvals.ql index 30c68e3b13ae..47c974ebefb3 100644 --- a/swift/ql/src/queries/Summary/RegexEvals.ql +++ b/swift/ql/src/queries/Summary/RegexEvals.ql @@ -12,4 +12,4 @@ import codeql.swift.regex.Regex from RegexEval e select e, - "Regular expression evaluation with " + count(e.getARegex()).toString() + " associated regex(s)" + "Regular expression evaluation with " + count(e.getARegex()).toString() + " associated regex(s)." From 8e9ca7bc26f6df3f7086bb7fab8517503ccad0e0 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 28 Sep 2023 08:31:45 +0100 Subject: [PATCH 6/6] Swift: Improve RegexEvals.ql and summary regex reporting. --- swift/ql/src/queries/Summary/RegexEvals.ql | 11 ++++++++--- swift/ql/src/queries/Summary/SummaryStats.ql | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/swift/ql/src/queries/Summary/RegexEvals.ql b/swift/ql/src/queries/Summary/RegexEvals.ql index 47c974ebefb3..5ce27689458a 100644 --- a/swift/ql/src/queries/Summary/RegexEvals.ql +++ b/swift/ql/src/queries/Summary/RegexEvals.ql @@ -10,6 +10,11 @@ import swift import codeql.swift.regex.Regex -from RegexEval e -select e, - "Regular expression evaluation with " + count(e.getARegex()).toString() + " associated regex(s)." +from RegexEval e, string message, Expr regex +where + message = "Regular expression evaluation with source $@." and regex = e.getARegex() + or + message = "Regular expression evaluation with no identified source." and + not exists(e.getARegex()) and + regex = e +select e, message, regex, regex.toString() diff --git a/swift/ql/src/queries/Summary/SummaryStats.ql b/swift/ql/src/queries/Summary/SummaryStats.ql index aab8cf6610de..3f72785afbcc 100644 --- a/swift/ql/src/queries/Summary/SummaryStats.ql +++ b/swift/ql/src/queries/Summary/SummaryStats.ql @@ -30,8 +30,8 @@ predicate statistic(string what, string value) { or what = "Regular expression evals" and value = count(RegexEval e).toString() or - what = "Regular expression evals with associated regex" and - value = count(RegexEval e | exists(e.getARegex())).toString() + what = "Regular expressions evaluated" and + value = count(RegexEval e | | e.getARegex()).toString() } from string what, string value