diff --git a/mysql/query.go b/mysql/query.go index b2f3a30..dd10674 100644 --- a/mysql/query.go +++ b/mysql/query.go @@ -3,9 +3,7 @@ package mysql import ( "context" "database/sql" - "encoding/hex" "fmt" - "strconv" "strings" "github.com/jmoiron/sqlx" @@ -59,72 +57,48 @@ func (b MySQLBackend) CountEvents(ctx context.Context, filter nostr.Filter) (int return count, nil } +func makePlaceHolders(n int) string { + return strings.TrimRight(strings.Repeat("?,", n), ",") +} + func (b MySQLBackend) queryEventsSql(filter nostr.Filter, doCount bool) (string, []any, error) { var conditions []string var params []any - if filter.IDs != nil { + if len(filter.IDs) > 0 { if len(filter.IDs) > b.QueryIDsLimit { // too many ids, fail everything return "", nil, nil } - likeids := make([]string, 0, len(filter.IDs)) - for _, id := range filter.IDs { - // to prevent sql attack here we will check if - // these ids are valid 32byte hex - parsed, err := hex.DecodeString(id) - if err != nil || len(parsed) != 32 { - continue - } - likeids = append(likeids, fmt.Sprintf("id LIKE '%x%%'", parsed)) - } - if len(likeids) == 0 { - // ids being [] mean you won't get anything - return "", nil, nil + for _, v := range filter.IDs { + params = append(params, v) } - conditions = append(conditions, "("+strings.Join(likeids, " OR ")+")") + conditions = append(conditions, ` id IN (`+makePlaceHolders(len(filter.IDs))+`)`) } - if filter.Authors != nil { + if len(filter.Authors) > 0 { if len(filter.Authors) > b.QueryAuthorsLimit { // too many authors, fail everything return "", nil, nil } - likekeys := make([]string, 0, len(filter.Authors)) - for _, key := range filter.Authors { - // to prevent sql attack here we will check if - // these keys are valid 32byte hex - parsed, err := hex.DecodeString(key) - if err != nil || len(parsed) != 32 { - continue - } - likekeys = append(likekeys, fmt.Sprintf("pubkey LIKE '%x%%'", parsed)) - } - if len(likekeys) == 0 { - // authors being [] mean you won't get anything - return "", nil, nil + for _, v := range filter.Authors { + params = append(params, v) } - conditions = append(conditions, "("+strings.Join(likekeys, " OR ")+")") + conditions = append(conditions, ` pubkey IN (`+makePlaceHolders(len(filter.IDs))+`)`) } - if filter.Kinds != nil { + if len(filter.Kinds) > 0 { if len(filter.Kinds) > b.QueryKindsLimit { // too many kinds, fail everything return "", nil, nil } - if len(filter.Kinds) == 0 { - // kinds being [] mean you won't get anything - return "", nil, nil - } - // no sql injection issues since these are ints - inkinds := make([]string, len(filter.Kinds)) - for i, kind := range filter.Kinds { - inkinds[i] = strconv.Itoa(kind) + for _, v := range filter.Kinds { + params = append(params, v) } - conditions = append(conditions, `kind IN (`+strings.Join(inkinds, ",")+`)`) + conditions = append(conditions, `kind IN (`+makePlaceHolders(len(filter.Kinds))+`)`) } tagQuery := make([]string, 0, 1) diff --git a/postgresql/query.go b/postgresql/query.go index 7eb9124..fa66a40 100644 --- a/postgresql/query.go +++ b/postgresql/query.go @@ -3,9 +3,7 @@ package postgresql import ( "context" "database/sql" - "encoding/hex" "fmt" - "strconv" "strings" "github.com/jmoiron/sqlx" @@ -56,72 +54,48 @@ func (b PostgresBackend) CountEvents(ctx context.Context, filter nostr.Filter) ( return count, nil } +func makePlaceHolders(n int) string { + return strings.TrimRight(strings.Repeat("?,", n), ",") +} + func (b PostgresBackend) queryEventsSql(filter nostr.Filter, doCount bool) (string, []any, error) { var conditions []string var params []any - if filter.IDs != nil { + if len(filter.IDs) > 0 { if len(filter.IDs) > b.QueryIDsLimit { // too many ids, fail everything return "", nil, nil } - likeids := make([]string, 0, len(filter.IDs)) - for _, id := range filter.IDs { - // to prevent sql attack here we will check if - // these ids are valid 32byte hex - parsed, err := hex.DecodeString(id) - if err != nil || len(parsed) != 32 { - continue - } - likeids = append(likeids, fmt.Sprintf("id LIKE '%x%%'", parsed)) - } - if len(likeids) == 0 { - // ids being [] mean you won't get anything - return "", nil, nil + for _, v := range filter.IDs { + params = append(params, v) } - conditions = append(conditions, "("+strings.Join(likeids, " OR ")+")") + conditions = append(conditions, ` id IN (`+makePlaceHolders(len(filter.IDs))+`)`) } - if filter.Authors != nil { + if len(filter.Authors) > 0 { if len(filter.Authors) > b.QueryAuthorsLimit { // too many authors, fail everything return "", nil, nil } - likekeys := make([]string, 0, len(filter.Authors)) - for _, key := range filter.Authors { - // to prevent sql attack here we will check if - // these keys are valid 32byte hex - parsed, err := hex.DecodeString(key) - if err != nil || len(parsed) != 32 { - continue - } - likekeys = append(likekeys, fmt.Sprintf("pubkey LIKE '%x%%'", parsed)) - } - if len(likekeys) == 0 { - // authors being [] mean you won't get anything - return "", nil, nil + for _, v := range filter.Authors { + params = append(params, v) } - conditions = append(conditions, "("+strings.Join(likekeys, " OR ")+")") + conditions = append(conditions, ` pubkey IN (`+makePlaceHolders(len(filter.IDs))+`)`) } - if filter.Kinds != nil { + if len(filter.Kinds) > 0 { if len(filter.Kinds) > b.QueryKindsLimit { // too many kinds, fail everything return "", nil, nil } - if len(filter.Kinds) == 0 { - // kinds being [] mean you won't get anything - return "", nil, nil - } - // no sql injection issues since these are ints - inkinds := make([]string, len(filter.Kinds)) - for i, kind := range filter.Kinds { - inkinds[i] = strconv.Itoa(kind) + for _, v := range filter.Kinds { + params = append(params, v) } - conditions = append(conditions, `kind IN (`+strings.Join(inkinds, ",")+`)`) + conditions = append(conditions, `kind IN (`+makePlaceHolders(len(filter.Kinds))+`)`) } tagQuery := make([]string, 0, 1) @@ -141,16 +115,11 @@ func (b PostgresBackend) queryEventsSql(filter nostr.Filter, doCount bool) (stri } if len(tagQuery) > 0 { - arrayBuild := make([]string, len(tagQuery)) - for i, tagValue := range tagQuery { - arrayBuild[i] = "?" + for _, tagValue := range tagQuery { params = append(params, tagValue) } - // we use a very bad implementation in which we only check the tag values and - // ignore the tag names - conditions = append(conditions, - "tagvalues && ARRAY["+strings.Join(arrayBuild, ",")+"]") + conditions = append(conditions, "tagvalues && ARRAY["+makePlaceHolders(len(tagQuery))+"]") } if filter.Since != nil { diff --git a/sqlite3/query.go b/sqlite3/query.go index e775f7d..133c9cd 100644 --- a/sqlite3/query.go +++ b/sqlite3/query.go @@ -3,9 +3,7 @@ package sqlite3 import ( "context" "database/sql" - "encoding/hex" "fmt" - "strconv" "strings" "github.com/jmoiron/sqlx" @@ -56,72 +54,48 @@ func (b SQLite3Backend) CountEvents(ctx context.Context, filter nostr.Filter) (i return count, nil } +func makePlaceHolders(n int) string { + return strings.TrimRight(strings.Repeat("?,", n), ",") +} + func (b SQLite3Backend) queryEventsSql(filter nostr.Filter, doCount bool) (string, []any, error) { var conditions []string var params []any - if filter.IDs != nil { + if len(filter.IDs) > 0 { if len(filter.IDs) > 500 { // too many ids, fail everything return "", nil, nil } - likeids := make([]string, 0, len(filter.IDs)) - for _, id := range filter.IDs { - // to prevent sql attack here we will check if - // these ids are valid 32byte hex - parsed, err := hex.DecodeString(id) - if err != nil || len(parsed) != 32 { - continue - } - likeids = append(likeids, fmt.Sprintf("id LIKE '%x%%'", parsed)) - } - if len(likeids) == 0 { - // ids being [] mean you won't get anything - return "", nil, nil + for _, v := range filter.IDs { + params = append(params, v) } - conditions = append(conditions, "("+strings.Join(likeids, " OR ")+")") + conditions = append(conditions, ` id IN (`+makePlaceHolders(len(filter.IDs))+`)`) } - if filter.Authors != nil { + if len(filter.Authors) > 0 { if len(filter.Authors) > b.QueryAuthorsLimit { // too many authors, fail everything return "", nil, nil } - likekeys := make([]string, 0, len(filter.Authors)) - for _, key := range filter.Authors { - // to prevent sql attack here we will check if - // these keys are valid 32byte hex - parsed, err := hex.DecodeString(key) - if err != nil || len(parsed) != 32 { - continue - } - likekeys = append(likekeys, fmt.Sprintf("pubkey LIKE '%x%%'", parsed)) - } - if len(likekeys) == 0 { - // authors being [] mean you won't get anything - return "", nil, nil + for _, v := range filter.Authors { + params = append(params, v) } - conditions = append(conditions, "("+strings.Join(likekeys, " OR ")+")") + conditions = append(conditions, ` pubkey IN (`+makePlaceHolders(len(filter.Authors))+`)`) } - if filter.Kinds != nil { + if len(filter.Kinds) > 0 { if len(filter.Kinds) > 10 { // too many kinds, fail everything return "", nil, nil } - if len(filter.Kinds) == 0 { - // kinds being [] mean you won't get anything - return "", nil, nil - } - // no sql injection issues since these are ints - inkinds := make([]string, len(filter.Kinds)) - for i, kind := range filter.Kinds { - inkinds[i] = strconv.Itoa(kind) + for _, v := range filter.Kinds { + params = append(params, v) } - conditions = append(conditions, `kind IN (`+strings.Join(inkinds, ",")+`)`) + conditions = append(conditions, `kind IN (`+makePlaceHolders(len(filter.Kinds))+`)`) } tagQuery := make([]string, 0, 1)