Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SEC-2706 #958

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 31 additions & 21 deletions bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"strings"
"sync"

"github.com/google/go-safeweb/safesql"
"github.com/google/go-safeweb/safesql/uncheckedconversions"
"github.com/jmoiron/sqlx/reflectx"
)

Expand Down Expand Up @@ -57,19 +59,23 @@ func BindDriver(driverName string, bindType int) {
// losing much speed, and should be to avoid confusion.

// Rebind a query from the default bindtype (QUESTION) to the target bindtype.
func Rebind(bindType int, query string) string {
func Rebind(bindType int, query safesql.TrustedSQLString) safesql.TrustedSQLString {
switch bindType {
case QUESTION, UNKNOWN:
return query
}

// Work with the raw string and convert back via unchecked conversion
// Make sure that no untrusted data is making its way into the string in the meantime!
rawQuery := query.String()

// Add space enough for 10 params before we have to allocate
rqb := make([]byte, 0, len(query)+10)
rqb := make([]byte, 0, len(rawQuery)+10)

var i, j int

for i = strings.Index(query, "?"); i != -1; i = strings.Index(query, "?") {
rqb = append(rqb, query[:i]...)
for i = strings.Index(rawQuery, "?"); i != -1; i = strings.Index(rawQuery, "?") {
rqb = append(rqb, rawQuery[:i]...)

switch bindType {
case DOLLAR:
Expand All @@ -83,25 +89,28 @@ func Rebind(bindType int, query string) string {
j++
rqb = strconv.AppendInt(rqb, int64(j), 10)

query = query[i+1:]
rawQuery = rawQuery[i+1:]
}

return string(append(rqb, query...))
rawQuery = string(append(rqb, rawQuery...))

return uncheckedconversions.TrustedSQLStringFromStringKnownToSatisfyTypeContract(rawQuery)
}

// Experimental implementation of Rebind which uses a bytes.Buffer. The code is
// much simpler and should be more resistant to odd unicode, but it is twice as
// slow. Kept here for benchmarking purposes and to possibly replace Rebind if
// problems arise with its somewhat naive handling of unicode.
func rebindBuff(bindType int, query string) string {
func rebindBuff(bindType int, query safesql.TrustedSQLString) safesql.TrustedSQLString {
if bindType != DOLLAR {
return query
}

b := make([]byte, 0, len(query))
rawQuery := query.String()
b := make([]byte, 0, len(rawQuery))
rqb := bytes.NewBuffer(b)
j := 1
for _, r := range query {
for _, r := range rawQuery {
if r == '?' {
rqb.WriteRune('$')
rqb.WriteString(strconv.Itoa(j))
Expand All @@ -111,7 +120,7 @@ func rebindBuff(bindType int, query string) string {
}
}

return rqb.String()
return uncheckedconversions.TrustedSQLStringFromStringKnownToSatisfyTypeContract(rqb.String())
}

func asSliceForIn(i interface{}) (v reflect.Value, ok bool) {
Expand Down Expand Up @@ -139,7 +148,7 @@ func asSliceForIn(i interface{}) (v reflect.Value, ok bool) {
// In expands slice values in args, returning the modified query string
// and a new arg list that can be executed by a database. The `query` should
// use the `?` bindVar. The return value uses the `?` bindVar.
func In(query string, args ...interface{}) (string, []interface{}, error) {
func In(query safesql.TrustedSQLString, args ...interface{}) (safesql.TrustedSQLString, []interface{}, error) {
// argMeta stores reflect.Value and length for slices and
// the value itself for non-slice arguments
type argMeta struct {
Expand All @@ -165,7 +174,7 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
var err error
arg, err = a.Value()
if err != nil {
return "", nil, err
return safesql.New(""), nil, err
}
}

Expand All @@ -177,7 +186,7 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
flatArgsCount += meta[i].length

if meta[i].length == 0 {
return "", nil, errors.New("empty slice passed to 'in' query")
return safesql.New(""), nil, errors.New("empty slice passed to 'in' query")
}
} else {
meta[i].i = arg
Expand All @@ -194,17 +203,18 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
newArgs := make([]interface{}, 0, flatArgsCount)

var buf strings.Builder
buf.Grow(len(query) + len(", ?")*flatArgsCount)
rawQuery := query.String()
buf.Grow(len(rawQuery) + len(", ?")*flatArgsCount)

var arg, offset int

for i := strings.IndexByte(query[offset:], '?'); i != -1; i = strings.IndexByte(query[offset:], '?') {
for i := strings.IndexByte(rawQuery[offset:], '?'); i != -1; i = strings.IndexByte(rawQuery[offset:], '?') {
if arg >= len(meta) {
// if an argument wasn't passed, lets return an error; this is
// not actually how database/sql Exec/Query works, but since we are
// creating an argument list programmatically, we want to be able
// to catch these programmer errors earlier.
return "", nil, errors.New("number of bindVars exceeds arguments")
return safesql.New(""), nil, errors.New("number of bindVars exceeds arguments")
}

argMeta := meta[arg]
Expand All @@ -220,7 +230,7 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
}

// write everything up to and including our ? character
buf.WriteString(query[:offset+i+1])
buf.WriteString(rawQuery[:offset+i+1])

for si := 1; si < argMeta.length; si++ {
buf.WriteString(", ?")
Expand All @@ -230,17 +240,17 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {

// slice the query and reset the offset. this avoids some bookkeeping for
// the write after the loop
query = query[offset+i+1:]
rawQuery = rawQuery[offset+i+1:]
offset = 0
}

buf.WriteString(query)
buf.WriteString(rawQuery)

if arg < len(meta) {
return "", nil, errors.New("number of bindVars less than number arguments")
return safesql.New(""), nil, errors.New("number of bindVars less than number arguments")
}

return buf.String(), newArgs, nil
return uncheckedconversions.TrustedSQLStringFromStringKnownToSatisfyTypeContract(buf.String()), newArgs, nil
}

func appendReflectSlice(args []interface{}, v reflect.Value, vlen int) []interface{} {
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
module github.com/jmoiron/sqlx
module github.com/kasluthra-sec/sqlx

go 1.10

require (
github.com/go-sql-driver/mysql v1.8.1
github.com/google/go-safeweb v0.0.0-20240727104708-c2d1215a6a24
github.com/lib/pq v1.10.9
github.com/mattn/go-sqlite3 v1.14.22
)
51 changes: 51 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,58 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-safeweb v0.0.0-20240727104708-c2d1215a6a24 h1:84uaHf8KLrWKVuJOfQfGZMMGdC8ujrPDDeekLGxkTss=
github.com/google/go-safeweb v0.0.0-20240727104708-c2d1215a6a24/go.mod h1:ukNyX9TdScmbBtYBtWwaU+n7MtodX/Wr6rBKBADTvGQ=
github.com/google/safehtml v0.0.2/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Loading