Merge pull request #15230 from geoffw0/swiftui

Swift: Add dataflow tests for property wrappers and SwiftUI
This commit is contained in:
Geoffrey White 2024-01-08 17:41:43 +00:00 коммит произвёл GitHub
Родитель c84e85d35d 4016033f88
Коммит 2f6f376d2d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 361 добавлений и 0 удалений

Просмотреть файл

@ -1,5 +1,8 @@
edges
| file://:0:0:0:0 | .wrappedValue | test.swift:949:15:949:15 | x |
| file://:0:0:0:0 | .wrappedValue | test.swift:951:15:951:15 | x |
| file://:0:0:0:0 | KeyPathComponent [some:0] | test.swift:663:13:663:29 | exit #keyPath(...) [some:0] |
| file://:0:0:0:0 | [post] self [wrappedValue] | file://:0:0:0:0 | self [wrappedValue] |
| file://:0:0:0:0 | self [a, x] | file://:0:0:0:0 | .a [x] |
| file://:0:0:0:0 | self [s, x] | file://:0:0:0:0 | .s [x] |
| file://:0:0:0:0 | self [str] | file://:0:0:0:0 | .str |
@ -7,6 +10,7 @@ edges
| file://:0:0:0:0 | self [v2] | file://:0:0:0:0 | .v2 |
| file://:0:0:0:0 | self [v3] | file://:0:0:0:0 | .v3 |
| file://:0:0:0:0 | self [v] | file://:0:0:0:0 | .v |
| file://:0:0:0:0 | self [wrappedValue] | test.swift:958:9:958:9 | self [wrappedValue] |
| file://:0:0:0:0 | self [x, some:0] | file://:0:0:0:0 | .x [some:0] |
| file://:0:0:0:0 | self [x] | file://:0:0:0:0 | .x |
| file://:0:0:0:0 | self [x] | file://:0:0:0:0 | .x |
@ -14,8 +18,12 @@ edges
| file://:0:0:0:0 | value | file://:0:0:0:0 | [post] self [v2] |
| file://:0:0:0:0 | value | file://:0:0:0:0 | [post] self [v3] |
| file://:0:0:0:0 | value | file://:0:0:0:0 | [post] self [v] |
| file://:0:0:0:0 | value | file://:0:0:0:0 | [post] self [wrappedValue] |
| file://:0:0:0:0 | value | file://:0:0:0:0 | [post] self [x] |
| file://:0:0:0:0 | value | file://:0:0:0:0 | [post] self [x] |
| file://:0:0:0:0 | value | test.swift:938:9:938:9 | newValue |
| file://:0:0:0:0 | value | test.swift:957:9:957:9 | value |
| file://:0:0:0:0 | value | test.swift:965:9:965:9 | newValue |
| file://:0:0:0:0 | value [some:0] | file://:0:0:0:0 | [post] self [v2, some:0] |
| file://:0:0:0:0 | value [some:0] | file://:0:0:0:0 | [post] self [x, some:0] |
| test.swift:6:19:6:26 | call to source() | test.swift:7:15:7:15 | t1 |
@ -606,6 +614,24 @@ edges
| test.swift:908:19:908:26 | call to source() | test.swift:904:13:904:18 | call to ... |
| test.swift:927:12:927:31 | call to source(_:) | test.swift:927:12:927:31 | OpenExistentialExpr |
| test.swift:929:12:929:57 | call to source(_:) | test.swift:929:12:929:57 | OpenExistentialExpr |
| test.swift:937:22:937:29 | call to source() | file://:0:0:0:0 | .wrappedValue |
| test.swift:938:9:938:9 | newValue | test.swift:938:25:938:25 | newValue |
| test.swift:941:10:941:24 | wrappedValue | test.swift:942:19:942:19 | wrappedValue |
| test.swift:943:29:943:36 | call to source() | test.swift:938:9:938:9 | newValue |
| test.swift:948:33:948:33 | value | file://:0:0:0:0 | value |
| test.swift:948:42:948:49 | call to source() | test.swift:941:10:941:24 | wrappedValue |
| test.swift:950:9:950:16 | call to source() | test.swift:948:33:948:33 | value |
| test.swift:957:9:957:9 | value | file://:0:0:0:0 | value |
| test.swift:958:9:958:9 | self [wrappedValue] | test.swift:959:23:959:23 | self [wrappedValue] |
| test.swift:959:23:959:23 | self [wrappedValue] | test.swift:959:23:959:23 | .wrappedValue |
| test.swift:965:9:965:9 | newValue | test.swift:967:28:967:28 | newValue |
| test.swift:967:28:967:28 | newValue | test.swift:957:9:957:9 | value |
| test.swift:971:10:971:24 | wrappedValue | test.swift:972:19:972:19 | wrappedValue |
| test.swift:978:34:978:34 | value | file://:0:0:0:0 | value |
| test.swift:980:9:980:16 | call to source() | test.swift:978:34:978:34 | value |
| test.swift:983:38:983:45 | call to source() | test.swift:971:10:971:24 | wrappedValue |
| test.swift:988:34:988:34 | value | file://:0:0:0:0 | value |
| test.swift:991:10:991:17 | call to source() | test.swift:988:34:988:34 | value |
nodes
| file://:0:0:0:0 | .a [x] | semmle.label | .a [x] |
| file://:0:0:0:0 | .s [x] | semmle.label | .s [x] |
@ -614,6 +640,7 @@ nodes
| file://:0:0:0:0 | .v2 | semmle.label | .v2 |
| file://:0:0:0:0 | .v2 [some:0] | semmle.label | .v2 [some:0] |
| file://:0:0:0:0 | .v3 | semmle.label | .v3 |
| file://:0:0:0:0 | .wrappedValue | semmle.label | .wrappedValue |
| file://:0:0:0:0 | .x | semmle.label | .x |
| file://:0:0:0:0 | .x | semmle.label | .x |
| file://:0:0:0:0 | .x | semmle.label | .x |
@ -623,6 +650,7 @@ nodes
| file://:0:0:0:0 | [post] self [v2] | semmle.label | [post] self [v2] |
| file://:0:0:0:0 | [post] self [v3] | semmle.label | [post] self [v3] |
| file://:0:0:0:0 | [post] self [v] | semmle.label | [post] self [v] |
| file://:0:0:0:0 | [post] self [wrappedValue] | semmle.label | [post] self [wrappedValue] |
| file://:0:0:0:0 | [post] self [x, some:0] | semmle.label | [post] self [x, some:0] |
| file://:0:0:0:0 | [post] self [x] | semmle.label | [post] self [x] |
| file://:0:0:0:0 | [post] self [x] | semmle.label | [post] self [x] |
@ -633,6 +661,7 @@ nodes
| file://:0:0:0:0 | self [v2] | semmle.label | self [v2] |
| file://:0:0:0:0 | self [v3] | semmle.label | self [v3] |
| file://:0:0:0:0 | self [v] | semmle.label | self [v] |
| file://:0:0:0:0 | self [wrappedValue] | semmle.label | self [wrappedValue] |
| file://:0:0:0:0 | self [x, some:0] | semmle.label | self [x, some:0] |
| file://:0:0:0:0 | self [x] | semmle.label | self [x] |
| file://:0:0:0:0 | self [x] | semmle.label | self [x] |
@ -642,6 +671,10 @@ nodes
| file://:0:0:0:0 | value | semmle.label | value |
| file://:0:0:0:0 | value | semmle.label | value |
| file://:0:0:0:0 | value | semmle.label | value |
| file://:0:0:0:0 | value | semmle.label | value |
| file://:0:0:0:0 | value | semmle.label | value |
| file://:0:0:0:0 | value | semmle.label | value |
| file://:0:0:0:0 | value | semmle.label | value |
| file://:0:0:0:0 | value [some:0] | semmle.label | value [some:0] |
| file://:0:0:0:0 | value [some:0] | semmle.label | value [some:0] |
| test.swift:6:19:6:26 | call to source() | semmle.label | call to source() |
@ -1262,6 +1295,30 @@ nodes
| test.swift:929:12:929:57 | OpenExistentialExpr | semmle.label | OpenExistentialExpr |
| test.swift:929:12:929:57 | call to source(_:) | semmle.label | call to source(_:) |
| test.swift:930:12:930:65 | call to source(_:) | semmle.label | call to source(_:) |
| test.swift:937:22:937:29 | call to source() | semmle.label | call to source() |
| test.swift:938:9:938:9 | newValue | semmle.label | newValue |
| test.swift:938:25:938:25 | newValue | semmle.label | newValue |
| test.swift:941:10:941:24 | wrappedValue | semmle.label | wrappedValue |
| test.swift:942:19:942:19 | wrappedValue | semmle.label | wrappedValue |
| test.swift:943:29:943:36 | call to source() | semmle.label | call to source() |
| test.swift:948:33:948:33 | value | semmle.label | value |
| test.swift:948:42:948:49 | call to source() | semmle.label | call to source() |
| test.swift:949:15:949:15 | x | semmle.label | x |
| test.swift:950:9:950:16 | call to source() | semmle.label | call to source() |
| test.swift:951:15:951:15 | x | semmle.label | x |
| test.swift:957:9:957:9 | value | semmle.label | value |
| test.swift:958:9:958:9 | self [wrappedValue] | semmle.label | self [wrappedValue] |
| test.swift:959:23:959:23 | .wrappedValue | semmle.label | .wrappedValue |
| test.swift:959:23:959:23 | self [wrappedValue] | semmle.label | self [wrappedValue] |
| test.swift:965:9:965:9 | newValue | semmle.label | newValue |
| test.swift:967:28:967:28 | newValue | semmle.label | newValue |
| test.swift:971:10:971:24 | wrappedValue | semmle.label | wrappedValue |
| test.swift:972:19:972:19 | wrappedValue | semmle.label | wrappedValue |
| test.swift:978:34:978:34 | value | semmle.label | value |
| test.swift:980:9:980:16 | call to source() | semmle.label | call to source() |
| test.swift:983:38:983:45 | call to source() | semmle.label | call to source() |
| test.swift:988:34:988:34 | value | semmle.label | value |
| test.swift:991:10:991:17 | call to source() | semmle.label | call to source() |
subpaths
| test.swift:75:22:75:22 | x | test.swift:65:16:65:28 | arg1 | test.swift:65:1:70:1 | arg2[return] | test.swift:75:32:75:32 | [post] y |
| test.swift:114:19:114:19 | arg | test.swift:109:9:109:14 | arg | test.swift:110:12:110:12 | arg | test.swift:114:12:114:22 | call to ... |
@ -1463,3 +1520,11 @@ subpaths
| test.swift:928:12:928:31 | call to source(_:) | test.swift:928:12:928:31 | call to source(_:) | test.swift:928:12:928:31 | call to source(_:) | result |
| test.swift:929:12:929:57 | OpenExistentialExpr | test.swift:929:12:929:57 | call to source(_:) | test.swift:929:12:929:57 | OpenExistentialExpr | result |
| test.swift:930:12:930:65 | call to source(_:) | test.swift:930:12:930:65 | call to source(_:) | test.swift:930:12:930:65 | call to source(_:) | result |
| test.swift:938:25:938:25 | newValue | test.swift:943:29:943:36 | call to source() | test.swift:938:25:938:25 | newValue | result |
| test.swift:938:25:938:25 | newValue | test.swift:950:9:950:16 | call to source() | test.swift:938:25:938:25 | newValue | result |
| test.swift:942:19:942:19 | wrappedValue | test.swift:948:42:948:49 | call to source() | test.swift:942:19:942:19 | wrappedValue | result |
| test.swift:949:15:949:15 | x | test.swift:937:22:937:29 | call to source() | test.swift:949:15:949:15 | x | result |
| test.swift:951:15:951:15 | x | test.swift:937:22:937:29 | call to source() | test.swift:951:15:951:15 | x | result |
| test.swift:959:23:959:23 | .wrappedValue | test.swift:980:9:980:16 | call to source() | test.swift:959:23:959:23 | .wrappedValue | result |
| test.swift:959:23:959:23 | .wrappedValue | test.swift:991:10:991:17 | call to source() | test.swift:959:23:959:23 | .wrappedValue | result |
| test.swift:972:19:972:19 | wrappedValue | test.swift:983:38:983:45 | call to source() | test.swift:972:19:972:19 | wrappedValue | result |

Просмотреть файл

@ -1165,3 +1165,57 @@
| test.swift:926:45:926:48 | y | test.swift:926:45:926:48 | SSA def(y) |
| test.swift:927:12:927:31 | call to source(_:) | test.swift:927:12:927:31 | OpenExistentialExpr |
| test.swift:929:12:929:57 | call to source(_:) | test.swift:929:12:929:57 | OpenExistentialExpr |
| test.swift:936:9:936:9 | self | test.swift:936:9:936:9 | SSA def(self) |
| test.swift:937:9:937:9 | SSA def(self) | test.swift:937:9:937:31 | self[return] |
| test.swift:937:9:937:9 | self | test.swift:937:9:937:9 | SSA def(self) |
| test.swift:938:9:938:9 | SSA def(newValue) | test.swift:938:25:938:25 | newValue |
| test.swift:938:9:938:9 | SSA def(self) | test.swift:938:9:938:35 | self[return] |
| test.swift:938:9:938:9 | newValue | test.swift:938:9:938:9 | SSA def(newValue) |
| test.swift:938:9:938:9 | self | test.swift:938:9:938:9 | SSA def(self) |
| test.swift:941:5:941:5 | SSA def(self) | test.swift:943:9:943:9 | self |
| test.swift:941:5:941:5 | self | test.swift:941:5:941:5 | SSA def(self) |
| test.swift:941:10:941:24 | SSA def(wrappedValue) | test.swift:942:19:942:19 | wrappedValue |
| test.swift:941:10:941:24 | wrappedValue | test.swift:941:10:941:24 | SSA def(wrappedValue) |
| test.swift:943:9:943:9 | [post] self | test.swift:941:5:944:5 | self[return] |
| test.swift:943:9:943:9 | self | test.swift:941:5:944:5 | self[return] |
| test.swift:948:6:948:49 | call to MyTaintPropertyWrapper.init(wrappedValue:) | test.swift:948:33:948:36 | ... as ... |
| test.swift:948:33:948:33 | value | test.swift:948:33:948:33 | SSA def(value) |
| test.swift:948:33:948:36 | ... as ... | test.swift:948:33:948:33 | x |
| test.swift:957:9:957:9 | self | test.swift:957:9:957:9 | SSA def(self) |
| test.swift:957:9:957:9 | self | test.swift:957:9:957:9 | SSA def(self) |
| test.swift:957:9:957:9 | self | test.swift:957:9:957:9 | SSA def(self) |
| test.swift:957:9:957:9 | value | test.swift:957:9:957:9 | SSA def(value) |
| test.swift:958:9:958:9 | SSA def(self) | test.swift:959:23:959:23 | self |
| test.swift:958:9:958:9 | self | test.swift:958:9:958:9 | SSA def(self) |
| test.swift:959:23:959:23 | [post] self | test.swift:958:9:960:9 | self[return] |
| test.swift:959:23:959:23 | self | test.swift:958:9:960:9 | self[return] |
| test.swift:963:9:963:9 | self | test.swift:963:9:963:9 | SSA def(self) |
| test.swift:964:9:964:9 | SSA def(self) | test.swift:964:15:964:15 | self |
| test.swift:964:9:964:9 | self | test.swift:964:9:964:9 | SSA def(self) |
| test.swift:964:15:964:15 | [post] self | test.swift:964:9:964:28 | self[return] |
| test.swift:964:15:964:15 | self | test.swift:964:9:964:28 | self[return] |
| test.swift:965:9:965:9 | SSA def(newValue) | test.swift:967:28:967:28 | newValue |
| test.swift:965:9:965:9 | SSA def(self) | test.swift:966:23:966:23 | self |
| test.swift:965:9:965:9 | newValue | test.swift:965:9:965:9 | SSA def(newValue) |
| test.swift:965:9:965:9 | self | test.swift:965:9:965:9 | SSA def(self) |
| test.swift:966:23:966:23 | [post] self | test.swift:967:13:967:13 | self |
| test.swift:966:23:966:23 | self | test.swift:967:13:967:13 | self |
| test.swift:967:13:967:13 | [post] self | test.swift:965:9:968:9 | self[return] |
| test.swift:967:13:967:13 | self | test.swift:965:9:968:9 | self[return] |
| test.swift:971:5:971:5 | SSA def(self) | test.swift:973:9:973:9 | self |
| test.swift:971:5:971:5 | self | test.swift:971:5:971:5 | SSA def(self) |
| test.swift:971:10:971:24 | SSA def(wrappedValue) | test.swift:972:19:972:19 | wrappedValue |
| test.swift:971:10:971:24 | wrappedValue | test.swift:971:10:971:24 | SSA def(wrappedValue) |
| test.swift:972:19:972:19 | [post] wrappedValue | test.swift:973:29:973:29 | wrappedValue |
| test.swift:972:19:972:19 | wrappedValue | test.swift:973:29:973:29 | wrappedValue |
| test.swift:973:9:973:9 | [post] self | test.swift:971:5:974:5 | self[return] |
| test.swift:973:9:973:9 | self | test.swift:971:5:974:5 | self[return] |
| test.swift:978:6:978:38 | call to MySimplePropertyWrapper.init(wrappedValue:) | test.swift:978:34:978:34 | a |
| test.swift:978:34:978:34 | value | test.swift:978:34:978:34 | SSA def(value) |
| test.swift:978:34:978:34 | value | test.swift:978:34:978:34 | SSA def(value) |
| test.swift:983:6:983:45 | call to MySimplePropertyWrapper.init(wrappedValue:) | test.swift:983:34:983:34 | b |
| test.swift:983:34:983:34 | value | test.swift:983:34:983:34 | SSA def(value) |
| test.swift:983:34:983:34 | value | test.swift:983:34:983:34 | SSA def(value) |
| test.swift:988:6:988:38 | call to MySimplePropertyWrapper.init(wrappedValue:) | test.swift:988:34:988:34 | c |
| test.swift:988:34:988:34 | value | test.swift:988:34:988:34 | SSA def(value) |
| test.swift:988:34:988:34 | value | test.swift:988:34:988:34 | SSA def(value) |

Просмотреть файл

@ -931,3 +931,64 @@ func testOpenExistentialExpr(x: MyProtocol, y: MyProcotolImpl) {
}
// ---
@propertyWrapper struct MyTaintPropertyWrapper {
var wrappedValue: Int {
get { return source() }
set { sink(arg: newValue) } // $ flow=943 flow=950
}
init(wrappedValue: Int) {
sink(arg: wrappedValue) // $ flow=948
self.wrappedValue = source()
}
}
func test_my_taint_property_wrapper() {
@MyTaintPropertyWrapper var x: Int = source()
sink(arg: x) // $ flow=937
x = source()
sink(arg: x) // $ flow=937
}
// ---
@propertyWrapper struct MySimplePropertyWrapper {
var wrappedValue: Int {
didSet {
sink(arg: wrappedValue) // $ flow=980 flow=991
}
}
var projectedValue: Int {
get { wrappedValue }
set {
sink(arg: wrappedValue) // $ MISSING: flow=991
wrappedValue = newValue
}
}
init(wrappedValue: Int) {
sink(arg: wrappedValue) // $ flow=983
self.wrappedValue = wrappedValue
}
}
func test_my_property_wrapper() {
@MySimplePropertyWrapper var a = 0
sink(arg: a)
a = source()
sink(arg: a) // $ MISSING: flow=980
@MySimplePropertyWrapper var b = source()
sink(arg: b) // $ MISSING: flow=983
b = 0
sink(arg: b)
@MySimplePropertyWrapper var c = 0
sink(arg: c)
sink(arg: $c)
$c = source()
sink(arg: c) // $ MISSING: flow=991
sink(arg: $c) // $ MISSING: flow=991
}

Просмотреть файл

@ -0,0 +1,98 @@
// --- stubs ---
protocol View {
}
struct Binding<Value> {
}
@propertyWrapper
struct State<Value> { // an @State
var wrappedValue: Value
var projectedValue: Binding<Value> { get { return 0 as! Binding<Value> } } // what you get with `$`
}
struct LocalizedStringKey : ExpressibleByStringLiteral {
typealias StringLiteralType = String
init(stringLiteral value: Self.StringLiteralType) {
}
}
struct Label<Title, Icon> : View where Title : View, Icon : View {
}
struct Text : View {
}
struct TextField<Label> : View where Label : View {
init(_ titleKey: LocalizedStringKey, text: Binding<String>) where Label == Text { }
}
struct SecureField<Label> : View where Label : View {
init(_ titleKey: LocalizedStringKey, text: Binding<String>, prompt: Text?) where Label == Text { }
}
struct TextEditor : View {
init(text: Binding<String>) { }
}
struct SubmitTriggers {
init(rawValue: UInt) {
self.rawValue = rawValue
}
var rawValue: UInt
static let text = SubmitTriggers(rawValue: 1)
}
extension View {
func onSubmit(
of triggers: SubmitTriggers = .text,
_ action: @escaping (() -> Void)
) -> some View {
return self
}
}
// --- tests ---
func sink(arg: Any) { }
func mkHarmlessBinding(text: Binding<String>) { }
struct MyStruct {
@State var input: String = "default value"
@State var harmless: String = "default value"
@State var harmless2: String = "default value"
var myView1: some View {
TextField("title", text: $input)
.onSubmit {
sink(arg: input) // $ MISSING: tainted
sink(arg: harmless)
mkHarmlessBinding(text: $harmless2)
sink(arg: harmless2)
}
}
@State var secureInput: String = "default value"
var myView2: some View {
SecureField("title", text: $secureInput, prompt: nil)
.onSubmit {
sink(arg: secureInput) // $ MISSING: tainted
}
}
@State var longInput: String = "default value"
var myView3: some View {
TextEditor(text: $longInput)
.onSubmit {
sink(arg: longInput) // $ MISSING: tainted
}
}
}

Просмотреть файл

@ -0,0 +1,83 @@
// --- stubs ---
protocol View {
}
struct Binding<Value> {
}
@propertyWrapper
struct State<Value> { // an @State
var wrappedValue: Value
var projectedValue: Binding<Value> { get { return 0 as! Binding<Value> } } // what you get with `$`
}
struct LocalizedStringKey : ExpressibleByStringLiteral {
typealias StringLiteralType = String
init(stringLiteral value: Self.StringLiteralType) {
}
}
struct Label<Title, Icon> : View where Title : View, Icon : View {
}
struct Text : View {
}
struct TextField<Label> : View where Label : View {
init(_ titleKey: LocalizedStringKey, text: Binding<String>) where Label == Text { }
}
struct SecureField<Label> : View where Label : View {
init(_ titleKey: LocalizedStringKey, text: Binding<String>, prompt: Text?) where Label == Text { }
}
struct SubmitTriggers {
init(rawValue: UInt) {
self.rawValue = rawValue
}
var rawValue: UInt
static let text = SubmitTriggers(rawValue: 1)
}
extension View {
func onSubmit(
of triggers: SubmitTriggers = .text,
_ action: @escaping (() -> Void)
) -> some View {
return self
}
}
struct URL
{
init?(string: String) {}
init?(string: String, relativeTo: URL?) {}
}
// --- tests ---
func mkHarmlessBinding(text: Binding<String>) { }
struct MyStruct {
@State var textInput: String = "default value"
@State var secureInput: String = "default value"
var myView1: some View {
TextField("title", text: $textInput)
.onSubmit {
_ = URL(string: "http://example.com/page?text=\(textInput)"); // GOOD (not sensitive)
}
}
var myView2: some View {
SecureField("title", text: $secureInput, prompt: nil)
.onSubmit {
_ = URL(string: "http://example.com/login?key=\(secureInput)"); // BAD [NOT DETECTED]
}
}
}