mirror of
https://github.com/github/codeql.git
synced 2026-04-24 00:05:14 +02:00
Swift: Standardize the sources, sinks etc.
This commit is contained in:
@@ -7,13 +7,14 @@ import swift
|
||||
import codeql.swift.dataflow.DataFlow
|
||||
|
||||
/**
|
||||
* A flow state for encoding types of Swift string encoding.
|
||||
* A type of Swift string encoding. This class is used as a flow state for
|
||||
* the string length conflation taint tracking configuration.
|
||||
*/
|
||||
class StringLengthConflationFlowState extends string {
|
||||
string equivClass;
|
||||
class StringType extends string {
|
||||
string singular;
|
||||
string equivClass;
|
||||
|
||||
StringLengthConflationFlowState() {
|
||||
StringType() {
|
||||
this = "String" and singular = "a String" and equivClass = "String"
|
||||
or
|
||||
this = "NSString" and singular = "an NSString" and equivClass = "NSString"
|
||||
@@ -38,3 +39,159 @@ class StringLengthConflationFlowState extends string {
|
||||
*/
|
||||
string getSingular() { result = singular }
|
||||
}
|
||||
|
||||
/**
|
||||
* A dataflow source for string length conflation vulnerabilities. That is,
|
||||
* a `DataFlow::Node` where a string length is generated.
|
||||
*/
|
||||
abstract class StringLengthConflationSource extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the `StringType` for this source.
|
||||
*/
|
||||
abstract StringType getStringType();
|
||||
}
|
||||
|
||||
/**
|
||||
* A dataflow sink for string length conflation vulnerabilities. That is,
|
||||
* a `DataFlow::Node` where a string length is used.
|
||||
*/
|
||||
abstract class StringLengthConflationSink extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the correct `StringType` for this sink.
|
||||
*/
|
||||
abstract StringType getCorrectStringType();
|
||||
}
|
||||
|
||||
abstract class StringLengthConflationSanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A unit class for adding additional taint steps.
|
||||
*/
|
||||
class StringLengthConflationAdditionalTaintStep extends Unit {
|
||||
/**
|
||||
* Holds if the step from `node1` to `node2` should be considered a taint
|
||||
* step for paths related to string length conflation vulnerabilities.
|
||||
*/
|
||||
abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo);
|
||||
}
|
||||
|
||||
private class DefaultStringLengthConflationSource extends StringLengthConflationSource {
|
||||
StringType stringType;
|
||||
|
||||
DefaultStringLengthConflationSource() {
|
||||
exists(MemberRefExpr memberRef, string className, string varName |
|
||||
memberRef.getBase().getType().(NominalType).getABaseType*().getName() = className and
|
||||
memberRef.getMember().(VarDecl).getName() = varName and
|
||||
this.asExpr() = memberRef and
|
||||
(
|
||||
// result of a call to `String.count`
|
||||
className = "String" and
|
||||
varName = "count" and
|
||||
stringType = "String"
|
||||
or
|
||||
// result of a call to `NSString.length`
|
||||
className = ["NSString", "NSMutableString"] and
|
||||
varName = "length" and
|
||||
stringType = "NSString"
|
||||
or
|
||||
// result of a call to `String.utf8.count`
|
||||
className = "String.UTF8View" and
|
||||
varName = "count" and
|
||||
stringType = "String.utf8"
|
||||
or
|
||||
// result of a call to `String.utf16.count`
|
||||
className = "String.UTF16View" and
|
||||
varName = "count" and
|
||||
stringType = "String.utf16"
|
||||
or
|
||||
// result of a call to `String.unicodeScalars.count`
|
||||
className = "String.UnicodeScalarView" and
|
||||
varName = "count" and
|
||||
stringType = "String.unicodeScalars"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override StringType getStringType() { result = stringType }
|
||||
}
|
||||
|
||||
private class DefaultStringLengthConflationSink extends StringLengthConflationSink {
|
||||
StringType correctStringType;
|
||||
|
||||
DefaultStringLengthConflationSink() {
|
||||
exists(AbstractFunctionDecl funcDecl, CallExpr call, string funcName, int arg |
|
||||
(
|
||||
// arguments to method calls...
|
||||
exists(string className, ClassOrStructDecl c |
|
||||
(
|
||||
// `NSRange.init`
|
||||
className = "NSRange" and
|
||||
funcName = "init(location:length:)" and
|
||||
arg = [0, 1]
|
||||
or
|
||||
// `NSString.character`
|
||||
className = ["NSString", "NSMutableString"] and
|
||||
funcName = "character(at:)" and
|
||||
arg = 0
|
||||
or
|
||||
// `NSString.character`
|
||||
className = ["NSString", "NSMutableString"] and
|
||||
funcName = "substring(from:)" and
|
||||
arg = 0
|
||||
or
|
||||
// `NSString.character`
|
||||
className = ["NSString", "NSMutableString"] and
|
||||
funcName = "substring(to:)" and
|
||||
arg = 0
|
||||
or
|
||||
// `NSMutableString.insert`
|
||||
className = "NSMutableString" and
|
||||
funcName = "insert(_:at:)" and
|
||||
arg = 1
|
||||
) and
|
||||
c.getName() = className and
|
||||
c.getABaseTypeDecl*().(ClassOrStructDecl).getAMember() = funcDecl and
|
||||
call.getStaticTarget() = funcDecl and
|
||||
correctStringType = "NSString"
|
||||
)
|
||||
or
|
||||
// arguments to function calls...
|
||||
// `NSMakeRange`
|
||||
funcName = "NSMakeRange(_:_:)" and
|
||||
arg = [0, 1] and
|
||||
call.getStaticTarget() = funcDecl and
|
||||
correctStringType = "NSString"
|
||||
or
|
||||
// arguments to method calls...
|
||||
(
|
||||
// `String.dropFirst`, `String.dropLast`, `String.removeFirst`, `String.removeLast`
|
||||
funcName = ["dropFirst(_:)", "dropLast(_:)", "removeFirst(_:)", "removeLast(_:)"] and
|
||||
arg = 0
|
||||
or
|
||||
// `String.prefix`, `String.suffix`
|
||||
funcName = ["prefix(_:)", "suffix(_:)"] and
|
||||
arg = 0
|
||||
or
|
||||
// `String.Index.init`
|
||||
funcName = "init(encodedOffset:)" and
|
||||
arg = 0
|
||||
or
|
||||
// `String.index`
|
||||
funcName = ["index(_:offsetBy:)", "index(_:offsetBy:limitBy:)"] and
|
||||
arg = [0, 1]
|
||||
or
|
||||
// `String.formIndex`
|
||||
funcName = ["formIndex(_:offsetBy:)", "formIndex(_:offsetBy:limitBy:)"] and
|
||||
arg = [0, 1]
|
||||
) and
|
||||
call.getStaticTarget() = funcDecl and
|
||||
correctStringType = "String"
|
||||
) and
|
||||
// match up `funcName`, `arg`, `node`.
|
||||
funcDecl.getName() = funcName and
|
||||
call.getArgument(arg).getExpr() = this.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override StringType getCorrectStringType() { result = correctStringType }
|
||||
}
|
||||
|
||||
@@ -17,125 +17,23 @@ class StringLengthConflationConfiguration extends TaintTracking::Configuration {
|
||||
StringLengthConflationConfiguration() { this = "StringLengthConflationConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node, string flowstate) {
|
||||
exists(MemberRefExpr memberRef, string className, string varName |
|
||||
memberRef.getBase().getType().(NominalType).getABaseType*().getName() = className and
|
||||
memberRef.getMember().(VarDecl).getName() = varName and
|
||||
node.asExpr() = memberRef and
|
||||
(
|
||||
// result of a call to `String.count`
|
||||
className = "String" and
|
||||
varName = "count" and
|
||||
flowstate = "String"
|
||||
or
|
||||
// result of a call to `NSString.length`
|
||||
className = ["NSString", "NSMutableString"] and
|
||||
varName = "length" and
|
||||
flowstate = "NSString"
|
||||
or
|
||||
// result of a call to `String.utf8.count`
|
||||
className = "String.UTF8View" and
|
||||
varName = "count" and
|
||||
flowstate = "String.utf8"
|
||||
or
|
||||
// result of a call to `String.utf16.count`
|
||||
className = "String.UTF16View" and
|
||||
varName = "count" and
|
||||
flowstate = "String.utf16"
|
||||
or
|
||||
// result of a call to `String.unicodeScalars.count`
|
||||
className = "String.UnicodeScalarView" and
|
||||
varName = "count" and
|
||||
flowstate = "String.unicodeScalars"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is a sink and `flowstate` is the *correct* flow state for
|
||||
* that sink. We actually want to report incorrect flow states.
|
||||
*/
|
||||
predicate isSinkImpl(DataFlow::Node node, string flowstate) {
|
||||
exists(AbstractFunctionDecl funcDecl, CallExpr call, string funcName, int arg |
|
||||
(
|
||||
// arguments to method calls...
|
||||
exists(string className, ClassOrStructDecl c |
|
||||
(
|
||||
// `NSRange.init`
|
||||
className = "NSRange" and
|
||||
funcName = "init(location:length:)" and
|
||||
arg = [0, 1]
|
||||
or
|
||||
// `NSString.character`
|
||||
className = ["NSString", "NSMutableString"] and
|
||||
funcName = "character(at:)" and
|
||||
arg = 0
|
||||
or
|
||||
// `NSString.character`
|
||||
className = ["NSString", "NSMutableString"] and
|
||||
funcName = "substring(from:)" and
|
||||
arg = 0
|
||||
or
|
||||
// `NSString.character`
|
||||
className = ["NSString", "NSMutableString"] and
|
||||
funcName = "substring(to:)" and
|
||||
arg = 0
|
||||
or
|
||||
// `NSMutableString.insert`
|
||||
className = "NSMutableString" and
|
||||
funcName = "insert(_:at:)" and
|
||||
arg = 1
|
||||
) and
|
||||
c.getName() = className and
|
||||
c.getABaseTypeDecl*().(ClassOrStructDecl).getAMember() = funcDecl and
|
||||
call.getStaticTarget() = funcDecl and
|
||||
flowstate = "NSString"
|
||||
)
|
||||
or
|
||||
// arguments to function calls...
|
||||
// `NSMakeRange`
|
||||
funcName = "NSMakeRange(_:_:)" and
|
||||
arg = [0, 1] and
|
||||
call.getStaticTarget() = funcDecl and
|
||||
flowstate = "NSString"
|
||||
or
|
||||
// arguments to method calls...
|
||||
(
|
||||
// `String.dropFirst`, `String.dropLast`, `String.removeFirst`, `String.removeLast`
|
||||
funcName = ["dropFirst(_:)", "dropLast(_:)", "removeFirst(_:)", "removeLast(_:)"] and
|
||||
arg = 0
|
||||
or
|
||||
// `String.prefix`, `String.suffix`
|
||||
funcName = ["prefix(_:)", "suffix(_:)"] and
|
||||
arg = 0
|
||||
or
|
||||
// `String.Index.init`
|
||||
funcName = "init(encodedOffset:)" and
|
||||
arg = 0
|
||||
or
|
||||
// `String.index`
|
||||
funcName = ["index(_:offsetBy:)", "index(_:offsetBy:limitBy:)"] and
|
||||
arg = [0, 1]
|
||||
or
|
||||
// `String.formIndex`
|
||||
funcName = ["formIndex(_:offsetBy:)", "formIndex(_:offsetBy:limitBy:)"] and
|
||||
arg = [0, 1]
|
||||
) and
|
||||
call.getStaticTarget() = funcDecl and
|
||||
flowstate = "String"
|
||||
) and
|
||||
// match up `funcName`, `arg`, `node`.
|
||||
funcDecl.getName() = funcName and
|
||||
call.getArgument(arg).getExpr() = node.asExpr()
|
||||
)
|
||||
flowstate = node.(StringLengthConflationSource).getStringType()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node node, string flowstate) {
|
||||
// Permit any *incorrect* flowstate, as those are the results the query
|
||||
// should report.
|
||||
exists(string correctFlowState |
|
||||
isSinkImpl(node, correctFlowState) and
|
||||
flowstate.(StringLengthConflationFlowState).getEquivClass() !=
|
||||
correctFlowState.(StringLengthConflationFlowState).getEquivClass()
|
||||
correctFlowState = node.(StringLengthConflationSink).getCorrectStringType() and
|
||||
flowstate.(StringType).getEquivClass() != correctFlowState.(StringType).getEquivClass()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node sanitizer) {
|
||||
sanitizer instanceof StringLengthConflationSanitizer
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
any(StringLengthConflationAdditionalTaintStep s).step(nodeFrom, nodeTo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,12 @@ import DataFlow::PathGraph
|
||||
|
||||
from
|
||||
StringLengthConflationConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink,
|
||||
StringLengthConflationFlowState sourceFlowState, StringLengthConflationFlowState sinkFlowstate,
|
||||
string message
|
||||
StringType sourceType, StringType sinkType, string message
|
||||
where
|
||||
config.hasFlowPath(source, sink) and
|
||||
config.isSource(source.getNode(), sourceFlowState) and
|
||||
config.isSinkImpl(sink.getNode(), sinkFlowstate) and
|
||||
config.isSource(source.getNode(), sourceType) and
|
||||
sinkType = sink.getNode().(StringLengthConflationSink).getCorrectStringType() and
|
||||
message =
|
||||
"This " + sourceFlowState + " length is used in " + sinkFlowstate.getSingular() +
|
||||
"This " + sourceType + " length is used in " + sinkType.getSingular() +
|
||||
", but it may not be equivalent."
|
||||
select sink.getNode(), source, sink, message
|
||||
|
||||
Reference in New Issue
Block a user