diff --git a/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.expected b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.expected new file mode 100644 index 00000000..e69de29b diff --git a/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.ql b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.ql new file mode 100644 index 00000000..881d262f --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.ql @@ -0,0 +1,2 @@ +import go +import TestUtilities.InlineFlowTest diff --git a/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/genericfunctions.go b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/genericfunctions.go new file mode 100644 index 00000000..2ab18e23 --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/genericfunctions.go @@ -0,0 +1,49 @@ +package main + +func genericSource[T any](t T) string { + x := source() + return x +} + +func genericIdentity[T any](t T) T { + return t +} + +func genericSink1[T any](t T, u string) { + sink(u) // $ hasValueFlow="u" +} + +func genericSink2[T any](t T, u string) { + sink(u) // $ hasValueFlow="u" +} + +func test() { + var x struct { + x1 int + x2 string + } + { + s := genericSource(x) + sink(s) // $ hasValueFlow="s" + } + { + s := genericSource[int8](2) + sink(s) // $ hasValueFlow="s" + } + { + s := genericIdentity(source()) + sink(s) // $ hasValueFlow="s" + } + { + s := genericIdentity[string](source()) + sink(s) // $ hasValueFlow="s" + } + { + s := source() + genericSink1(x, s) + } + { + s := source() + genericSink2[uint16](3, s) + } +} diff --git a/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/generictypesandmethods.go b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/generictypesandmethods.go new file mode 100644 index 00000000..18d42d0e --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/generictypesandmethods.go @@ -0,0 +1,88 @@ +package main + +func source() string { + return "untrusted data" +} + +func sink(string) { +} + +type GenericStruct1[T any] struct { + Field T +} + +func (g GenericStruct1[U]) Identity(u U) U { return u } +func (g GenericStruct1[U]) Getter() U { return g.Field } +func (g GenericStruct1[U]) Setter(u U) { g.Field = u } + +type GenericStruct2[S, T any] struct { + Field1 S + Field2 T +} + +func (g GenericStruct2[U, _]) Identity1(u U) U { return u } +func (g GenericStruct2[U, _]) Getter1() U { return g.Field1 } +func (g GenericStruct2[U, _]) Setter1(u U) { g.Field1 = u } + +func (g GenericStruct2[_, V]) Identity2(v V) V { return v } +func (g GenericStruct2[_, V]) Getter2() V { return g.Field2 } +func (g GenericStruct2[_, V]) Setter2(v V) { g.Field2 = v } + +func main() { + { + gs1 := GenericStruct1[string]{source()} + sink(gs1.Field) // $ hasValueFlow="selection of Field" + } + { + gs1 := GenericStruct1[string]{""} + sink(gs1.Identity(source())) // $ hasValueFlow="call to Identity" + } + { + gs1 := GenericStruct1[string]{""} + gs1.Field = source() + sink(gs1.Getter()) // $ hasValueFlow="call to Getter" + } + { + gs1 := GenericStruct1[string]{""} + gs1.Setter(source()) + sink(gs1.Field) // $ hasValueFlow="selection of Field" + } + + { + gs2 := GenericStruct2[string, string]{source(), ""} + sink(gs2.Field1) // $ hasValueFlow="selection of Field1" + } + { + gs2 := GenericStruct2[string, string]{"", ""} + sink(gs2.Identity1(source())) // $ hasValueFlow="call to Identity1" + } + { + gs2 := GenericStruct2[string, string]{"", ""} + gs2.Field1 = source() + sink(gs2.Getter1()) // $ hasValueFlow="call to Getter1" + } + { + gs2 := GenericStruct2[string, string]{"", ""} + gs2.Setter1(source()) + sink(gs2.Field1) // $ hasValueFlow="selection of Field1" + } + + { + gs2 := GenericStruct2[string, string]{"", source()} + sink(gs2.Field2) // $ hasValueFlow="selection of Field2" + } + { + gs2 := GenericStruct2[string, string]{"", ""} + sink(gs2.Identity2(source())) // $ hasValueFlow="call to Identity2" + } + { + gs2 := GenericStruct2[string, string]{"", ""} + gs2.Field2 = source() + sink(gs2.Getter2()) // $ hasValueFlow="call to Getter2" + } + { + gs2 := GenericStruct2[string, string]{"", ""} + gs2.Setter2(source()) + sink(gs2.Field2) // $ hasValueFlow="selection of Field2" + } +}