|
import ( |
|
"html" |
|
"reflect" |
|
"strings" |
|
|
|
"github.com/microcosm-cc/bluemonday" |
|
) |
|
|
|
var p = bluemonday.UGCPolicy() |
|
|
|
// StripHTMLTags strips the string field in `input`. The string field can be nested in other |
|
// data types, such as a map or a slice. The method will search through all the fields. |
|
func StripHTMLTags(input interface{}) { |
|
values := reflect.ValueOf(input) |
|
stripRecursive(values) |
|
} |
|
|
|
func stripRecursive(field reflect.Value) { |
|
switch field.Kind() { |
|
|
|
// String type, finally at the place we want to be, sanitize |
|
case reflect.String: |
|
if field.CanSet() { // Can't set for unexported type, etc. |
|
sanitized := sanitize(sanitize(field.Interface().(string))) |
|
field.SetString(sanitized) |
|
} |
|
|
|
// Pointer type, we check if it's nil. Dereference it and call recursion. |
|
// Interface type is similar. |
|
case reflect.Ptr, |
|
reflect.Interface: |
|
elem := field.Elem() |
|
if !elem.IsValid() { // nil pointer stop processing |
|
return |
|
} |
|
stripRecursive(elem) |
|
|
|
// For slice, loop each element and call recursion. |
|
case reflect.Slice: |
|
for j := 0; j < field.Len(); j++ { |
|
sj := field.Index(j) |
|
stripRecursive(sj) |
|
} |
|
|
|
// For struct, loop each field and call recursion. |
|
case reflect.Struct: |
|
for j := 0; j < field.NumField(); j++ { |
|
ff := field.Field(j) |
|
stripRecursive(ff) |
|
} |
|
|
|
// For map, loop each key and call recursion. Note that we have to construct a new interface |
|
// for a value in the map, or else the value cannot be able to set. |
|
case reflect.Map: |
|
for _, key := range field.MapKeys() { |
|
val := field.MapIndex(key) |
|
cloneVal := reflect.New(val.Type()).Elem() |
|
cloneVal.Set(val) |
|
stripRecursive(cloneVal) |
|
field.SetMapIndex(key, cloneVal) |
|
} |
|
} |
|
} |
|
|
|
func sanitize(input string) string { |
|
sanitized := p.Sanitize(input) |
|
sanitized = html.UnescapeString(sanitized) |
|
sanitized = strings.TrimSpace(sanitized) |
|
sanitized = strings.ReplaceAll(sanitized, "`", "") |
|
return sanitized |
|
} |