2017-10-02 10:32:02 +02:00
package anonymize
import (
"encoding/json"
"fmt"
"reflect"
"regexp"
"github.com/mitchellh/copystructure"
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 {
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 {
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
}
if stField . Tag . Get ( "export" ) == "true" {
if err := doOnStruct ( fld ) ; err != nil {
return err
}
} else {
if err := reset ( fld , stField . Name ) ; err != nil {
return err
}
}
}
case reflect . Map :
for _ , key := range field . MapKeys ( ) {
if err := doOnStruct ( field . MapIndex ( key ) ) ; err != nil {
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 ( ) != "" {
field . Set ( reflect . ValueOf ( maskShort ) )
}
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-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 )
}