- Reject identifiers and directions outside the caller allow-list - Emit ordinary ORDER BY without COLLATE so ICU pl-PL applies Co-authored-by: Cursor <cursoragent@cursor.com>
47 lines
1.2 KiB
Go
47 lines
1.2 KiB
Go
package lagoon
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// OrderBy appends a database-default ORDER BY for an allow-listed qualified
|
|
// column. Identifiers and directions are never taken from untrusted input:
|
|
// column must match allowed exactly, and dir must be asc or desc. No COLLATE
|
|
// is emitted; Postgres ICU pl-PL is the database default (CheckLocale).
|
|
func OrderBy(db *gorm.DB, column, dir string, allowed []string) (*gorm.DB, error) {
|
|
if db == nil {
|
|
return nil, fmt.Errorf("lagoon: gorm db is nil")
|
|
}
|
|
clause, err := orderClause(column, dir, allowed)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return db.Order(clause), nil
|
|
}
|
|
|
|
func orderClause(column, dir string, allowed []string) (string, error) {
|
|
if !allowListed(column, allowed) {
|
|
return "", fmt.Errorf("lagoon: order column %q is not allow-listed", column)
|
|
}
|
|
switch strings.ToLower(strings.TrimSpace(dir)) {
|
|
case "asc":
|
|
return column + " ASC", nil
|
|
case "desc":
|
|
return column + " DESC", nil
|
|
default:
|
|
return "", fmt.Errorf("lagoon: order direction %q is not allow-listed", dir)
|
|
}
|
|
}
|
|
|
|
func allowListed(column string, allowed []string) bool {
|
|
for _, a := range allowed {
|
|
if a == column {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|