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/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) diff --git a/swift/ql/src/queries/Summary/RegexEvals.ql b/swift/ql/src/queries/Summary/RegexEvals.ql new file mode 100644 index 000000000000..5ce27689458a --- /dev/null +++ b/swift/ql/src/queries/Summary/RegexEvals.ql @@ -0,0 +1,20 @@ +/** + * @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, 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 49710791af89..3f72785afbcc 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 @@ -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,13 +28,10 @@ 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 expressions evaluated" and + value = count(RegexEval e | | e.getARegex()).toString() } from string what, string value 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