|  | 
|  | 1 | +package definition | 
|  | 2 | + | 
|  | 3 | +import ( | 
|  | 4 | +	"unsafe" | 
|  | 5 | + | 
|  | 6 | +	jsoniter "github.com/json-iterator/go" | 
|  | 7 | +	"github.com/modern-go/reflect2" | 
|  | 8 | +	amcfg "github.com/prometheus/alertmanager/config" | 
|  | 9 | +	commoncfg "github.com/prometheus/common/config" | 
|  | 10 | +) | 
|  | 11 | + | 
|  | 12 | +// secretEncoder encodes Secret to plain text JSON, | 
|  | 13 | +// avoding the default masking behavior of the structure. | 
|  | 14 | +type secretEncoder struct { | 
|  | 15 | +	isPtr bool | 
|  | 16 | +} | 
|  | 17 | + | 
|  | 18 | +func (encoder *secretEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) { | 
|  | 19 | +	if encoder.isPtr { | 
|  | 20 | +		p := *(**string)(ptr) | 
|  | 21 | +		if p == nil { | 
|  | 22 | +			stream.WriteNil() | 
|  | 23 | +			return | 
|  | 24 | +		} | 
|  | 25 | +		stream.WriteString(*p) | 
|  | 26 | +	} else { | 
|  | 27 | +		v := *(*string)(ptr) | 
|  | 28 | +		stream.WriteString(v) | 
|  | 29 | +	} | 
|  | 30 | +} | 
|  | 31 | + | 
|  | 32 | +func (encoder *secretEncoder) IsEmpty(ptr unsafe.Pointer) bool { | 
|  | 33 | +	if encoder.isPtr { | 
|  | 34 | +		p := *(**string)(ptr) | 
|  | 35 | +		if p == nil { | 
|  | 36 | +			return true | 
|  | 37 | +		} | 
|  | 38 | +		return len(*p) == 0 | 
|  | 39 | +	} | 
|  | 40 | + | 
|  | 41 | +	return len(*(*string)(ptr)) == 0 | 
|  | 42 | +} | 
|  | 43 | + | 
|  | 44 | +// secretURLEncoder encodes SecretURL to plain text JSON, | 
|  | 45 | +// avoding the default masking behavior of the structure. | 
|  | 46 | +type secretURLEncoder struct { | 
|  | 47 | +	isPtr bool | 
|  | 48 | +} | 
|  | 49 | + | 
|  | 50 | +func (encoder *secretURLEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) { | 
|  | 51 | +	if encoder.isPtr { | 
|  | 52 | +		p := *(**amcfg.SecretURL)(ptr) | 
|  | 53 | +		if p == nil { | 
|  | 54 | +			stream.WriteNil() | 
|  | 55 | +			return | 
|  | 56 | +		} | 
|  | 57 | +		encoder.encodeSecretURL(*p, stream) | 
|  | 58 | +	} else { | 
|  | 59 | +		v := *(*amcfg.SecretURL)(ptr) | 
|  | 60 | +		encoder.encodeSecretURL(v, stream) | 
|  | 61 | +	} | 
|  | 62 | +} | 
|  | 63 | + | 
|  | 64 | +func (encoder *secretURLEncoder) IsEmpty(ptr unsafe.Pointer) bool { | 
|  | 65 | +	if encoder.isPtr { | 
|  | 66 | +		p := *(**amcfg.SecretURL)(ptr) | 
|  | 67 | +		if p == nil { | 
|  | 68 | +			return true | 
|  | 69 | +		} | 
|  | 70 | +		return encoder.isSecretURLEmpty(*p) | 
|  | 71 | +	} | 
|  | 72 | + | 
|  | 73 | +	v := *(*amcfg.SecretURL)(ptr) | 
|  | 74 | +	return encoder.isSecretURLEmpty(v) | 
|  | 75 | +} | 
|  | 76 | + | 
|  | 77 | +func (encoder *secretURLEncoder) encodeSecretURL(v amcfg.SecretURL, stream *jsoniter.Stream) { | 
|  | 78 | +	// SecretURL is a type alias for URL, so we can cast it to URL to access the URL field | 
|  | 79 | +	url := amcfg.URL(v) | 
|  | 80 | +	if url.URL != nil { | 
|  | 81 | +		stream.WriteString(url.String()) | 
|  | 82 | +	} else { | 
|  | 83 | +		stream.WriteNil() | 
|  | 84 | +	} | 
|  | 85 | +} | 
|  | 86 | + | 
|  | 87 | +func (encoder *secretURLEncoder) isSecretURLEmpty(v amcfg.SecretURL) bool { | 
|  | 88 | +	url := amcfg.URL(v) | 
|  | 89 | +	return url.URL == nil || url.String() == "" | 
|  | 90 | +} | 
|  | 91 | + | 
|  | 92 | +func newPlainAPI() jsoniter.API { | 
|  | 93 | +	cfg := jsoniter.Config{ | 
|  | 94 | +		EscapeHTML:             true, | 
|  | 95 | +		SortMapKeys:            true, | 
|  | 96 | +		ValidateJsonRawMessage: true, | 
|  | 97 | +	} | 
|  | 98 | +	api := cfg.Froze() | 
|  | 99 | + | 
|  | 100 | +	extension := jsoniter.EncoderExtension{ | 
|  | 101 | +		reflect2.TypeOfPtr((*amcfg.Secret)(nil)).Elem():     &secretEncoder{isPtr: false}, | 
|  | 102 | +		reflect2.TypeOfPtr((*amcfg.Secret)(nil)):            &secretEncoder{isPtr: true}, | 
|  | 103 | +		reflect2.TypeOfPtr((*commoncfg.Secret)(nil)).Elem(): &secretEncoder{isPtr: false}, | 
|  | 104 | +		reflect2.TypeOfPtr((*commoncfg.Secret)(nil)):        &secretEncoder{isPtr: true}, | 
|  | 105 | +		reflect2.TypeOfPtr((*amcfg.SecretURL)(nil)).Elem():  &secretURLEncoder{isPtr: false}, | 
|  | 106 | +		reflect2.TypeOfPtr((*amcfg.SecretURL)(nil)):         &secretURLEncoder{isPtr: true}, | 
|  | 107 | +	} | 
|  | 108 | + | 
|  | 109 | +	api.RegisterExtension(extension) | 
|  | 110 | + | 
|  | 111 | +	return api | 
|  | 112 | +} | 
|  | 113 | + | 
|  | 114 | +var ( | 
|  | 115 | +	plainJSON = newPlainAPI() | 
|  | 116 | +) | 
|  | 117 | + | 
|  | 118 | +// MarshalJSONWithSecrets marshals the given value to JSON with secrets in plain text. | 
|  | 119 | +// | 
|  | 120 | +// alertmanager's and prometeus' Secret and SecretURL types mask their values | 
|  | 121 | +// when marshaled with the standard JSON or YAML marshallers. This function | 
|  | 122 | +// preserves the values of these types by using a custom JSON encoder. | 
|  | 123 | +func MarshalJSONWithSecrets(v any) ([]byte, error) { | 
|  | 124 | +	return plainJSON.Marshal(v) | 
|  | 125 | +} | 
0 commit comments