mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-29 17:52:32 +00:00
backport of commit ae774b93d3 (#22955)
Co-authored-by: Peter Wilson <peter.wilson@hashicorp.com>
This commit is contained in:
committed by
GitHub
parent
fc3fb91a40
commit
27491f82e4
@@ -15,6 +15,7 @@ func getDefaultOptions() options {
|
||||
return options{
|
||||
withNow: time.Now(),
|
||||
withFormat: JSONFormat,
|
||||
withHMACAccessor: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,11 +109,7 @@ func WithFormat(f string) Option {
|
||||
// WithPrefix provides an Option to represent a prefix for a file sink.
|
||||
func WithPrefix(prefix string) Option {
|
||||
return func(o *options) error {
|
||||
prefix = strings.TrimSpace(prefix)
|
||||
|
||||
if prefix != "" {
|
||||
o.withPrefix = prefix
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ func TestOptions_WithPrefix(t *testing.T) {
|
||||
"whitespace": {
|
||||
Value: " ",
|
||||
IsErrorExpected: false,
|
||||
ExpectedErrorMessage: "",
|
||||
ExpectedValue: " ",
|
||||
},
|
||||
"valid": {
|
||||
Value: "test",
|
||||
|
||||
@@ -98,7 +98,7 @@ func TestFormatJSON_formatRequest(t *testing.T) {
|
||||
|
||||
for name, tc := range cases {
|
||||
var buf bytes.Buffer
|
||||
cfg, err := NewFormatterConfig()
|
||||
cfg, err := NewFormatterConfig(WithHMACAccessor(false))
|
||||
require.NoError(t, err)
|
||||
f, err := NewEntryFormatter(cfg, ss)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -22,6 +22,11 @@ import (
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
)
|
||||
|
||||
const (
|
||||
stdout = "stdout"
|
||||
discard = "discard"
|
||||
)
|
||||
|
||||
func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool, headersConfig audit.HeaderFormatter) (audit.Backend, error) {
|
||||
if conf.SaltConfig == nil {
|
||||
return nil, fmt.Errorf("nil salt config")
|
||||
@@ -39,53 +44,45 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
||||
}
|
||||
|
||||
// normalize path if configured for stdout
|
||||
if strings.EqualFold(path, "stdout") {
|
||||
path = "stdout"
|
||||
if strings.EqualFold(path, stdout) {
|
||||
path = stdout
|
||||
}
|
||||
if strings.EqualFold(path, "discard") {
|
||||
path = "discard"
|
||||
if strings.EqualFold(path, discard) {
|
||||
path = discard
|
||||
}
|
||||
|
||||
format, ok := conf.Config["format"]
|
||||
if !ok {
|
||||
format = audit.JSONFormat.String()
|
||||
}
|
||||
switch format {
|
||||
case audit.JSONFormat.String(), audit.JSONxFormat.String():
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown format type %q", format)
|
||||
var cfgOpts []audit.Option
|
||||
|
||||
if format, ok := conf.Config["format"]; ok {
|
||||
cfgOpts = append(cfgOpts, audit.WithFormat(format))
|
||||
}
|
||||
|
||||
// Check if hashing of accessor is disabled
|
||||
hmacAccessor := true
|
||||
if hmacAccessorRaw, ok := conf.Config["hmac_accessor"]; ok {
|
||||
value, err := strconv.ParseBool(hmacAccessorRaw)
|
||||
v, err := strconv.ParseBool(hmacAccessorRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hmacAccessor = value
|
||||
cfgOpts = append(cfgOpts, audit.WithHMACAccessor(v))
|
||||
}
|
||||
|
||||
// Check if raw logging is enabled
|
||||
logRaw := false
|
||||
if raw, ok := conf.Config["log_raw"]; ok {
|
||||
b, err := strconv.ParseBool(raw)
|
||||
v, err := strconv.ParseBool(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logRaw = b
|
||||
cfgOpts = append(cfgOpts, audit.WithRaw(v))
|
||||
}
|
||||
|
||||
elideListResponses := false
|
||||
if elideListResponsesRaw, ok := conf.Config["elide_list_responses"]; ok {
|
||||
value, err := strconv.ParseBool(elideListResponsesRaw)
|
||||
v, err := strconv.ParseBool(elideListResponsesRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
elideListResponses = value
|
||||
cfgOpts = append(cfgOpts, audit.WithElision(v))
|
||||
}
|
||||
|
||||
// Check if mode is provided
|
||||
mode := os.FileMode(0o600)
|
||||
if modeRaw, ok := conf.Config["mode"]; ok {
|
||||
m, err := strconv.ParseUint(modeRaw, 8, 32)
|
||||
@@ -95,7 +92,7 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
||||
switch m {
|
||||
case 0:
|
||||
// if mode is 0000, then do not modify file mode
|
||||
if path != "stdout" && path != "discard" {
|
||||
if path != stdout && path != discard {
|
||||
fileInfo, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -107,12 +104,7 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
||||
}
|
||||
}
|
||||
|
||||
cfg, err := audit.NewFormatterConfig(
|
||||
audit.WithElision(elideListResponses),
|
||||
audit.WithFormat(format),
|
||||
audit.WithHMACAccessor(hmacAccessor),
|
||||
audit.WithRaw(logRaw),
|
||||
)
|
||||
cfg, err := audit.NewFormatterConfig(cfgOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -136,11 +128,13 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
||||
return nil, fmt.Errorf("error creating formatter: %w", err)
|
||||
}
|
||||
var w audit.Writer
|
||||
switch format {
|
||||
case "json":
|
||||
switch b.formatConfig.RequiredFormat {
|
||||
case audit.JSONFormat:
|
||||
w = &audit.JSONWriter{Prefix: conf.Config["prefix"]}
|
||||
case "jsonx":
|
||||
case audit.JSONxFormat:
|
||||
w = &audit.JSONxWriter{Prefix: conf.Config["prefix"]}
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown format type %q", b.formatConfig.RequiredFormat)
|
||||
}
|
||||
|
||||
fw, err := audit.NewEntryFormatterWriter(b.formatConfig, f, w)
|
||||
@@ -164,16 +158,24 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
||||
var sinkNode eventlogger.Node
|
||||
|
||||
switch path {
|
||||
case "stdout":
|
||||
sinkNode = &audit.SinkWrapper{Name: path, Sink: event.NewStdoutSinkNode(format)}
|
||||
case "discard":
|
||||
case stdout:
|
||||
sinkNode = &audit.SinkWrapper{Name: path, Sink: event.NewStdoutSinkNode(b.formatConfig.RequiredFormat.String())}
|
||||
case discard:
|
||||
sinkNode = &audit.SinkWrapper{Name: path, Sink: event.NewNoopSink()}
|
||||
default:
|
||||
var err error
|
||||
|
||||
var opts []event.Option
|
||||
// Check if mode is provided
|
||||
if modeRaw, ok := conf.Config["mode"]; ok {
|
||||
opts = append(opts, event.WithFileMode(modeRaw))
|
||||
}
|
||||
|
||||
// The NewFileSink function attempts to open the file and will
|
||||
// return an error if it can't.
|
||||
n, err := event.NewFileSink(b.path, format, event.WithFileMode(strconv.FormatUint(uint64(mode), 8)))
|
||||
n, err := event.NewFileSink(
|
||||
b.path,
|
||||
b.formatConfig.RequiredFormat.String(), opts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("file sink creation failed for path %q: %w", path, err)
|
||||
}
|
||||
@@ -189,8 +191,8 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
||||
b.nodeMap[sinkNodeID] = sinkNode
|
||||
} else {
|
||||
switch path {
|
||||
case "stdout":
|
||||
case "discard":
|
||||
case stdout:
|
||||
case discard:
|
||||
default:
|
||||
// Ensure that the file can be successfully opened for writing;
|
||||
// otherwise it will be too late to catch later without problems
|
||||
@@ -257,9 +259,9 @@ func (b *Backend) Salt(ctx context.Context) (*salt.Salt, error) {
|
||||
func (b *Backend) LogRequest(ctx context.Context, in *logical.LogInput) error {
|
||||
var writer io.Writer
|
||||
switch b.path {
|
||||
case "stdout":
|
||||
case stdout:
|
||||
writer = os.Stdout
|
||||
case "discard":
|
||||
case discard:
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -288,7 +290,7 @@ func (b *Backend) log(_ context.Context, buf *bytes.Buffer, writer io.Writer) er
|
||||
if _, err := reader.WriteTo(writer); err == nil {
|
||||
b.fileLock.Unlock()
|
||||
return nil
|
||||
} else if b.path == "stdout" {
|
||||
} else if b.path == stdout {
|
||||
b.fileLock.Unlock()
|
||||
return err
|
||||
}
|
||||
@@ -313,9 +315,9 @@ func (b *Backend) log(_ context.Context, buf *bytes.Buffer, writer io.Writer) er
|
||||
func (b *Backend) LogResponse(ctx context.Context, in *logical.LogInput) error {
|
||||
var writer io.Writer
|
||||
switch b.path {
|
||||
case "stdout":
|
||||
case stdout:
|
||||
writer = os.Stdout
|
||||
case "discard":
|
||||
case discard:
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -337,9 +339,9 @@ func (b *Backend) LogTestMessage(ctx context.Context, in *logical.LogInput, conf
|
||||
// Old behavior
|
||||
var writer io.Writer
|
||||
switch b.path {
|
||||
case "stdout":
|
||||
case stdout:
|
||||
writer = os.Stdout
|
||||
case "discard":
|
||||
case discard:
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -390,7 +392,7 @@ func (b *Backend) open() error {
|
||||
|
||||
func (b *Backend) Reload(_ context.Context) error {
|
||||
switch b.path {
|
||||
case "stdout", "discard":
|
||||
case stdout, discard:
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,10 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
||||
|
||||
"github.com/hashicorp/eventlogger"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
||||
"github.com/hashicorp/vault/audit"
|
||||
"github.com/hashicorp/vault/internal/observability/event"
|
||||
"github.com/hashicorp/vault/sdk/helper/salt"
|
||||
@@ -38,7 +39,6 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
||||
if !ok {
|
||||
socketType = "tcp"
|
||||
}
|
||||
|
||||
writeDeadline, ok := conf.Config["write_timeout"]
|
||||
if !ok {
|
||||
writeDeadline = "2s"
|
||||
@@ -48,51 +48,39 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
||||
return nil, err
|
||||
}
|
||||
|
||||
format, ok := conf.Config["format"]
|
||||
if !ok {
|
||||
format = audit.JSONFormat.String()
|
||||
}
|
||||
switch format {
|
||||
case audit.JSONFormat.String(), audit.JSONxFormat.String():
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown format type %q", format)
|
||||
var cfgOpts []audit.Option
|
||||
|
||||
if format, ok := conf.Config["format"]; ok {
|
||||
cfgOpts = append(cfgOpts, audit.WithFormat(format))
|
||||
}
|
||||
|
||||
// Check if hashing of accessor is disabled
|
||||
hmacAccessor := true
|
||||
if hmacAccessorRaw, ok := conf.Config["hmac_accessor"]; ok {
|
||||
value, err := strconv.ParseBool(hmacAccessorRaw)
|
||||
v, err := strconv.ParseBool(hmacAccessorRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hmacAccessor = value
|
||||
cfgOpts = append(cfgOpts, audit.WithHMACAccessor(v))
|
||||
}
|
||||
|
||||
// Check if raw logging is enabled
|
||||
logRaw := false
|
||||
if raw, ok := conf.Config["log_raw"]; ok {
|
||||
b, err := strconv.ParseBool(raw)
|
||||
v, err := strconv.ParseBool(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logRaw = b
|
||||
cfgOpts = append(cfgOpts, audit.WithRaw(v))
|
||||
}
|
||||
|
||||
elideListResponses := false
|
||||
if elideListResponsesRaw, ok := conf.Config["elide_list_responses"]; ok {
|
||||
value, err := strconv.ParseBool(elideListResponsesRaw)
|
||||
v, err := strconv.ParseBool(elideListResponsesRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
elideListResponses = value
|
||||
cfgOpts = append(cfgOpts, audit.WithElision(v))
|
||||
}
|
||||
|
||||
cfg, err := audit.NewFormatterConfig(
|
||||
audit.WithElision(elideListResponses),
|
||||
audit.WithFormat(format),
|
||||
audit.WithHMACAccessor(hmacAccessor),
|
||||
audit.WithRaw(logRaw),
|
||||
)
|
||||
cfg, err := audit.NewFormatterConfig(cfgOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -113,10 +101,10 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
||||
return nil, fmt.Errorf("error creating formatter: %w", err)
|
||||
}
|
||||
var w audit.Writer
|
||||
switch format {
|
||||
case audit.JSONFormat.String():
|
||||
switch b.formatConfig.RequiredFormat {
|
||||
case audit.JSONFormat:
|
||||
w = &audit.JSONWriter{Prefix: conf.Config["prefix"]}
|
||||
case audit.JSONxFormat.String():
|
||||
case audit.JSONxFormat:
|
||||
w = &audit.JSONxWriter{Prefix: conf.Config["prefix"]}
|
||||
}
|
||||
|
||||
@@ -128,6 +116,16 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
||||
b.formatter = fw
|
||||
|
||||
if useEventLogger {
|
||||
var opts []event.Option
|
||||
|
||||
if socketType, ok := conf.Config["socket_type"]; ok {
|
||||
opts = append(opts, event.WithSocketType(socketType))
|
||||
}
|
||||
|
||||
if writeDeadline, ok := conf.Config["write_timeout"]; ok {
|
||||
opts = append(opts, event.WithMaxDuration(writeDeadline))
|
||||
}
|
||||
|
||||
b.nodeIDList = make([]eventlogger.NodeID, 2)
|
||||
b.nodeMap = make(map[eventlogger.NodeID]eventlogger.Node)
|
||||
|
||||
@@ -138,7 +136,7 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
||||
b.nodeIDList[0] = formatterNodeID
|
||||
b.nodeMap[formatterNodeID] = f
|
||||
|
||||
n, err := event.NewSocketSink(format, address, event.WithSocketType(socketType), event.WithMaxDuration(writeDuration.String()))
|
||||
n, err := event.NewSocketSink(b.formatConfig.RequiredFormat.String(), address, opts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating socket sink node: %w", err)
|
||||
}
|
||||
|
||||
@@ -39,43 +39,41 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
||||
tag = "vault"
|
||||
}
|
||||
|
||||
format, ok := conf.Config["format"]
|
||||
if !ok {
|
||||
format = audit.JSONFormat.String()
|
||||
}
|
||||
switch format {
|
||||
case audit.JSONFormat.String(), audit.JSONxFormat.String():
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown format type %q", format)
|
||||
var cfgOpts []audit.Option
|
||||
|
||||
if format, ok := conf.Config["format"]; ok {
|
||||
cfgOpts = append(cfgOpts, audit.WithFormat(format))
|
||||
}
|
||||
|
||||
// Check if hashing of accessor is disabled
|
||||
hmacAccessor := true
|
||||
if hmacAccessorRaw, ok := conf.Config["hmac_accessor"]; ok {
|
||||
value, err := strconv.ParseBool(hmacAccessorRaw)
|
||||
v, err := strconv.ParseBool(hmacAccessorRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hmacAccessor = value
|
||||
cfgOpts = append(cfgOpts, audit.WithHMACAccessor(v))
|
||||
}
|
||||
|
||||
// Check if raw logging is enabled
|
||||
logRaw := false
|
||||
if raw, ok := conf.Config["log_raw"]; ok {
|
||||
b, err := strconv.ParseBool(raw)
|
||||
v, err := strconv.ParseBool(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logRaw = b
|
||||
cfgOpts = append(cfgOpts, audit.WithRaw(v))
|
||||
}
|
||||
|
||||
elideListResponses := false
|
||||
if elideListResponsesRaw, ok := conf.Config["elide_list_responses"]; ok {
|
||||
value, err := strconv.ParseBool(elideListResponsesRaw)
|
||||
v, err := strconv.ParseBool(elideListResponsesRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
elideListResponses = value
|
||||
cfgOpts = append(cfgOpts, audit.WithElision(v))
|
||||
}
|
||||
|
||||
cfg, err := audit.NewFormatterConfig(cfgOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the logger
|
||||
@@ -84,16 +82,6 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg, err := audit.NewFormatterConfig(
|
||||
audit.WithElision(elideListResponses),
|
||||
audit.WithFormat(format),
|
||||
audit.WithHMACAccessor(hmacAccessor),
|
||||
audit.WithRaw(logRaw),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := &Backend{
|
||||
logger: logger,
|
||||
saltConfig: conf.SaltConfig,
|
||||
@@ -108,10 +96,10 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
||||
}
|
||||
|
||||
var w audit.Writer
|
||||
switch format {
|
||||
case audit.JSONFormat.String():
|
||||
switch b.formatConfig.RequiredFormat {
|
||||
case audit.JSONFormat:
|
||||
w = &audit.JSONWriter{Prefix: conf.Config["prefix"]}
|
||||
case audit.JSONxFormat.String():
|
||||
case audit.JSONxFormat:
|
||||
w = &audit.JSONxWriter{Prefix: conf.Config["prefix"]}
|
||||
}
|
||||
|
||||
@@ -123,6 +111,17 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
||||
b.formatter = fw
|
||||
|
||||
if useEventLogger {
|
||||
var opts []event.Option
|
||||
|
||||
// Get facility or default to AUTH
|
||||
if facility, ok := conf.Config["facility"]; ok {
|
||||
opts = append(opts, event.WithFacility(facility))
|
||||
}
|
||||
|
||||
if tag, ok := conf.Config["tag"]; ok {
|
||||
opts = append(opts, event.WithTag(tag))
|
||||
}
|
||||
|
||||
b.nodeIDList = make([]eventlogger.NodeID, 2)
|
||||
b.nodeMap = make(map[eventlogger.NodeID]eventlogger.Node)
|
||||
|
||||
@@ -133,7 +132,7 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
||||
b.nodeIDList[0] = formatterNodeID
|
||||
b.nodeMap[formatterNodeID] = f
|
||||
|
||||
n, err := event.NewSyslogSink(format, event.WithFacility(facility), event.WithTag(tag))
|
||||
n, err := event.NewSyslogSink(b.formatConfig.RequiredFormat.String(), opts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating syslog sink node: %w", err)
|
||||
}
|
||||
|
||||
@@ -32,12 +32,15 @@ type options struct {
|
||||
|
||||
// getDefaultOptions returns Options with their default values.
|
||||
func getDefaultOptions() options {
|
||||
fileMode := os.FileMode(0o600)
|
||||
|
||||
return options{
|
||||
withNow: time.Now(),
|
||||
withFacility: "AUTH",
|
||||
withTag: "vault",
|
||||
withSocketType: "tcp",
|
||||
withMaxDuration: 2 * time.Second,
|
||||
withFileMode: &fileMode,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,11 +113,7 @@ func WithNow(now time.Time) Option {
|
||||
// WithFacility provides an Option to represent a 'facility' for a syslog sink.
|
||||
func WithFacility(facility string) Option {
|
||||
return func(o *options) error {
|
||||
facility = strings.TrimSpace(facility)
|
||||
|
||||
if facility != "" {
|
||||
o.withFacility = facility
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -123,11 +122,7 @@ func WithFacility(facility string) Option {
|
||||
// WithTag provides an Option to represent a 'tag' for a syslog sink.
|
||||
func WithTag(tag string) Option {
|
||||
return func(o *options) error {
|
||||
tag = strings.TrimSpace(tag)
|
||||
|
||||
if tag != "" {
|
||||
o.withTag = tag
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ func TestOptions_WithFacility(t *testing.T) {
|
||||
},
|
||||
"whitespace": {
|
||||
Value: " ",
|
||||
ExpectedValue: "",
|
||||
ExpectedValue: " ",
|
||||
},
|
||||
"value": {
|
||||
Value: "juan",
|
||||
@@ -213,7 +213,7 @@ func TestOptions_WithFacility(t *testing.T) {
|
||||
},
|
||||
"spacey-value": {
|
||||
Value: " juan ",
|
||||
ExpectedValue: "juan",
|
||||
ExpectedValue: " juan ",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ func TestOptions_WithTag(t *testing.T) {
|
||||
},
|
||||
"whitespace": {
|
||||
Value: " ",
|
||||
ExpectedValue: "",
|
||||
ExpectedValue: " ",
|
||||
},
|
||||
"value": {
|
||||
Value: "juan",
|
||||
@@ -251,7 +251,7 @@ func TestOptions_WithTag(t *testing.T) {
|
||||
},
|
||||
"spacey-value": {
|
||||
Value: " juan ",
|
||||
ExpectedValue: "juan",
|
||||
ExpectedValue: " juan ",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user