2017-10-02 10:32:02 +02:00
package anonymize
import (
"encoding/json"
"fmt"
"reflect"
"regexp"
"github.com/mitchellh/copystructure"
2020-12-03 15:52:05 +01:00
"github.com/traefik/traefik/v2/pkg/config/dynamic"
"github.com/traefik/traefik/v2/pkg/tls"
2019-08-03 03:58:23 +02:00
"mvdan.cc/xurls/v2"
2017-10-02 10:32:02 +02:00
)
const (
maskShort = "xxxx"
maskLarge = maskShort + maskShort + maskShort + maskShort + maskShort + maskShort + maskShort + maskShort
)
2020-05-11 12:06:07 +02:00
// Do sends configuration.
2017-10-02 10:32:02 +02:00
func Do ( baseConfig interface { } , indent bool ) ( string , error ) {
anomConfig , err := copystructure . Copy ( baseConfig )
if err != nil {
return "" , err
}
val := reflect . ValueOf ( anomConfig )
err = doOnStruct ( val )
if err != nil {
return "" , err
}
configJSON , err := marshal ( anomConfig , indent )
if err != nil {
return "" , err
}
return doOnJSON ( string ( configJSON ) ) , nil
}
func doOnJSON ( input string ) string {
2021-03-04 09:02:03 +01:00
mailExp := regexp . MustCompile ( ` \w[-.\w]*\w@\w[-.\w]*\w\.\w { 2,3}" ` )
2019-08-03 03:58:23 +02:00
return xurls . Relaxed ( ) . ReplaceAllString ( mailExp . ReplaceAllString ( input , maskLarge + "\"" ) , maskLarge )
2017-10-02 10:32:02 +02:00
}
func doOnStruct ( field reflect . Value ) error {
2020-12-03 15:52:05 +01:00
if field . Type ( ) . AssignableTo ( reflect . TypeOf ( dynamic . PluginConf { } ) ) {
resetPlugin ( field )
return nil
}
2017-10-02 10:32:02 +02:00
switch field . Kind ( ) {
case reflect . Ptr :
if ! field . IsNil ( ) {
if err := doOnStruct ( field . Elem ( ) ) ; err != nil {
return err
}
}
case reflect . Struct :
for i := 0 ; i < field . NumField ( ) ; i ++ {
fld := field . Field ( i )
stField := field . Type ( ) . Field ( i )
if ! isExported ( stField ) {
continue
}
2020-12-03 15:52:05 +01:00
2017-10-02 10:32:02 +02:00
if stField . Tag . Get ( "export" ) == "true" {
2020-12-03 15:52:05 +01:00
// A struct field cannot be set it must be filled as pointer.
if fld . Kind ( ) == reflect . Struct {
fldPtr := reflect . New ( fld . Type ( ) )
fldPtr . Elem ( ) . Set ( fld )
if err := doOnStruct ( fldPtr ) ; err != nil {
return err
}
fld . Set ( fldPtr . Elem ( ) )
continue
2017-10-02 10:32:02 +02:00
}
2020-12-03 15:52:05 +01:00
if err := doOnStruct ( fld ) ; err != nil {
2017-10-02 10:32:02 +02:00
return err
}
2020-12-03 15:52:05 +01:00
} else if err := reset ( fld , stField . Name ) ; err != nil {
return err
2017-10-02 10:32:02 +02:00
}
}
case reflect . Map :
for _ , key := range field . MapKeys ( ) {
2020-12-03 15:52:05 +01:00
val := field . MapIndex ( key )
// A struct value cannot be set it must be filled as pointer.
if val . Kind ( ) == reflect . Struct {
valPtr := reflect . New ( val . Type ( ) )
valPtr . Elem ( ) . Set ( val )
if err := doOnStruct ( valPtr ) ; err != nil {
return err
}
field . SetMapIndex ( key , valPtr . Elem ( ) )
continue
}
if err := doOnStruct ( val ) ; err != nil {
2017-10-02 10:32:02 +02:00
return err
}
}
case reflect . Slice :
for j := 0 ; j < field . Len ( ) ; j ++ {
if err := doOnStruct ( field . Index ( j ) ) ; err != nil {
return err
}
}
}
2020-10-30 12:44:05 +01:00
2017-10-02 10:32:02 +02:00
return nil
}
func reset ( field reflect . Value , name string ) error {
if ! field . CanSet ( ) {
return fmt . Errorf ( "cannot reset field %s" , name )
}
switch field . Kind ( ) {
case reflect . Ptr :
if ! field . IsNil ( ) {
field . Set ( reflect . Zero ( field . Type ( ) ) )
}
case reflect . Struct :
if field . IsValid ( ) {
field . Set ( reflect . Zero ( field . Type ( ) ) )
}
case reflect . String :
if field . String ( ) != "" {
2020-12-03 15:52:05 +01:00
if field . Type ( ) . AssignableTo ( reflect . TypeOf ( tls . FileOrContent ( "" ) ) ) {
field . Set ( reflect . ValueOf ( tls . FileOrContent ( maskShort ) ) )
} else {
field . Set ( reflect . ValueOf ( maskShort ) )
}
2017-10-02 10:32:02 +02:00
}
case reflect . Map :
if field . Len ( ) > 0 {
field . Set ( reflect . MakeMap ( field . Type ( ) ) )
}
case reflect . Slice :
if field . Len ( ) > 0 {
2020-10-30 12:44:05 +01:00
switch field . Type ( ) . Elem ( ) . Kind ( ) {
case reflect . String :
slice := reflect . MakeSlice ( field . Type ( ) , field . Len ( ) , field . Len ( ) )
for j := 0 ; j < field . Len ( ) ; j ++ {
slice . Index ( j ) . SetString ( maskShort )
}
field . Set ( slice )
default :
field . Set ( reflect . MakeSlice ( field . Type ( ) , 0 , 0 ) )
}
2017-10-02 10:32:02 +02:00
}
case reflect . Interface :
if ! field . IsNil ( ) {
return reset ( field . Elem ( ) , "" )
}
default :
// Primitive type
field . Set ( reflect . Zero ( field . Type ( ) ) )
}
return nil
}
2020-12-03 15:52:05 +01:00
// resetPlugin resets the plugin configuration so it keep the plugin name but not its configuration.
func resetPlugin ( field reflect . Value ) {
for _ , key := range field . MapKeys ( ) {
field . SetMapIndex ( key , reflect . ValueOf ( struct { } { } ) )
}
}
2020-05-11 12:06:07 +02:00
// isExported return true is a struct field is exported, else false.
2017-10-02 10:32:02 +02:00
func isExported ( f reflect . StructField ) bool {
if f . PkgPath != "" && ! f . Anonymous {
return false
}
return true
}
func marshal ( anomConfig interface { } , indent bool ) ( [ ] byte , error ) {
if indent {
2020-10-30 12:44:05 +01:00
return json . MarshalIndent ( anomConfig , "" , " " )
2017-10-02 10:32:02 +02:00
}
return json . Marshal ( anomConfig )
}