Skip to content

Commit

Permalink
fix all queryEventsSql
Browse files Browse the repository at this point in the history
  • Loading branch information
mattn authored and fiatjaf committed Jan 29, 2024
1 parent f5d23a3 commit 7d4015d
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 133 deletions.
58 changes: 16 additions & 42 deletions mysql/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ package mysql
import (
"context"
"database/sql"
"encoding/hex"
"fmt"
"strconv"
"strings"

"github.com/jmoiron/sqlx"
Expand Down Expand Up @@ -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)
Expand Down
67 changes: 18 additions & 49 deletions postgresql/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ package postgresql
import (
"context"
"database/sql"
"encoding/hex"
"fmt"
"strconv"
"strings"

"github.com/jmoiron/sqlx"
Expand Down Expand Up @@ -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)
Expand All @@ -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 {
Expand Down
58 changes: 16 additions & 42 deletions sqlite3/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ package sqlite3
import (
"context"
"database/sql"
"encoding/hex"
"fmt"
"strconv"
"strings"

"github.com/jmoiron/sqlx"
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 7d4015d

Please sign in to comment.