Better checking for api sort param to prevent sql injection

And moved filters out and cached object reflection
This commit is contained in:
Jamie Curnow
2023-07-24 11:49:08 +10:00
parent 9b32329f41
commit a0e17f9678
12 changed files with 312 additions and 223 deletions

View File

@ -1,66 +1,22 @@
package entity
import (
"reflect"
"regexp"
"strings"
"npm/internal/model"
"npm/internal/tags"
)
type filterMapValue struct {
Type string
Field string
}
// GetFilterMap returns the filter map
func GetFilterMap(m interface{}, includeBaseEntity bool) map[string]filterMapValue {
filterMap := getFilterMapForInterface(m)
func GetFilterMap(m interface{}, includeBaseEntity bool) map[string]model.FilterMapValue {
filterMap := tags.GetFilterMap(m)
if includeBaseEntity {
return mergeFilterMaps(getFilterMapForInterface(ModelBase{}), filterMap)
return mergeFilterMaps(tags.GetFilterMap(ModelBase{}), filterMap)
}
return filterMap
}
func getFilterMapForInterface(m interface{}) map[string]filterMapValue {
var filterMap = make(map[string]filterMapValue)
// TypeOf returns the reflection Type that represents the dynamic type of variable.
// If variable is a nil interface value, TypeOf returns nil.
t := reflect.TypeOf(m)
// Iterate over all available fields and read the tag value
for i := 0; i < t.NumField(); i++ {
// Get the field, returns https://golang.org/pkg/reflect/#StructField
field := t.Field(i)
// Get the field tag value
filterTag := field.Tag.Get("filter")
dbTag := field.Tag.Get("gorm")
if filterTag != "" && dbTag != "" && dbTag != "-" && filterTag != "-" {
// db can have many parts, we need to pull out the "column:value" part
dbField := field.Name
r := regexp.MustCompile(`(?:^|;)column:([^;|$]+)(?:$|;)`)
if matches := r.FindStringSubmatch(dbTag); len(matches) > 1 {
dbField = matches[1]
}
// Filter tag can be a 2 part thing: name,type
// ie: account_id,integer
// So we need to split and use the first part
parts := strings.Split(filterTag, ",")
if len(parts) > 1 {
filterMap[parts[0]] = filterMapValue{
Type: parts[1],
Field: dbField,
}
}
}
}
return filterMap
}
func mergeFilterMaps(m1 map[string]filterMapValue, m2 map[string]filterMapValue) map[string]filterMapValue {
merged := make(map[string]filterMapValue, 0)
func mergeFilterMaps(m1 map[string]model.FilterMapValue, m2 map[string]model.FilterMapValue) map[string]model.FilterMapValue {
merged := make(map[string]model.FilterMapValue, 0)
for k, v := range m1 {
merged[k] = v
}

View File

@ -26,7 +26,7 @@ type ListResponse struct {
func ListQueryBuilder(
pageInfo *model.PageInfo,
filters []model.Filter,
filterMap map[string]filterMapValue,
filterMap map[string]model.FilterMapValue,
) *gorm.DB {
scopes := make([]func(*gorm.DB) *gorm.DB, 0)
scopes = append(scopes, ScopeOffsetLimit(pageInfo))

View File

@ -36,7 +36,7 @@ func ScopeOrderBy(pageInfo *model.PageInfo, defaultSort model.Sort) func(db *gor
}
}
func ScopeFilters(filters []model.Filter, filterMap map[string]filterMapValue) func(db *gorm.DB) *gorm.DB {
func ScopeFilters(filters []model.Filter, filterMap map[string]model.FilterMapValue) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
like := database.GetCaseInsensitiveLike()
for _, f := range filters {