From d66fdb4dfd3f9d46afbbcf47644db6c47f3a08f7 Mon Sep 17 00:00:00 2001 From: Marc Boudreau Date: Mon, 14 Aug 2023 11:00:49 -0400 Subject: [PATCH] Use non-persistent Salter for logging test message (#22308) * use non-persistent Salter for logging test message * adjust tests based on code changes to ProcessManual * suggestion for log test message fix (#22320) * clean up test code and fix misnamed elements --------- Co-authored-by: Peter Wilson --- audit/entry_formatter.go | 10 +++ audit/nodes.go | 20 +++-- audit/nodes_test.go | 170 +++++++++++++++++++++++++++------------ audit/types.go | 2 +- 4 files changed, 143 insertions(+), 59 deletions(-) diff --git a/audit/entry_formatter.go b/audit/entry_formatter.go index d3ff44b6c6..af404e1d10 100644 --- a/audit/entry_formatter.go +++ b/audit/entry_formatter.go @@ -580,3 +580,13 @@ func doElideListResponseData(data map[string]interface{}) { } } } + +// newTemporaryEntryFormatter creates a cloned EntryFormatter instance with a non-persistent Salter. +func newTemporaryEntryFormatter(n *EntryFormatter) *EntryFormatter { + return &EntryFormatter{ + salter: &nonPersistentSalt{}, + headerFormatter: n.headerFormatter, + config: n.config, + prefix: n.prefix, + } +} diff --git a/audit/nodes.go b/audit/nodes.go index 0214c1ebe3..01602d4b13 100644 --- a/audit/nodes.go +++ b/audit/nodes.go @@ -23,8 +23,8 @@ func ProcessManual(ctx context.Context, data *logical.LogInput, ids []eventlogge switch { case data == nil: return errors.New("data cannot be nil") - case len(ids) == 0: - return errors.New("ids are required") + case len(ids) < 2: + return errors.New("minimum of 2 ids are required") case nodes == nil: return errors.New("nodes cannot be nil") case len(nodes) == 0: @@ -50,16 +50,26 @@ func ProcessManual(ctx context.Context, data *logical.LogInput, ids []eventlogge var lastSeen eventlogger.NodeType - // Process nodes data order, updating the event with the result. + // Process nodes in order, updating the event with the result. // This means we *should* do: - // 1. formatter + // 1. formatter (temporary) // 2. sink for _, id := range ids { node, ok := nodes[id] if !ok { return fmt.Errorf("node not found: %v", id) } - e, err = node.Process(ctx, e) + + switch node.Type() { + case eventlogger.NodeTypeFormatter: + // Use a temporary formatter node which doesn't persist its salt anywhere. + if formatNode, ok := node.(*EntryFormatter); ok && formatNode != nil { + e, err = newTemporaryEntryFormatter(formatNode).Process(ctx, e) + } + default: + e, err = node.Process(ctx, e) + } + if err != nil { return err } diff --git a/audit/nodes_test.go b/audit/nodes_test.go index a82bc33ace..a50034c1d4 100644 --- a/audit/nodes_test.go +++ b/audit/nodes_test.go @@ -26,10 +26,10 @@ func TestProcessManual_NilData(t *testing.T) { var ids []eventlogger.NodeID nodes := make(map[eventlogger.NodeID]eventlogger.Node) - // Filter node - filterId, filterNode := newFilterNode(t) - ids = append(ids, filterId) - nodes[filterId] = filterNode + // Formatter node + formatterId, formatterNode := newFormatterNode(t) + ids = append(ids, formatterId) + nodes[formatterId] = formatterNode // Sink node sinkId, sinkNode := newSinkNode(t) @@ -41,29 +41,48 @@ func TestProcessManual_NilData(t *testing.T) { require.EqualError(t, err, "data cannot be nil") } -// TestProcessManual_NoIds tests ProcessManual when no IDs are supplied -func TestProcessManual_NoIds(t *testing.T) { - t.Parallel() +// TestProcessManual_BadIDs tests ProcessManual when different bad values are +// supplied for the ID parameter. +func TestProcessManual_BadIDs(t *testing.T) { + tests := map[string]struct { + IDs []eventlogger.NodeID + ExpectedErrorMessage string + }{ + "nil": { + IDs: nil, + ExpectedErrorMessage: "minimum of 2 ids are required", + }, + "one": { + IDs: []eventlogger.NodeID{"1"}, + ExpectedErrorMessage: "minimum of 2 ids are required", + }, + } - var ids []eventlogger.NodeID - nodes := make(map[eventlogger.NodeID]eventlogger.Node) + for name, tc := range tests { + name := name + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + nodes := make(map[eventlogger.NodeID]eventlogger.Node) - // Filter node - filterId, filterNode := newFilterNode(t) - nodes[filterId] = filterNode + // Formatter node + formatterId, formatterNode := newFormatterNode(t) + nodes[formatterId] = formatterNode - // Sink node - sinkId, sinkNode := newSinkNode(t) - nodes[sinkId] = sinkNode + // Sink node + sinkId, sinkNode := newSinkNode(t) + nodes[sinkId] = sinkNode - // Data - requestId, err := uuid.GenerateUUID() - require.NoError(t, err) - data := newData(requestId) + // Data + requestId, err := uuid.GenerateUUID() + require.NoError(t, err) + data := newData(requestId) - err = ProcessManual(namespace.RootContext(context.Background()), data, ids, nodes) - require.Error(t, err) - require.EqualError(t, err, "ids are required") + err = ProcessManual(namespace.RootContext(context.Background()), data, tc.IDs, nodes) + require.Error(t, err) + require.EqualError(t, err, tc.ExpectedErrorMessage) + }) + } } // TestProcessManual_NoNodes tests ProcessManual when no nodes are supplied. @@ -73,9 +92,9 @@ func TestProcessManual_NoNodes(t *testing.T) { var ids []eventlogger.NodeID nodes := make(map[eventlogger.NodeID]eventlogger.Node) - // Filter node - filterId, _ := newFilterNode(t) - ids = append(ids, filterId) + // Formatter node + formatterId, _ := newFormatterNode(t) + ids = append(ids, formatterId) // Sink node sinkId, _ := newSinkNode(t) @@ -99,10 +118,10 @@ func TestProcessManual_IdNodeMismatch(t *testing.T) { var ids []eventlogger.NodeID nodes := make(map[eventlogger.NodeID]eventlogger.Node) - // Filter node - filterId, filterNode := newFilterNode(t) - ids = append(ids, filterId) - nodes[filterId] = filterNode + // Formatter node + formatterId, formatterNode := newFormatterNode(t) + ids = append(ids, formatterId) + nodes[formatterId] = formatterNode // Sink node sinkId, _ := newSinkNode(t) @@ -118,18 +137,46 @@ func TestProcessManual_IdNodeMismatch(t *testing.T) { require.ErrorContains(t, err, "node not found: ") } -// TestProcessManual_LastNodeNotSink tests ProcessManual when the last node (by ID) -// is not an eventlogger.NodeTypeSink. +// TestProcessManual_NotEnoughNodes tests ProcessManual when there is only one +// node provided. +func TestProcessManual_NotEnoughNodes(t *testing.T) { + t.Parallel() + + var ids []eventlogger.NodeID + nodes := make(map[eventlogger.NodeID]eventlogger.Node) + + // Formatter node + formatterId, formatterNode := newFormatterNode(t) + ids = append(ids, formatterId) + nodes[formatterId] = formatterNode + + // Data + requestId, err := uuid.GenerateUUID() + require.NoError(t, err) + data := newData(requestId) + + err = ProcessManual(namespace.RootContext(context.Background()), data, ids, nodes) + require.Error(t, err) + require.EqualError(t, err, "minimum of 2 ids are required") +} + +// TestProcessManual_LastNodeNotSink tests ProcessManual when the last node is +// not a Sink node. func TestProcessManual_LastNodeNotSink(t *testing.T) { t.Parallel() var ids []eventlogger.NodeID nodes := make(map[eventlogger.NodeID]eventlogger.Node) - // Filter node - filterId, filterNode := newFilterNode(t) - ids = append(ids, filterId) - nodes[filterId] = filterNode + // Formatter node + formatterId, formatterNode := newFormatterNode(t) + ids = append(ids, formatterId) + nodes[formatterId] = formatterNode + + // Another Formatter node + formatterId, formatterNode = newFormatterNode(t) + ids = append(ids, formatterId) + nodes[formatterId] = formatterNode // Data requestId, err := uuid.GenerateUUID() @@ -149,10 +196,10 @@ func TestProcessManual(t *testing.T) { var ids []eventlogger.NodeID nodes := make(map[eventlogger.NodeID]eventlogger.Node) - // Filter node - filterId, filterNode := newFilterNode(t) - ids = append(ids, filterId) - nodes[filterId] = filterNode + // Formatter node + formatterId, formatterNode := newFormatterNode(t) + ids = append(ids, formatterId) + nodes[formatterId] = formatterNode // Sink node sinkId, sinkNode := newSinkNode(t) @@ -168,20 +215,6 @@ func TestProcessManual(t *testing.T) { require.NoError(t, err) } -// newFilterNode creates a new UUID and EntryFormatter (filter node). -func newFilterNode(t *testing.T) (eventlogger.NodeID, *EntryFormatter) { - t.Helper() - - filterId, err := event.GenerateNodeID() - require.NoError(t, err) - cfg, err := NewFormatterConfig() - require.NoError(t, err) - filterNode, err := NewEntryFormatter(cfg, newStaticSalt(t)) - require.NoError(t, err) - - return filterId, filterNode -} - // newSinkNode creates a new UUID and NoopSink (sink node). func newSinkNode(t *testing.T) (eventlogger.NodeID, *event.NoopSink) { t.Helper() @@ -193,6 +226,37 @@ func newSinkNode(t *testing.T) (eventlogger.NodeID, *event.NoopSink) { return sinkId, sinkNode } +// TestFormatter is a trivial implementation of the eventlogger.Node interface +// used as a place-holder for Formatter nodes in tests. +type TestFormatter struct{} + +// Process trivially formats the event by storing "test" as a byte slice under +// the test format type. +func (f *TestFormatter) Process(_ context.Context, e *eventlogger.Event) (*eventlogger.Event, error) { + e.FormattedAs("test", []byte("test")) + + return e, nil +} + +// Reopen does nothing. +func (f *TestFormatter) Reopen() error { + return nil +} + +// Type returns the eventlogger.NodeTypeFormatter type. +func (f *TestFormatter) Type() eventlogger.NodeType { + return eventlogger.NodeTypeFormatter +} + +// newFormatterNode creates a new TestFormatter (formatter node). +func newFormatterNode(t *testing.T) (eventlogger.NodeID, *TestFormatter) { + nodeId, err := event.GenerateNodeID() + require.NoError(t, err) + node := &TestFormatter{} + + return nodeId, node +} + // newData creates a sample logical.LogInput to be used as data for tests. func newData(id string) *logical.LogInput { return &logical.LogInput{ diff --git a/audit/types.go b/audit/types.go index f7afebb8b9..8b3b64bd03 100644 --- a/audit/types.go +++ b/audit/types.go @@ -257,7 +257,7 @@ type Namespace struct { Path string `json:"path,omitempty"` } -// nonPersistentSalt is used for obtaining a salt that is +// nonPersistentSalt is used for obtaining a salt that is not persisted. type nonPersistentSalt struct{} // Backend interface must be implemented for an audit