diff --git a/audit/options.go b/audit/options.go index c38dc92c7c..812ac51d54 100644 --- a/audit/options.go +++ b/audit/options.go @@ -5,6 +5,7 @@ package audit import ( "errors" + "reflect" "strings" "time" ) @@ -150,9 +151,13 @@ func WithHMACAccessor(h bool) Option { } // WithHeaderFormatter provides an Option to supply a HeaderFormatter. +// If the HeaderFormatter interface supplied is nil (type or value), the option will not be applied. func WithHeaderFormatter(f HeaderFormatter) Option { return func(o *options) error { - o.withHeaderFormatter = f + if f != nil && !reflect.ValueOf(f).IsNil() { + o.withHeaderFormatter = f + } + return nil } } diff --git a/audit/options_test.go b/audit/options_test.go index bfcb989de4..5d089f229b 100644 --- a/audit/options_test.go +++ b/audit/options_test.go @@ -4,6 +4,7 @@ package audit import ( + "context" "testing" "time" @@ -361,6 +362,45 @@ func TestOptions_WithOmitTime(t *testing.T) { } } +// TestOptions_WithHeaderFormatter exercises the WithHeaderFormatter Option to +// ensure it applies the option as expected under various circumstances. +func TestOptions_WithHeaderFormatter(t *testing.T) { + tests := map[string]struct { + Value HeaderFormatter + ExpectedValue HeaderFormatter + ShouldLeaveUninitialized bool + }{ + "nil": { + Value: nil, + ExpectedValue: nil, + }, + "unassigned-interface": { + ShouldLeaveUninitialized: true, + }, + "happy-path": { + Value: &testHeaderFormatter{}, + ExpectedValue: &testHeaderFormatter{}, + }, + } + + for name, tc := range tests { + name := name + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + opts := &options{} + var f HeaderFormatter + if !tc.ShouldLeaveUninitialized { + f = tc.Value + } + applyOption := WithHeaderFormatter(f) + err := applyOption(opts) + require.NoError(t, err) + require.Equal(t, tc.ExpectedValue, opts.withHeaderFormatter) + }) + } +} + // TestOptions_Default exercises getDefaultOptions to assert the default values. func TestOptions_Default(t *testing.T) { opts := getDefaultOptions() @@ -485,3 +525,12 @@ func TestOptions_Opts(t *testing.T) { }) } } + +// testHeaderFormatter is a stub to prevent the need to import the vault package +// to bring in vault.AuditedHeadersConfig for testing. +type testHeaderFormatter struct{} + +// ApplyConfig satisfied the HeaderFormatter interface for testing. +func (f *testHeaderFormatter) ApplyConfig(ctx context.Context, headers map[string][]string, salter Salter) (result map[string][]string, retErr error) { + return nil, nil +} diff --git a/changelog/22694.txt b/changelog/22694.txt new file mode 100644 index 0000000000..26f61b8660 --- /dev/null +++ b/changelog/22694.txt @@ -0,0 +1,3 @@ +```release-note:bug +audit: Prevent panic due to nil pointer receiver for audit header formatting. +```