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:
@@ -17,6 +17,7 @@ type Config struct {
|
||||
EventBroker EventBrokerConfig `mapstructure:"event_broker"`
|
||||
DBManager DBManagerConfig `mapstructure:"dbmanager"`
|
||||
DBTrace DBTraceConfig `mapstructure:"db_trace"`
|
||||
Hardening HardeningConfig `mapstructure:"hardening"`
|
||||
Paths PathsConfig `mapstructure:"paths"`
|
||||
Extensions map[string]interface{} `mapstructure:"extensions"`
|
||||
}
|
||||
@@ -143,6 +144,25 @@ type CORSConfig struct {
|
||||
MaxAge int `mapstructure:"max_age"`
|
||||
}
|
||||
|
||||
// HardeningConfig toggles security hardening that may reject requests which
|
||||
// older clients relied on. All switches default to true; set one to false to
|
||||
// restore the previous (permissive) behaviour.
|
||||
// Env: RESOLVESPEC_HARDENING_CORS_STRICT_ORIGINS, _SORT_STRICT, _SQL_STRICT, _SQL_BLOCK_SUBQUERIES.
|
||||
type HardeningConfig struct {
|
||||
// CORSStrictOrigins only reflects origins listed in cors.allowed_origins (and the
|
||||
// server URLs); credentials are never sent for unlisted or wildcard origins.
|
||||
CORSStrictOrigins bool `mapstructure:"cors_strict_origins"`
|
||||
// SortStrict stops empty/substring join aliases from admitting arbitrary sort strings.
|
||||
SortStrict bool `mapstructure:"sort_strict"`
|
||||
// SQLStrict hardens client raw-SQL fragments (x-custom-sql-*, preload where, cursor):
|
||||
// balanced parentheses, no subqueries/functions/comments, and rejection instead of
|
||||
// silently dropping the filter.
|
||||
SQLStrict bool `mapstructure:"sql_strict"`
|
||||
// SQLBlockSubqueries additionally rejects subqueries (select/union/with) in client
|
||||
// WHERE fragments (not custom joins). Off by default: existing clients use them.
|
||||
SQLBlockSubqueries bool `mapstructure:"sql_block_subqueries"`
|
||||
}
|
||||
|
||||
// DBTraceConfig controls database usage logging (off by default).
|
||||
// Env: RESOLVESPEC_DB_TRACE_ENABLED, _MIN_CALLS, _MIN_DURATION, _POOL_LOG.
|
||||
type DBTraceConfig struct {
|
||||
|
||||
@@ -168,6 +168,7 @@ func (m *Manager) SetConfig(cfg *Config) error {
|
||||
m.v.Set("event_broker", cfg.EventBroker)
|
||||
m.v.Set("dbmanager", cfg.DBManager)
|
||||
m.v.Set("db_trace", cfg.DBTrace)
|
||||
m.v.Set("hardening", cfg.Hardening)
|
||||
m.v.Set("paths", cfg.Paths)
|
||||
m.v.Set("extensions", cfg.Extensions)
|
||||
|
||||
@@ -279,6 +280,12 @@ func setDefaults(v *viper.Viper) {
|
||||
v.SetDefault("cors.allowed_headers", []string{"*"})
|
||||
v.SetDefault("cors.max_age", 3600)
|
||||
|
||||
// Security hardening defaults (on)
|
||||
v.SetDefault("hardening.cors_strict_origins", true)
|
||||
v.SetDefault("hardening.sort_strict", true)
|
||||
v.SetDefault("hardening.sql_strict", true)
|
||||
v.SetDefault("hardening.sql_block_subqueries", false)
|
||||
|
||||
// Database defaults
|
||||
v.SetDefault("database.url", "")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user