diff --git a/go/ql/lib/change-notes/2026-09-09-go-1.27-models.md b/go/ql/lib/change-notes/2026-09-09-go-1.27-models.md new file mode 100644 index 000000000000..94c1b4469b73 --- /dev/null +++ b/go/ql/lib/change-notes/2026-09-09-go-1.27-models.md @@ -0,0 +1,7 @@ +--- +category: minorAnalysis +--- +* Added or improved data flow models for the following Go standard-library APIs introduced or updated in Go 1.27: + * `bytes.CutLast`, `database/sql.ConvertAssign`, `database/sql/driver.RowsColumnScanner.ScanColumn`, `net/url.URL.Clone`, `net/url.Values.Clone` and `strings.CutLast`. + * The new `encoding/json/jsontext` package. +* Added more data flow models for the `strings` package: `strings.Clone`, `Cut`, `CutPrefix`, `CutSuffix`, `Fields`, `FieldsFunc`, and `Join`; `strings.Builder.String`, `Builder.WriteByte`, and `Builder.WriteRune`; `strings.Reader.ReadByte` and `Reader.ReadRune`; and `strings.Replacer.Replace` and `Replacer.WriteString`. diff --git a/go/ql/lib/ext/bytes.model.yml b/go/ql/lib/ext/bytes.model.yml index b55749f828bf..9f5ad37176a7 100644 --- a/go/ql/lib/ext/bytes.model.yml +++ b/go/ql/lib/ext/bytes.model.yml @@ -5,6 +5,7 @@ extensions: data: - ["bytes", "", False, "Clone", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] - ["bytes", "", False, "Cut", "", "", "Argument[0]", "ReturnValue[0..1]", "taint", "manual"] + - ["bytes", "", False, "CutLast", "", "", "Argument[0]", "ReturnValue[0..1]", "taint", "manual"] - ["bytes", "", False, "CutPrefix", "", "", "Argument[0]", "ReturnValue[0]", "taint", "manual"] - ["bytes", "", False, "CutSuffix", "", "", "Argument[0]", "ReturnValue[0]", "taint", "manual"] - ["bytes", "", False, "Fields", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] diff --git a/go/ql/lib/ext/database.sql.driver.model.yml b/go/ql/lib/ext/database.sql.driver.model.yml index 0f33a6e14b8c..2c5770e26194 100644 --- a/go/ql/lib/ext/database.sql.driver.model.yml +++ b/go/ql/lib/ext/database.sql.driver.model.yml @@ -24,5 +24,6 @@ extensions: - ["database/sql/driver", "Conn", True, "Prepare", "", "", "Argument[0]", "ReturnValue[0]", "taint", "manual"] - ["database/sql/driver", "ConnPrepareContext", True, "PrepareContext", "", "", "Argument[1]", "ReturnValue[0]", "taint", "manual"] - ["database/sql/driver", "Rows", True, "Next", "", "", "Argument[receiver]", "Argument[0]", "taint", "manual"] + - ["database/sql/driver", "RowsColumnScanner", True, "ScanColumn", "", "", "Argument[receiver]", "Argument[2]", "taint", "manual"] - ["database/sql/driver", "ValueConverter", True, "ConvertValue", "", "", "Argument[0]", "ReturnValue[0]", "taint", "manual"] - ["database/sql/driver", "Valuer", True, "Value", "", "", "Argument[receiver]", "ReturnValue[0]", "taint", "manual"] diff --git a/go/ql/lib/ext/database.sql.model.yml b/go/ql/lib/ext/database.sql.model.yml index 8d67dd921423..3f37c2a6eab4 100644 --- a/go/ql/lib/ext/database.sql.model.yml +++ b/go/ql/lib/ext/database.sql.model.yml @@ -49,6 +49,7 @@ extensions: pack: codeql/go-all extensible: summaryModel data: + - ["database/sql", "", False, "ConvertAssign", "", "", "Argument[2]", "Argument[1]", "taint", "manual"] - ["database/sql", "", False, "Named", "", "", "Argument[0..1]", "ReturnValue", "taint", "manual"] - ["database/sql", "Conn", True, "PrepareContext", "", "", "Argument[1]", "ReturnValue[0]", "taint", "manual"] - ["database/sql", "DB", True, "Prepare", "", "", "Argument[0]", "ReturnValue[0]", "taint", "manual"] diff --git a/go/ql/lib/ext/encoding.json.jsontext.model.yml b/go/ql/lib/ext/encoding.json.jsontext.model.yml new file mode 100644 index 000000000000..d04afceaefe2 --- /dev/null +++ b/go/ql/lib/ext/encoding.json.jsontext.model.yml @@ -0,0 +1,36 @@ +extensions: + - addsTo: + pack: codeql/go-all + extensible: summaryModel + data: + - ["encoding/json/jsontext", "", False, "AppendFloat", "", "", "Argument[0..1]", "ReturnValue", "taint", "manual"] + - ["encoding/json/jsontext", "", False, "AppendFormat", "", "", "Argument[0..1]", "ReturnValue[0]", "taint", "manual"] + - ["encoding/json/jsontext", "", False, "AppendQuote", "", "", "Argument[0..1]", "ReturnValue[0]", "taint", "manual"] + - ["encoding/json/jsontext", "", False, "AppendUnquote", "", "", "Argument[0..1]", "ReturnValue[0]", "taint", "manual"] + - ["encoding/json/jsontext", "", False, "NewDecoder", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] + - ["encoding/json/jsontext", "", False, "Float", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] + - ["encoding/json/jsontext", "", False, "Float32", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] + - ["encoding/json/jsontext", "", False, "Int", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] + - ["encoding/json/jsontext", "", False, "String", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] + - ["encoding/json/jsontext", "", False, "Uint", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] + - ["encoding/json/jsontext", "Decoder", True, "ReadToken", "", "", "Argument[receiver]", "ReturnValue[0]", "taint", "manual"] + - ["encoding/json/jsontext", "Decoder", True, "ReadValue", "", "", "Argument[receiver]", "ReturnValue[0]", "taint", "manual"] + - ["encoding/json/jsontext", "Decoder", True, "Reset", "", "", "Argument[0]", "Argument[receiver]", "taint", "manual"] + - ["encoding/json/jsontext", "Decoder", True, "UnreadBuffer", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["encoding/json/jsontext", "Encoder", True, "Reset", "", "", "Argument[receiver]", "Argument[0]", "taint", "manual"] + - ["encoding/json/jsontext", "Encoder", True, "WriteToken", "", "", "Argument[0]", "Argument[receiver]", "taint", "manual"] + - ["encoding/json/jsontext", "Encoder", True, "WriteValue", "", "", "Argument[0]", "Argument[receiver]", "taint", "manual"] + - ["encoding/json/jsontext", "Pointer", True, "AppendToken", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["encoding/json/jsontext", "Pointer", True, "AppendToken", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] + - ["encoding/json/jsontext", "Pointer", True, "LastToken", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["encoding/json/jsontext", "Pointer", True, "Parent", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["encoding/json/jsontext", "Token", True, "Clone", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["encoding/json/jsontext", "Token", True, "Float", "", "", "Argument[receiver]", "ReturnValue[0]", "taint", "manual"] + - ["encoding/json/jsontext", "Token", True, "Float32", "", "", "Argument[receiver]", "ReturnValue[0]", "taint", "manual"] + - ["encoding/json/jsontext", "Token", True, "Int", "", "", "Argument[receiver]", "ReturnValue[0]", "taint", "manual"] + - ["encoding/json/jsontext", "Token", True, "String", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["encoding/json/jsontext", "Token", True, "Uint", "", "", "Argument[receiver]", "ReturnValue[0]", "taint", "manual"] + - ["encoding/json/jsontext", "Value", True, "Clone", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["encoding/json/jsontext", "Value", True, "MarshalJSON", "", "", "Argument[receiver]", "ReturnValue[0]", "taint", "manual"] + - ["encoding/json/jsontext", "Value", True, "String", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["encoding/json/jsontext", "Value", True, "UnmarshalJSON", "", "", "Argument[0]", "Argument[receiver]", "taint", "manual"] diff --git a/go/ql/lib/ext/net.url.model.yml b/go/ql/lib/ext/net.url.model.yml index 0b48aa2352c2..ca4cf2c170f2 100644 --- a/go/ql/lib/ext/net.url.model.yml +++ b/go/ql/lib/ext/net.url.model.yml @@ -12,6 +12,7 @@ extensions: - ["net/url", "", False, "QueryUnescape", "", "", "Argument[0]", "ReturnValue[0]", "taint", "manual"] - ["net/url", "", False, "User", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] - ["net/url", "", False, "UserPassword", "", "", "Argument[0..1]", "ReturnValue", "taint", "manual"] + - ["net/url", "URL", True, "Clone", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - ["net/url", "URL", True, "EscapedPath", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - ["net/url", "URL", True, "Hostname", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - ["net/url", "URL", True, "MarshalBinary", "", "", "Argument[receiver]", "ReturnValue[0]", "taint", "manual"] @@ -24,5 +25,6 @@ extensions: - ["net/url", "URL", True, "ResolveReference", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] - ["net/url", "Userinfo", True, "Password", "", "", "Argument[receiver]", "ReturnValue[0]", "taint", "manual"] - ["net/url", "Userinfo", True, "Username", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["net/url", "Values", True, "Clone", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - ["net/url", "Values", True, "Encode", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - ["net/url", "Values", True, "Get", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] diff --git a/go/ql/lib/ext/strings.model.yml b/go/ql/lib/ext/strings.model.yml index 01015b31517e..4a6d852baab4 100644 --- a/go/ql/lib/ext/strings.model.yml +++ b/go/ql/lib/ext/strings.model.yml @@ -3,9 +3,15 @@ extensions: pack: codeql/go-all extensible: summaryModel data: - - ["strings", "", False, "Fields", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] - - ["strings", "", False, "FieldsFunc", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] - - ["strings", "", False, "Join", "", "", "Argument[0..1]", "ReturnValue", "taint", "manual"] + - ["strings", "", False, "Clone", "", "", "Argument[0]", "ReturnValue", "value", "manual"] + - ["strings", "", False, "Cut", "", "", "Argument[0]", "ReturnValue[0..1]", "taint", "manual"] + - ["strings", "", False, "CutLast", "", "", "Argument[0]", "ReturnValue[0..1]", "taint", "manual"] + - ["strings", "", False, "CutPrefix", "", "", "Argument[0]", "ReturnValue[0]", "taint", "manual"] + - ["strings", "", False, "CutSuffix", "", "", "Argument[0]", "ReturnValue[0]", "taint", "manual"] + - ["strings", "", False, "Fields", "", "", "Argument[0]", "ReturnValue.ArrayElement", "taint", "manual"] + - ["strings", "", False, "FieldsFunc", "", "", "Argument[0]", "ReturnValue.ArrayElement", "taint", "manual"] + - ["strings", "", False, "Join", "", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "manual"] + - ["strings", "", False, "Join", "", "", "Argument[1]", "ReturnValue", "taint", "manual"] - ["strings", "", False, "Map", "", "", "Argument[1]", "ReturnValue", "taint", "manual"] - ["strings", "", False, "NewReader", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] - ["strings", "", False, "Repeat", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] @@ -34,6 +40,13 @@ extensions: - ["strings", "", False, "TrimRightFunc", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] - ["strings", "", False, "TrimSpace", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] - ["strings", "", False, "TrimSuffix", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] + - ["strings", "Builder", True, "String", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["strings", "Builder", True, "WriteByte", "", "", "Argument[0]", "Argument[receiver]", "taint", "manual"] + - ["strings", "Builder", True, "WriteRune", "", "", "Argument[0]", "Argument[receiver]", "taint", "manual"] + - ["strings", "Reader", True, "ReadByte", "", "", "Argument[receiver]", "ReturnValue[0]", "taint", "manual"] + - ["strings", "Reader", True, "ReadRune", "", "", "Argument[receiver]", "ReturnValue[0]", "taint", "manual"] - ["strings", "Reader", True, "Reset", "", "", "Argument[0]", "Argument[receiver]", "taint", "manual"] + - ["strings", "Replacer", True, "Replace", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - ["strings", "Replacer", True, "Replace", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] + - ["strings", "Replacer", True, "WriteString", "", "", "Argument[receiver]", "Argument[0]", "taint", "manual"] - ["strings", "Replacer", True, "WriteString", "", "", "Argument[1]", "Argument[0]", "taint", "manual"] diff --git a/go/ql/lib/semmle/go/frameworks/stdlib/EncodingJson.qll b/go/ql/lib/semmle/go/frameworks/stdlib/EncodingJson.qll index bf6e25ff9f12..e04f9392def7 100644 --- a/go/ql/lib/semmle/go/frameworks/stdlib/EncodingJson.qll +++ b/go/ql/lib/semmle/go/frameworks/stdlib/EncodingJson.qll @@ -41,9 +41,15 @@ module EncodingJson { FunctionOutput outp; FunctionModels() { - // signature: func NewEncoder(w io.Writer) *Encoder - this.hasQualifiedName("encoding/json", "NewEncoder") and - (inp.isResult() and outp.isParameter(0)) + ( + // signature: func NewEncoder(w io.Writer) *Encoder + this.hasQualifiedName("encoding/json", "NewEncoder") + or + // signature: func NewEncoder(w io.Writer, opts ...Options) *Encoder + this.hasQualifiedName("encoding/json/jsontext", "NewEncoder") + ) and + inp.isResult() and + outp.isParameter(0) } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { diff --git a/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/Bytes.go b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/Bytes.go index ac528c46267d..d744819e2807 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/Bytes.go +++ b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/Bytes.go @@ -335,6 +335,20 @@ func TaintStepTest_Cutright(sourceCQL interface{}) interface{} { return right } +func TaintStepTest_CutLastleft(sourceCQL interface{}) interface{} { + fromReader628 := sourceCQL.([]byte) + sep := []byte{} + left, _, _ := bytes.CutLast(fromReader628, sep) + return left +} + +func TaintStepTest_CutLastright(sourceCQL interface{}) interface{} { + fromReader628 := sourceCQL.([]byte) + sep := []byte{} + _, right, _ := bytes.CutLast(fromReader628, sep) + return right +} + func TaintStepTest_CutPrefix(sourceCQL interface{}) interface{} { fromReader628 := sourceCQL.([]byte) sep := []byte{} @@ -636,4 +650,14 @@ func RunAllTaints_Bytes() { out := TaintStepTest_BytesBufferPeek(source) sink(55, out) } + { + source := newSource(56) + out := TaintStepTest_CutLastleft(source) + sink(56, out) + } + { + source := newSource(57) + out := TaintStepTest_CutLastright(source) + sink(57, out) + } } diff --git a/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/DatabaseSql.go b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/DatabaseSql.go index 59a61cff31a9..3e2f5af18bba 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/DatabaseSql.go +++ b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/DatabaseSql.go @@ -2,7 +2,10 @@ package main -import "database/sql" +import ( + "database/sql" + "database/sql/driver" +) func TaintStepTest_DatabaseSqlNamed_B0I0O0(sourceCQL interface{}) interface{} { fromString656 := sourceCQL.(string) @@ -79,6 +82,12 @@ func TaintStepTest_DatabaseSqlConnPrepareContext(sourceCQL interface{}) interfac return intoPrepareResult0 } +func TaintStepTest_DatabaseSqlConvertAssign(sourceCQL interface{}) interface{} { + var destination interface{} + sql.ConvertAssign(driver.ScanContext{}, &destination, sourceCQL) + return destination +} + func RunAllTaints_DatabaseSql() { { source := newSource(0) @@ -135,4 +144,9 @@ func RunAllTaints_DatabaseSql() { out := TaintStepTest_DatabaseSqlConnPrepareContext(source) sink(10, out) } + { + source := newSource(11) + out := TaintStepTest_DatabaseSqlConvertAssign(source) + sink(11, out) + } } diff --git a/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/DatabaseSqlDriver.go b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/DatabaseSqlDriver.go index fe44132e0fb4..ff572e9d889d 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/DatabaseSqlDriver.go +++ b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/DatabaseSqlDriver.go @@ -45,6 +45,13 @@ func TaintStepTest_DatabaseSqlDriverValuerValue_B0I0O0(sourceCQL interface{}) in return intoValue982 } +func TaintStepTest_DatabaseSqlDriverRowsColumnScannerScanColumn(sourceCQL interface{}) interface{} { + fromRows := sourceCQL.(driver.RowsColumnScanner) + var destination interface{} + fromRows.ScanColumn(driver.ScanContext{}, 0, &destination) + return destination +} + func RunAllTaints_DatabaseSqlDriver() { { source := newSource(0) @@ -76,4 +83,9 @@ func RunAllTaints_DatabaseSqlDriver() { out := TaintStepTest_DatabaseSqlDriverValuerValue_B0I0O0(source) sink(5, out) } + { + source := newSource(6) + out := TaintStepTest_DatabaseSqlDriverRowsColumnScannerScanColumn(source) + sink(6, out) + } } diff --git a/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/EncodingJsonJsontext.go b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/EncodingJsonJsontext.go new file mode 100644 index 000000000000..771de17ded3f --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/EncodingJsonJsontext.go @@ -0,0 +1,413 @@ +package main + +import ( + "encoding/json/jsontext" + "io" +) + +func TaintStepTest_JsontextAppendFloat_I0(sourceCQL interface{}) interface{} { + fromByte := sourceCQL.([]byte) + intoByte := jsontext.AppendFloat(fromByte, 0, 64) + return intoByte +} + +func TaintStepTest_JsontextAppendFloat_I1(sourceCQL interface{}) interface{} { + fromFloat := sourceCQL.(float64) + intoByte := jsontext.AppendFloat(nil, fromFloat, 64) + return intoByte +} + +func TaintStepTest_JsontextAppendFormat_I0(sourceCQL interface{}) interface{} { + fromByte := sourceCQL.([]byte) + intoByte, _ := jsontext.AppendFormat(fromByte, []byte{}) + return intoByte +} + +func TaintStepTest_JsontextAppendFormat_I1(sourceCQL interface{}) interface{} { + fromByte := sourceCQL.([]byte) + intoByte, _ := jsontext.AppendFormat(nil, fromByte) + return intoByte +} + +func TaintStepTest_JsontextAppendQuote_I0(sourceCQL interface{}) interface{} { + fromByte := sourceCQL.([]byte) + intoByte, _ := jsontext.AppendQuote(fromByte, []byte{}) + return intoByte +} + +func TaintStepTest_JsontextAppendQuote_I1(sourceCQL interface{}) interface{} { + fromString := sourceCQL.(string) + intoByte, _ := jsontext.AppendQuote(nil, fromString) + return intoByte +} + +func TaintStepTest_JsontextAppendUnquote_I0(sourceCQL interface{}) interface{} { + fromByte := sourceCQL.([]byte) + intoByte, _ := jsontext.AppendUnquote(fromByte, []byte{}) + return intoByte +} + +func TaintStepTest_JsontextAppendUnquote_I1(sourceCQL interface{}) interface{} { + fromString := sourceCQL.(string) + intoByte, _ := jsontext.AppendUnquote(nil, fromString) + return intoByte +} + +func TaintStepTest_JsontextNewDecoder(sourceCQL interface{}) interface{} { + fromReader := sourceCQL.(io.Reader) + intoDecoder := jsontext.NewDecoder(fromReader) + return intoDecoder +} + +func TaintStepTest_JsontextNewEncoder(sourceCQL interface{}) interface{} { + fromEncoder := sourceCQL.(*jsontext.Encoder) + var intoWriter io.Writer + intermediateCQL := jsontext.NewEncoder(intoWriter) + link(fromEncoder, intermediateCQL) + return intoWriter +} + +func TaintStepTest_JsontextFloat(sourceCQL interface{}) interface{} { + fromFloat := sourceCQL.(float64) + intoToken := jsontext.Float(fromFloat) + return intoToken +} + +func TaintStepTest_JsontextFloat32(sourceCQL interface{}) interface{} { + fromFloat := sourceCQL.(float32) + intoToken := jsontext.Float32(fromFloat) + return intoToken +} + +func TaintStepTest_JsontextInt(sourceCQL interface{}) interface{} { + fromInt := sourceCQL.(int64) + intoToken := jsontext.Int(fromInt) + return intoToken +} + +func TaintStepTest_JsontextString(sourceCQL interface{}) interface{} { + fromString := sourceCQL.(string) + intoToken := jsontext.String(fromString) + return intoToken +} + +func TaintStepTest_JsontextUint(sourceCQL interface{}) interface{} { + fromUint := sourceCQL.(uint64) + intoToken := jsontext.Uint(fromUint) + return intoToken +} + +func TaintStepTest_JsontextDecoderReadToken(sourceCQL interface{}) interface{} { + fromDecoder := sourceCQL.(jsontext.Decoder) + intoToken, _ := fromDecoder.ReadToken() + return intoToken +} + +func TaintStepTest_JsontextDecoderReadValue(sourceCQL interface{}) interface{} { + fromDecoder := sourceCQL.(jsontext.Decoder) + intoValue, _ := fromDecoder.ReadValue() + return intoValue +} + +func TaintStepTest_JsontextDecoderReset(sourceCQL interface{}) interface{} { + fromReader := sourceCQL.(io.Reader) + var intoDecoder jsontext.Decoder + intoDecoder.Reset(fromReader) + return intoDecoder +} + +func TaintStepTest_JsontextDecoderUnreadBuffer(sourceCQL interface{}) interface{} { + fromDecoder := sourceCQL.(jsontext.Decoder) + intoByte := fromDecoder.UnreadBuffer() + return intoByte +} + +func TaintStepTest_JsontextEncoderReset(sourceCQL interface{}) interface{} { + fromEncoder := sourceCQL.(jsontext.Encoder) + var intoWriter io.Writer + fromEncoder.Reset(intoWriter) + return intoWriter +} + +func TaintStepTest_JsontextEncoderWriteToken(sourceCQL interface{}) interface{} { + fromToken := sourceCQL.(jsontext.Token) + var intoEncoder jsontext.Encoder + intoEncoder.WriteToken(fromToken) + return intoEncoder +} + +func TaintStepTest_JsontextEncoderWriteValue(sourceCQL interface{}) interface{} { + fromValue := sourceCQL.(jsontext.Value) + var intoEncoder jsontext.Encoder + intoEncoder.WriteValue(fromValue) + return intoEncoder +} + +func TaintStepTest_JsontextPointerAppendToken_Receiver(sourceCQL interface{}) interface{} { + fromPointer := sourceCQL.(jsontext.Pointer) + intoPointer := fromPointer.AppendToken("") + return intoPointer +} + +func TaintStepTest_JsontextPointerAppendToken_I0(sourceCQL interface{}) interface{} { + fromString := sourceCQL.(string) + var pointer jsontext.Pointer + intoPointer := pointer.AppendToken(fromString) + return intoPointer +} + +func TaintStepTest_JsontextPointerLastToken(sourceCQL interface{}) interface{} { + fromPointer := sourceCQL.(jsontext.Pointer) + intoString := fromPointer.LastToken() + return intoString +} + +func TaintStepTest_JsontextPointerParent(sourceCQL interface{}) interface{} { + fromPointer := sourceCQL.(jsontext.Pointer) + intoPointer := fromPointer.Parent() + return intoPointer +} + +func TaintStepTest_JsontextTokenClone(sourceCQL interface{}) interface{} { + fromToken := sourceCQL.(jsontext.Token) + intoToken := fromToken.Clone() + return intoToken +} + +func TaintStepTest_JsontextTokenFloat(sourceCQL interface{}) interface{} { + fromToken := sourceCQL.(jsontext.Token) + intoFloat, _ := fromToken.Float() + return intoFloat +} + +func TaintStepTest_JsontextTokenFloat32(sourceCQL interface{}) interface{} { + fromToken := sourceCQL.(jsontext.Token) + intoFloat, _ := fromToken.Float32() + return intoFloat +} + +func TaintStepTest_JsontextTokenInt(sourceCQL interface{}) interface{} { + fromToken := sourceCQL.(jsontext.Token) + intoInt, _ := fromToken.Int() + return intoInt +} + +func TaintStepTest_JsontextTokenString(sourceCQL interface{}) interface{} { + fromToken := sourceCQL.(jsontext.Token) + intoString := fromToken.String() + return intoString +} + +func TaintStepTest_JsontextTokenUint(sourceCQL interface{}) interface{} { + fromToken := sourceCQL.(jsontext.Token) + intoUint, _ := fromToken.Uint() + return intoUint +} + +func TaintStepTest_JsontextValueClone(sourceCQL interface{}) interface{} { + fromValue := sourceCQL.(jsontext.Value) + intoValue := fromValue.Clone() + return intoValue +} + +func TaintStepTest_JsontextValueMarshalJSON(sourceCQL interface{}) interface{} { + fromValue := sourceCQL.(jsontext.Value) + intoByte, _ := fromValue.MarshalJSON() + return intoByte +} + +func TaintStepTest_JsontextValueString(sourceCQL interface{}) interface{} { + fromValue := sourceCQL.(jsontext.Value) + intoString := fromValue.String() + return intoString +} + +func TaintStepTest_JsontextValueUnmarshalJSON(sourceCQL interface{}) interface{} { + fromByte := sourceCQL.([]byte) + var intoValue jsontext.Value + intoValue.UnmarshalJSON(fromByte) + return intoValue +} + +func RunAllTaints_EncodingJsonJsontext() { + { + source := newSource(0) + out := TaintStepTest_JsontextAppendFloat_I0(source) + sink(0, out) + } + { + source := newSource(1) + out := TaintStepTest_JsontextAppendFloat_I1(source) + sink(1, out) + } + { + source := newSource(2) + out := TaintStepTest_JsontextAppendFormat_I0(source) + sink(2, out) + } + { + source := newSource(3) + out := TaintStepTest_JsontextAppendFormat_I1(source) + sink(3, out) + } + { + source := newSource(4) + out := TaintStepTest_JsontextAppendQuote_I0(source) + sink(4, out) + } + { + source := newSource(5) + out := TaintStepTest_JsontextAppendQuote_I1(source) + sink(5, out) + } + { + source := newSource(6) + out := TaintStepTest_JsontextAppendUnquote_I0(source) + sink(6, out) + } + { + source := newSource(7) + out := TaintStepTest_JsontextAppendUnquote_I1(source) + sink(7, out) + } + { + source := newSource(8) + out := TaintStepTest_JsontextNewDecoder(source) + sink(8, out) + } + { + source := newSource(9) + out := TaintStepTest_JsontextNewEncoder(source) + sink(9, out) + } + { + source := newSource(10) + out := TaintStepTest_JsontextFloat(source) + sink(10, out) + } + { + source := newSource(11) + out := TaintStepTest_JsontextFloat32(source) + sink(11, out) + } + { + source := newSource(12) + out := TaintStepTest_JsontextInt(source) + sink(12, out) + } + { + source := newSource(13) + out := TaintStepTest_JsontextString(source) + sink(13, out) + } + { + source := newSource(14) + out := TaintStepTest_JsontextUint(source) + sink(14, out) + } + { + source := newSource(15) + out := TaintStepTest_JsontextDecoderReadToken(source) + sink(15, out) + } + { + source := newSource(16) + out := TaintStepTest_JsontextDecoderReadValue(source) + sink(16, out) + } + { + source := newSource(17) + out := TaintStepTest_JsontextDecoderReset(source) + sink(17, out) + } + { + source := newSource(18) + out := TaintStepTest_JsontextDecoderUnreadBuffer(source) + sink(18, out) + } + { + source := newSource(19) + out := TaintStepTest_JsontextEncoderReset(source) + sink(19, out) + } + { + source := newSource(20) + out := TaintStepTest_JsontextEncoderWriteToken(source) + sink(20, out) + } + { + source := newSource(21) + out := TaintStepTest_JsontextEncoderWriteValue(source) + sink(21, out) + } + { + source := newSource(22) + out := TaintStepTest_JsontextPointerAppendToken_Receiver(source) + sink(22, out) + } + { + source := newSource(23) + out := TaintStepTest_JsontextPointerAppendToken_I0(source) + sink(23, out) + } + { + source := newSource(24) + out := TaintStepTest_JsontextPointerLastToken(source) + sink(24, out) + } + { + source := newSource(25) + out := TaintStepTest_JsontextPointerParent(source) + sink(25, out) + } + { + source := newSource(26) + out := TaintStepTest_JsontextTokenClone(source) + sink(26, out) + } + { + source := newSource(27) + out := TaintStepTest_JsontextTokenFloat(source) + sink(27, out) + } + { + source := newSource(28) + out := TaintStepTest_JsontextTokenFloat32(source) + sink(28, out) + } + { + source := newSource(29) + out := TaintStepTest_JsontextTokenInt(source) + sink(29, out) + } + { + source := newSource(30) + out := TaintStepTest_JsontextTokenString(source) + sink(30, out) + } + { + source := newSource(31) + out := TaintStepTest_JsontextTokenUint(source) + sink(31, out) + } + { + source := newSource(32) + out := TaintStepTest_JsontextValueClone(source) + sink(32, out) + } + { + source := newSource(33) + out := TaintStepTest_JsontextValueMarshalJSON(source) + sink(33, out) + } + { + source := newSource(34) + out := TaintStepTest_JsontextValueString(source) + sink(34, out) + } + { + source := newSource(35) + out := TaintStepTest_JsontextValueUnmarshalJSON(source) + sink(35, out) + } +} diff --git a/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/NetHttp.go b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/NetHttp.go index ebf7fce029d0..86129bedbff2 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/NetHttp.go +++ b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/NetHttp.go @@ -6,6 +6,7 @@ import ( "bufio" "io" "net/http" + "net/url" ) func TaintStepTest_NetHttpCanonicalHeaderKey_B0I0O0(sourceCQL interface{}) interface{} { @@ -175,6 +176,16 @@ func TaintStepTest_NetHttpResponseWriterWrite_B0I0O0(sourceCQL interface{}) inte return intoResponseWriter139 } +func TaintStepTest_NetUrlURLClone(sourceCQL interface{}) interface{} { + fromURL := sourceCQL.(*url.URL) + return fromURL.Clone() +} + +func TaintStepTest_NetUrlValuesClone(sourceCQL interface{}) interface{} { + fromValues := sourceCQL.(url.Values) + return fromValues.Clone() +} + func RunAllTaints_NetHttp() { { source := newSource(0) @@ -306,4 +317,14 @@ func RunAllTaints_NetHttp() { out := TaintStepTest_NetHttpResponseWriterWrite_B0I0O0(source) sink(23, out) } + { + source := newSource(26) + out := TaintStepTest_NetUrlURLClone(source) + sink(26, out) + } + { + source := newSource(27) + out := TaintStepTest_NetUrlValuesClone(source) + sink(27, out) + } } diff --git a/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/Strings.go b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/Strings.go index 878f4809130b..ac152a84c3b5 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/Strings.go +++ b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/Strings.go @@ -7,21 +7,80 @@ import ( "strings" ) +func TaintStepTest_StringsClone(sourceCQL interface{}) interface{} { + return strings.Clone(sourceCQL.(string)) +} + +func TaintStepTest_StringsCutleft(sourceCQL interface{}) interface{} { + left, _, _ := strings.Cut(sourceCQL.(string), "") + return left +} + +func TaintStepTest_StringsCutright(sourceCQL interface{}) interface{} { + _, right, _ := strings.Cut(sourceCQL.(string), "") + return right +} + +func TaintStepTest_StringsCutPrefix(sourceCQL interface{}) interface{} { + result, _ := strings.CutPrefix(sourceCQL.(string), "") + return result +} + +func TaintStepTest_StringsCutSuffix(sourceCQL interface{}) interface{} { + result, _ := strings.CutSuffix(sourceCQL.(string), "") + return result +} + +func TaintStepTest_StringsFieldsFuncSeq(sourceCQL interface{}) interface{} { + for result := range strings.FieldsFuncSeq(sourceCQL.(string), nil) { + return result + } + return "" +} + +func TaintStepTest_StringsFieldsSeq(sourceCQL interface{}) interface{} { + for result := range strings.FieldsSeq(sourceCQL.(string)) { + return result + } + return "" +} + +func TaintStepTest_StringsLines(sourceCQL interface{}) interface{} { + for result := range strings.Lines(sourceCQL.(string)) { + return result + } + return "" +} + +func TaintStepTest_StringsSplitAfterSeq(sourceCQL interface{}) interface{} { + for result := range strings.SplitAfterSeq(sourceCQL.(string), "") { + return result + } + return "" +} + +func TaintStepTest_StringsSplitSeq(sourceCQL interface{}) interface{} { + for result := range strings.SplitSeq(sourceCQL.(string), "") { + return result + } + return "" +} + func TaintStepTest_StringsFields_B0I0O0(sourceCQL interface{}) interface{} { fromString656 := sourceCQL.(string) intoString414 := strings.Fields(fromString656) - return intoString414 + return intoString414[0] } func TaintStepTest_StringsFieldsFunc_B0I0O0(sourceCQL interface{}) interface{} { fromString518 := sourceCQL.(string) intoString650 := strings.FieldsFunc(fromString518, nil) - return intoString650 + return intoString650[0] } func TaintStepTest_StringsJoin_B0I0O0(sourceCQL interface{}) interface{} { - fromString784 := sourceCQL.([]string) - intoString957 := strings.Join(fromString784, "") + fromString784 := sourceCQL.(string) + intoString957 := strings.Join([]string{fromString784}, "") return intoString957 } @@ -231,6 +290,18 @@ func TaintStepTest_StringsBuilderWriteString_B0I0O0(sourceCQL interface{}) inter return intoBuilder389 } +func TaintStepTest_StringsBuilderWriteByte(sourceCQL interface{}) interface{} { + var builder strings.Builder + builder.WriteByte(sourceCQL.(byte)) + return builder +} + +func TaintStepTest_StringsBuilderWriteRune(sourceCQL interface{}) interface{} { + var builder strings.Builder + builder.WriteRune(sourceCQL.(rune)) + return builder +} + func TaintStepTest_StringsReaderRead_B0I0O0(sourceCQL interface{}) interface{} { fromReader198 := sourceCQL.(strings.Reader) var intoByte477 []byte @@ -245,6 +316,18 @@ func TaintStepTest_StringsReaderReadAt_B0I0O0(sourceCQL interface{}) interface{} return intoByte382 } +func TaintStepTest_StringsReaderReadByte(sourceCQL interface{}) interface{} { + reader := sourceCQL.(strings.Reader) + result, _ := reader.ReadByte() + return result +} + +func TaintStepTest_StringsReaderReadRune(sourceCQL interface{}) interface{} { + reader := sourceCQL.(strings.Reader) + result, _, _ := reader.ReadRune() + return result +} + func TaintStepTest_StringsReaderReset_B0I0O0(sourceCQL interface{}) interface{} { fromString715 := sourceCQL.(string) var intoReader179 strings.Reader @@ -274,6 +357,28 @@ func TaintStepTest_StringsReplacerWriteString_B0I0O0(sourceCQL interface{}) inte return intoWriter754 } +func TaintStepTest_StringsReplacerReplaceReceiver(sourceCQL interface{}) interface{} { + return sourceCQL.(*strings.Replacer).Replace("") +} + +func TaintStepTest_StringsReplacerWriteStringReceiver(sourceCQL interface{}) interface{} { + var writer io.Writer + sourceCQL.(*strings.Replacer).WriteString(writer, "") + return writer +} + +func TaintStepTest_StringsCutLastleft(sourceCQL interface{}) interface{} { + fromString := sourceCQL.(string) + left, _, _ := strings.CutLast(fromString, "") + return left +} + +func TaintStepTest_StringsCutLastright(sourceCQL interface{}) interface{} { + fromString := sourceCQL.(string) + _, right, _ := strings.CutLast(fromString, "") + return right +} + func RunAllTaints_Strings() { { source := newSource(0) @@ -490,4 +595,94 @@ func RunAllTaints_Strings() { out := TaintStepTest_StringsReplacerWriteString_B0I0O0(source) sink(42, out) } + { + source := newSource(43) + out := TaintStepTest_StringsCutLastleft(source) + sink(43, out) + } + { + source := newSource(44) + out := TaintStepTest_StringsCutLastright(source) + sink(44, out) + } + { + source := newSource(45) + out := TaintStepTest_StringsClone(source) + sink(45, out) + } + { + source := newSource(46) + out := TaintStepTest_StringsCutleft(source) + sink(46, out) + } + { + source := newSource(47) + out := TaintStepTest_StringsCutright(source) + sink(47, out) + } + { + source := newSource(48) + out := TaintStepTest_StringsCutPrefix(source) + sink(48, out) + } + { + source := newSource(49) + out := TaintStepTest_StringsCutSuffix(source) + sink(49, out) + } + // { + // source := newSource(50) + // out := TaintStepTest_StringsFieldsFuncSeq(source) + // sink(50, out) + // } + // { + // source := newSource(51) + // out := TaintStepTest_StringsFieldsSeq(source) + // sink(51, out) + // } + // { + // source := newSource(52) + // out := TaintStepTest_StringsLines(source) + // sink(52, out) + // } + // { + // source := newSource(53) + // out := TaintStepTest_StringsSplitAfterSeq(source) + // sink(53, out) + // } + // { + // source := newSource(54) + // out := TaintStepTest_StringsSplitSeq(source) + // sink(54, out) + // } + { + source := newSource(55) + out := TaintStepTest_StringsBuilderWriteByte(source) + sink(55, out) + } + { + source := newSource(56) + out := TaintStepTest_StringsBuilderWriteRune(source) + sink(56, out) + } + { + source := newSource(57) + out := TaintStepTest_StringsReaderReadByte(source) + sink(57, out) + } + { + source := newSource(58) + out := TaintStepTest_StringsReaderReadRune(source) + sink(58, out) + } + { + source := newSource(59) + out := TaintStepTest_StringsReplacerReplaceReceiver(source) + sink(59, out) + } + { + source := newSource(60) + out := TaintStepTest_StringsReplacerWriteStringReceiver(source) + sink(60, out) + } } diff --git a/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/go.mod b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/go.mod index 1a8220297f27..50e9957cce14 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/go.mod +++ b/go/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/go.mod @@ -1,6 +1,6 @@ module example.com/m -go 1.26 +go 1.27 require ( golang.org/x/net v0.0.0-20201010224723-4f7140c49acb diff --git a/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected b/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected index 3e593f0c2029..cee1acc74d80 100644 --- a/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected +++ b/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected @@ -58,9 +58,11 @@ edges | reflectedxsstest.go:51:14:51:18 | selection of URL | reflectedxsstest.go:51:14:51:26 | call to Query | provenance | Src:MaD:10 MaD:17 | | reflectedxsstest.go:51:14:51:26 | call to Query | reflectedxsstest.go:54:11:54:21 | type conversion | provenance | | | tst.go:14:15:14:20 | selection of Form | tst.go:14:15:14:36 | call to Get | provenance | Src:MaD:6 MaD:18 | -| tst.go:14:15:14:36 | call to Get | tst.go:18:32:18:32 | a | provenance | | +| tst.go:14:15:14:36 | call to Get | tst.go:17:18:17:25 | username | provenance | | +| tst.go:17:9:17:57 | slice literal [array] | tst.go:18:32:18:32 | a [array] | provenance | | +| tst.go:17:18:17:25 | username | tst.go:17:9:17:57 | slice literal [array] | provenance | | | tst.go:18:19:18:38 | call to Join | tst.go:18:12:18:39 | type conversion | provenance | | -| tst.go:18:32:18:32 | a | tst.go:18:19:18:38 | call to Join | provenance | MaD:19 | +| tst.go:18:32:18:32 | a [array] | tst.go:18:19:18:38 | call to Join | provenance | MaD:19 | | tst.go:48:14:48:19 | selection of Form | tst.go:48:14:48:34 | call to Get | provenance | Src:MaD:6 MaD:18 | | tst.go:48:14:48:34 | call to Get | tst.go:53:12:53:26 | type conversion | provenance | | | websocketXss.go:31:11:31:14 | xnet [postupdate] | websocketXss.go:32:24:32:27 | xnet | provenance | Src:MaD:5 | @@ -88,7 +90,7 @@ models | 16 | Summary: mime/multipart; Reader; true; NextPart; ; ; Argument[receiver]; ReturnValue[0]; taint; manual | | 17 | Summary: net/url; URL; true; Query; ; ; Argument[receiver]; ReturnValue; taint; manual | | 18 | Summary: net/url; Values; true; Get; ; ; Argument[receiver]; ReturnValue; taint; manual | -| 19 | Summary: strings; ; false; Join; ; ; Argument[0..1]; ReturnValue; taint; manual | +| 19 | Summary: strings; ; false; Join; ; ; Argument[0].ArrayElement; ReturnValue; taint; manual | nodes | ReflectedXss.go:11:15:11:20 | selection of Form | semmle.label | selection of Form | | ReflectedXss.go:11:15:11:36 | call to Get | semmle.label | call to Get | @@ -137,9 +139,11 @@ nodes | reflectedxsstest.go:54:11:54:21 | type conversion | semmle.label | type conversion | | tst.go:14:15:14:20 | selection of Form | semmle.label | selection of Form | | tst.go:14:15:14:36 | call to Get | semmle.label | call to Get | +| tst.go:17:9:17:57 | slice literal [array] | semmle.label | slice literal [array] | +| tst.go:17:18:17:25 | username | semmle.label | username | | tst.go:18:12:18:39 | type conversion | semmle.label | type conversion | | tst.go:18:19:18:38 | call to Join | semmle.label | call to Join | -| tst.go:18:32:18:32 | a | semmle.label | a | +| tst.go:18:32:18:32 | a [array] | semmle.label | a [array] | | tst.go:48:14:48:19 | selection of Form | semmle.label | selection of Form | | tst.go:48:14:48:34 | call to Get | semmle.label | call to Get | | tst.go:53:12:53:26 | type conversion | semmle.label | type conversion |