Skip to content

Commit

Permalink
Merge pull request #14329 from geoffw0/sinks
Browse files Browse the repository at this point in the history
Swift: Update summary queries
  • Loading branch information
geoffw0 authored Oct 3, 2023
2 parents 34b33e1 + 8e9ca7b commit d258f69
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 33 deletions.
12 changes: 10 additions & 2 deletions swift/ql/src/queries/Summary/FlowSources.ql
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
84 changes: 84 additions & 0 deletions swift/ql/src/queries/Summary/QuerySinks.ql
Original file line number Diff line number Diff line change
@@ -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)
20 changes: 20 additions & 0 deletions swift/ql/src/queries/Summary/RegexEvals.ql
Original file line number Diff line number Diff line change
@@ -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()
35 changes: 4 additions & 31 deletions swift/ql/src/queries/Summary/SummaryStats.ql
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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<TaintReachConfig>;

/**
* 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
Expand All @@ -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
Expand Down
48 changes: 48 additions & 0 deletions swift/ql/src/queries/Summary/TaintReach.ql
Original file line number Diff line number Diff line change
@@ -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<TaintReachConfig>;

/**
* 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

0 comments on commit d258f69

Please sign in to comment.