diff --git a/swift/ql/lib/change-notes/2023-10-16-substring.md b/swift/ql/lib/change-notes/2023-10-16-substring.md new file mode 100644 index 00000000000..be494a12184 --- /dev/null +++ b/swift/ql/lib/change-notes/2023-10-16-substring.md @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- + +* Added taint flow models for members of `Substring`. diff --git a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Collection.qll b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Collection.qll index b10185df41c..a8cf7b1dcd1 100644 --- a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Collection.qll +++ b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Collection.qll @@ -36,6 +36,8 @@ private class CollectionSummaries extends SummaryModelCsv { ";RangeReplaceableCollection;true;removeFirst();;;Argument[-1];ReturnValue;taint", ";RangeReplaceableCollection;true;removeLast();;;Argument[-1];ReturnValue;taint", ";RangeReplaceableCollection;true;insert(_:at:);;;Argument[0];Argument[-1];taint", + ";RangeReplaceableCollection;true;replaceSubrange(_:with:);;;Argument[1];Argument[-1];taint", + ";RangeReplaceableCollection;true;replaceSubrange(_:with:);;;Argument[1].CollectionElement;Argument[-1].CollectionElement;value", ";BidirectionalCollection;true;joined(separator:);;;Argument[-1..0];ReturnValue;taint", ";BidirectionalCollection;true;last(where:);;;Argument[-1];ReturnValue;taint", ";BidirectionalCollection;true;popLast();;;Argument[-1];ReturnValue;taint", diff --git a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/String.qll b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/String.qll index 93a46c32e68..f75488ab798 100644 --- a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/String.qll +++ b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/String.qll @@ -113,7 +113,6 @@ private class StringSummaries extends SummaryModelCsv { ";String;true;localizedStringWithFormat(_:_:);;;Argument[0];ReturnValue;taint", ";String;true;localizedStringWithFormat(_:_:);;;Argument[1].CollectionElement;ReturnValue;taint", ";String;true;insert(contentsOf:at:);;;Argument[0];Argument[-1];taint", - ";String;true;replaceSubrange(_:with:);;;Argument[1];Argument[-1];taint", ";String;true;max();;;Argument[-1];ReturnValue;taint", ";String;true;max(by:);;;Argument[-1];ReturnValue;taint", ";String;true;min();;;Argument[-1];ReturnValue;taint", @@ -127,6 +126,9 @@ private class StringSummaries extends SummaryModelCsv { ";String;true;decodeCString(_:as:repairingInvalidCodeUnits:);;;Argument[0];ReturnValue.TupleElement[0];taint", ";String;true;decodeCString(_:as:repairingInvalidCodeUnits:);;;Argument[0].CollectionElement;ReturnValue.TupleElement[0];taint", ";LosslessStringConvertible;true;init(_:);;;Argument[0];ReturnValue;taint", + ";Substring;true;withUTF8(_:);;;Argument[-1];Argument[0].Parameter[0].CollectionElement;taint", + ";Substring;true;withUTF8(_:);;;Argument[0].Parameter[0].CollectionElement;Argument[-1];taint", + ";Substring;true;withUTF8(_:);;;Argument[0].ReturnValue;ReturnValue;value", ] } } @@ -139,23 +141,26 @@ private class StringFieldsInheritTaint extends TaintInheritingContent, DataFlow::Content::FieldContent { StringFieldsInheritTaint() { - this.getField() - .hasQualifiedName(["String", "StringProtocol"], + exists(FieldDecl fieldDecl, Decl declaringDecl, TypeDecl namedTypeDecl | + ( + namedTypeDecl.getFullName() = ["String", "StringProtocol"] and + fieldDecl.getName() = [ "unicodeScalars", "utf8", "utf16", "lazy", "utf8CString", "dataValue", "identifierValue", "capitalized", "localizedCapitalized", "localizedLowercase", "localizedUppercase", "decomposedStringWithCanonicalMapping", "decomposedStringWithCompatibilityMapping", "precomposedStringWithCanonicalMapping", "precomposedStringWithCompatibilityMapping", "removingPercentEncoding" - ]) - or - exists(FieldDecl fieldDecl, Decl declaringDecl, TypeDecl namedTypeDecl | - ( + ] + or namedTypeDecl.getFullName() = "CustomStringConvertible" and fieldDecl.getName() = "description" or namedTypeDecl.getFullName() = "CustomDebugStringConvertible" and fieldDecl.getName() = "debugDescription" + or + namedTypeDecl.getFullName() = "Substring" and + fieldDecl.getName() = "base" ) and declaringDecl.getAMember() = fieldDecl and declaringDecl.asNominalTypeDecl() = namedTypeDecl.getADerivedTypeDecl*() and diff --git a/swift/ql/test/library-tests/dataflow/taint/libraries/string.swift b/swift/ql/test/library-tests/dataflow/taint/libraries/string.swift index 294f57ecf04..fd1da7d8eb6 100644 --- a/swift/ql/test/library-tests/dataflow/taint/libraries/string.swift +++ b/swift/ql/test/library-tests/dataflow/taint/libraries/string.swift @@ -687,3 +687,32 @@ func testDecodeCString() { sink(arg: str4) // $ tainted=669 sink(arg: repaired4) } + +func testSubstringMembers() { + let clean = "" + let tainted = source2() + + let sub1 = tainted[..