Trying to make a function that walks a struct recursively, and modifies any fields that are strings, based on on a certain tag.
Reflection is very tedious to work with. First time using it and having some troubles.
I'm getting a panic from one of my lines of code:
panic: reflect: Field of non-struct type
The panic comes from this line:
tf := vf.Type().Field(i)
I'm trying to get the type field so I can get a tag from it.
Here is the full function:
func Sanitize(s interface{}) error {
v := reflect.ValueOf(s)
// It's a pointer struct, convert to the value that it points to.
if v.Kind() == reflect.Ptr && !v.IsNil() {
v = v.Elem()
}
// Not a struct, return an error.
if v.Kind() != reflect.Struct {
return &InvalidSanitizerError{Type: reflect.TypeOf(s)}
}
for i := 0; i < v.NumField(); i++ {
vf := v.Field(i)
if vf.Kind() == reflect.Struct {
// Recurse.
err := Sanitize(v.Field(i).Interface())
if err != nil {
return err
}
// Move onto the next field.
continue
}
if vf.Kind() == reflect.String {
tf := vf.Type().Field(i) // <-- TROUBLE MAKER
// Get the field tag value
tag := tf.Tag.Get("sanitize")
// Skip if tag is not defined or ignored
if tag == "" || tag == "-" {
continue
}
shouldSanitize, err := strconv.ParseBool(tag)
if err != nil {
return err
}
if shouldSanitize && vf.CanSet() {
vf.SetString(policy.Sanitize(vf.String()))
}
}
}
return nil
}
This is an example of how the function should be used:
type User struct {
Name string `sanitize:"true"`
Bio *Bio
}
type Bio struct {
Text string `sanitize:"true"`
}
func main() {
user := &User{
Name: "Lansana<script>alert('rekt');</script>",
Bio: &Bio{
Text: "Hello world</script>alert('rekt');</script>",
},
}
if err := Sanitize(user); err != nil {
panic(err)
}
fmt.Println(user.Name) // Lansana
fmt.Println(user.Bio.Text) // Hello world
}
Any insight on the panic would be greatly appreciated.