mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-10-02 11:31:57 +00:00
fix(common): harden CORS, sort, raw-SQL WHERE and x-custom-sql-or
Add a `hardening` config section (RESOLVESPEC_HARDENING_*) so each
fix can be switched off to restore the previous behaviour:
- cors_strict_origins: only reflect origins listed in
cors.allowed_origins / server URLs, with credentials; `*` never
sends credentials; fix shared-slice append of expose headers.
- sort_strict: join aliases must match `alias.identifier` (empty alias
no longer matches everything); sort expressions reject dangerous
functions/catalogs; cql* columns must be identifier-safe.
- sql_strict: client raw-SQL fragments must have balanced parens and
quotes, no comments/`;`/`$$`, DML keywords, dangerous functions or
system catalogs; a rejected fragment now fails closed ("(1=0)")
instead of dropping the filter. Subqueries stay allowed unless
sql_block_subqueries is set.
- x-custom-sql-or is grouped together with the client's own
conditions (new optional WhereGrouper, implemented for bun and gorm)
so it can no longer OR past server-side filters.
This commit is contained in:
@@ -537,6 +537,19 @@ func (b *BunSelectQuery) WhereOr(query string, args ...interface{}) common.Selec
|
||||
return b
|
||||
}
|
||||
|
||||
// WhereGroup wraps the conditions added by fn in one parenthesised group ANDed with the rest.
|
||||
func (b *BunSelectQuery) WhereGroup(fn func(common.SelectQuery) common.SelectQuery) common.SelectQuery {
|
||||
b.query = b.query.WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
inner := *b
|
||||
inner.query = q
|
||||
if res, ok := fn(&inner).(*BunSelectQuery); ok {
|
||||
return res.query
|
||||
}
|
||||
return q
|
||||
})
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunSelectQuery) Join(query string, args ...interface{}) common.SelectQuery {
|
||||
// Extract optional prefix from args
|
||||
// If the last arg is a string that looks like a table prefix, use it
|
||||
|
||||
@@ -377,6 +377,16 @@ func (g *GormSelectQuery) WhereOr(query string, args ...interface{}) common.Sele
|
||||
return g
|
||||
}
|
||||
|
||||
// WhereGroup wraps the conditions added by fn in one parenthesised group ANDed with the rest.
|
||||
func (g *GormSelectQuery) WhereGroup(fn func(common.SelectQuery) common.SelectQuery) common.SelectQuery {
|
||||
inner := *g
|
||||
inner.db = g.db.Session(&gorm.Session{NewDB: true})
|
||||
if res, ok := fn(&inner).(*GormSelectQuery); ok {
|
||||
g.db = g.db.Where(res.db)
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormSelectQuery) Join(query string, args ...interface{}) common.SelectQuery {
|
||||
// Extract optional prefix from args
|
||||
// If the last arg is a string that looks like a table prefix, use it
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/uptrace/bun"
|
||||
"github.com/uptrace/bun/dialect/pgdialect"
|
||||
|
||||
"github.com/bitechdev/ResolveSpec/pkg/common"
|
||||
)
|
||||
|
||||
// The client's OR must widen only the client's own conditions, never the
|
||||
// server-side condition ANDed after it.
|
||||
func TestBunWhereGroupConfinesOr(t *testing.T) {
|
||||
db := bun.NewDB(&sql.DB{}, pgdialect.New())
|
||||
q := &BunSelectQuery{query: db.NewSelect().TableExpr("items"), db: db, driverName: "postgres"}
|
||||
|
||||
var sq common.SelectQuery = q.Where("a = 1")
|
||||
sq = sq.(common.WhereGrouper).WhereGroup(func(g common.SelectQuery) common.SelectQuery {
|
||||
return g.Where("b = 2").WhereOr("(c = 3)")
|
||||
})
|
||||
sq = sq.Where("tenant = 5")
|
||||
|
||||
got := sq.(*BunSelectQuery).query.String()
|
||||
want := `WHERE (a = 1) AND ((b = 2) OR ((c = 3))) AND (tenant = 5)`
|
||||
if !strings.Contains(got, want) {
|
||||
t.Fatalf("unexpected SQL:\n got: %s\nwant to contain: %s", got, want)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user