func redact()

in x-pack/filebeat/input/internal/private/private.go [119:295]


func redact(v reflect.Value, reps replacers, tag string, global []string, depth int, seen map[any]int) reflect.Value {
	switch v.Kind() {
	case reflect.Pointer:
		if v.IsNil() {
			return v
		}
		if depth > tooDeep {
			ident := v.Interface()
			if last, ok := seen[ident]; ok && last < depth {
				panic(cycle{v.Type()})
			}
			seen[ident] = depth
			defer delete(seen, ident)
		}
		return redact(v.Elem(), reps, tag, global, depth+1, seen).Addr()
	case reflect.Interface:
		if v.IsNil() {
			return v
		}
		return redact(v.Elem(), reps, tag, global, depth+1, seen)
	case reflect.Array:
		if v.Len() == 0 {
			return v
		}
		r := reflect.New(v.Type()).Elem()
		for i := 0; i < v.Len(); i++ {
			r.Index(i).Set(redact(v.Index(i), reps, tag, global, depth+1, seen))
		}
		return r
	case reflect.Slice:
		if v.Len() == 0 {
			return v
		}
		if depth > tooDeep {
			ident := struct {
				data unsafe.Pointer
				len  int
			}{
				v.UnsafePointer(),
				v.Len(),
			}
			if last, ok := seen[ident]; ok && last < depth {
				panic(cycle{v.Type()})
			}
			seen[ident] = depth
			defer delete(seen, ident)
		}
		r := reflect.MakeSlice(v.Type(), v.Len(), v.Cap())
		for i := 0; i < v.Len(); i++ {
			r.Index(i).Set(redact(v.Index(i), reps, tag, global, depth+1, seen))
		}
		return r
	case reflect.Map:
		if v.IsNil() {
			return v
		}
		if depth > tooDeep {
			ident := v.UnsafePointer()
			if last, ok := seen[ident]; ok && last < depth {
				panic(cycle{v.Type()})
			}
			seen[ident] = depth
			defer delete(seen, ident)
		}
		private := nextStep(global)
		if privateKey.CanConvert(v.Type().Key()) {
			p := v.MapIndex(privateKey.Convert(v.Type().Key()))
			if p.IsValid() && p.CanInterface() {
				switch p := p.Interface().(type) {
				case string:
					private = append(private, p)
				case []string:
					private = append(private, p...)
				case []any:
					for _, s := range p {
						private = append(private, fmt.Sprint(s))
					}
				}
			}
		}
		r := reflect.MakeMap(v.Type())
		it := v.MapRange()
		for it.Next() {
			name := it.Key().String()
			if slices.Contains(private, name) {
				v := replaceNestedWithin(it.Value(), reps)
				if v.IsValid() {
					r.SetMapIndex(it.Key(), v)
				}
				continue
			}
			r.SetMapIndex(it.Key(), redact(it.Value(), reps, tag, nextPath(name, global), depth+1, seen))
		}
		return r
	case reflect.Struct:
		private := nextStep(global)
		rt := v.Type()
		names := make([]string, rt.NumField())
		for i := range names {
			f := rt.Field(i)

			// Look for `private:` tags.
			p, ok := f.Tag.Lookup("private")
			if ok {
				if p != "" {
					private = append(private, strings.Split(p, ",")...)
				} else {
					if tag == "" {
						names[i] = f.Name
						private = append(private, f.Name)
					} else {
						p = f.Tag.Get(tag)
						if p != "" {
							name, _, _ := strings.Cut(p, ",")
							names[i] = name
							private = append(private, name)
						}
					}
				}
			}

			// Look after Private fields if we are not using a tag.
			if tag == "" {
				names[i] = f.Name
				if f.Name == "Private" {
					switch p := v.Field(i).Interface().(type) {
					case string:
						private = append(private, p)
					case []string:
						private = append(private, p...)
					}
				}
				continue
			}

			// If we are using a tag, look for `tag:"<private>"`
			// falling back to fields named Private if no tag is
			// present.
			p = f.Tag.Get(tag)
			var name string
			if p == "" {
				name = f.Name
			} else {
				name, _, _ = strings.Cut(p, ",")
			}
			names[i] = name
			if name == "private" {
				switch p := v.Field(i).Interface().(type) {
				case string:
					private = append(private, p)
				case []string:
					private = append(private, p...)
				}
			}
		}

		r := reflect.New(v.Type()).Elem()
		for i := 0; i < v.NumField(); i++ {
			f := v.Field(i)
			if f.IsZero() || !rt.Field(i).IsExported() {
				continue
			}
			if slices.Contains(private, names[i]) {
				v := replaceNestedWithin(f, reps)
				if v.IsValid() {
					r.Field(i).Set(v)
				}
				continue
			}
			if r.Field(i).CanSet() {
				r.Field(i).Set(redact(f, reps, tag, nextPath(names[i], global), depth+1, seen))
			}
		}
		return r
	}
	return v
}