From ef9002565e315f74a695fd84065aba569255378f Mon Sep 17 00:00:00 2001 From: Eliott Bouhana <47679741+eliottness@users.noreply.github.com> Date: Wed, 2 Oct 2024 17:44:07 +0200 Subject: [PATCH] internal/appsec: fix derivatives serdes on simple types (#2905) Signed-off-by: Eliott Bouhana --- .../emitter/trace/service_entry_span.go | 35 ++++++++++++------- internal/appsec/listener/waf/waf.go | 2 +- internal/appsec/waf_test.go | 19 ++++++---- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/internal/appsec/emitter/trace/service_entry_span.go b/internal/appsec/emitter/trace/service_entry_span.go index b8c32c8ef1..98e14b092f 100644 --- a/internal/appsec/emitter/trace/service_entry_span.go +++ b/internal/appsec/emitter/trace/service_entry_span.go @@ -41,8 +41,8 @@ type ( // ServiceEntrySpanTagsBulk is a bulk event that is used to send tags to a service entry span ServiceEntrySpanTagsBulk struct { - Tags []JSONServiceEntrySpanTag - JSONTags []JSONServiceEntrySpanTag + Tags []JSONServiceEntrySpanTag + SerializableTags []JSONServiceEntrySpanTag } ) @@ -55,19 +55,30 @@ func (op *ServiceEntrySpanOperation) SetTag(key string, value any) { op.tags[key] = value } -// SetJSONTag adds the key/value pair to the tags to add to the service entry span. Value will be serialized as JSON. -func (op *ServiceEntrySpanOperation) SetJSONTag(key string, value any) { +// SetSerializableTag adds the key/value pair to the tags to add to the service entry span. +// The value MAY be serialized as JSON if necessary but simple types will not be serialized. +func (op *ServiceEntrySpanOperation) SetSerializableTag(key string, value any) { op.mu.Lock() defer op.mu.Unlock() - op.jsonTags[key] = value + op.setSerializableTag(key, value) } -// SetJSONTags adds the key/value pairs to the tags to add to the service entry span. Values will be serialized as JSON. -func (op *ServiceEntrySpanOperation) SetJSONTags(tags map[string]any) { +// SetSerializableTags adds the key/value pairs to the tags to add to the service entry span. +// Values MAY be serialized as JSON if necessary but simple types will not be serialized. +func (op *ServiceEntrySpanOperation) SetSerializableTags(tags map[string]any) { op.mu.Lock() defer op.mu.Unlock() - for k, v := range tags { - op.jsonTags[k] = v + for key, value := range tags { + op.setSerializableTag(key, value) + } +} + +func (op *ServiceEntrySpanOperation) setSerializableTag(key string, value any) { + switch value.(type) { + case string, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, bool: + op.tags[key] = value + default: + op.jsonTags[key] = value } } @@ -96,7 +107,7 @@ func (op *ServiceEntrySpanOperation) OnServiceEntrySpanTagEvent(tag ServiceEntry // OnJSONServiceEntrySpanTagEvent is a callback that is called when a dyngo.OnData is triggered with a JSONServiceEntrySpanTag event func (op *ServiceEntrySpanOperation) OnJSONServiceEntrySpanTagEvent(tag JSONServiceEntrySpanTag) { - op.SetJSONTag(tag.Key, tag.Value) + op.SetSerializableTag(tag.Key, tag.Value) } // OnServiceEntrySpanTagsBulkEvent is a callback that is called when a dyngo.OnData is triggered with a ServiceEntrySpanTagsBulk event @@ -105,8 +116,8 @@ func (op *ServiceEntrySpanOperation) OnServiceEntrySpanTagsBulkEvent(bulk Servic op.SetTag(v.Key, v.Value) } - for _, v := range bulk.JSONTags { - op.SetJSONTag(v.Key, v.Value) + for _, v := range bulk.SerializableTags { + op.SetSerializableTag(v.Key, v.Value) } } diff --git a/internal/appsec/listener/waf/waf.go b/internal/appsec/listener/waf/waf.go index 517ce88cdb..308eaa25d7 100644 --- a/internal/appsec/listener/waf/waf.go +++ b/internal/appsec/listener/waf/waf.go @@ -112,7 +112,7 @@ func (waf *Feature) onFinish(op *waf.ContextOperation, _ waf.ContextRes) { log.Debug("appsec: failed to set event span tags: %v", err) } - op.SetJSONTags(op.Derivatives()) + op.SetSerializableTags(op.Derivatives()) if stacks := op.StackTraces(); len(stacks) > 0 { op.SetTag(stacktrace.SpanKey, stacktrace.GetSpanValue(stacks...)) } diff --git a/internal/appsec/waf_test.go b/internal/appsec/waf_test.go index 8045b86831..7e169ffb7a 100644 --- a/internal/appsec/waf_test.go +++ b/internal/appsec/waf_test.go @@ -967,13 +967,20 @@ func TestAttackerFingerprinting(t *testing.T) { resp, err := srv.Client().Do(req) require.NoError(t, err) defer resp.Body.Close() - spans := mt.FinishedSpans() - require.Len(t, spans, 1) - require.Contains(t, spans[0].Tags(), "_dd.appsec.fp.http.header") - require.Contains(t, spans[0].Tags(), "_dd.appsec.fp.http.endpoint") - require.Contains(t, spans[0].Tags(), "_dd.appsec.fp.http.network") - require.Contains(t, spans[0].Tags(), "_dd.appsec.fp.session") + require.Len(t, mt.FinishedSpans(), 1) + + tags := mt.FinishedSpans()[0].Tags() + + require.Contains(t, tags, "_dd.appsec.fp.http.header") + require.Contains(t, tags, "_dd.appsec.fp.http.endpoint") + require.Contains(t, tags, "_dd.appsec.fp.http.network") + require.Contains(t, tags, "_dd.appsec.fp.session") + + require.Regexp(t, `^hdr-`, tags["_dd.appsec.fp.http.header"]) + require.Regexp(t, `^http-`, tags["_dd.appsec.fp.http.endpoint"]) + require.Regexp(t, `^ssn-`, tags["_dd.appsec.fp.session"]) + require.Regexp(t, `^net-`, tags["_dd.appsec.fp.http.network"]) } func init() {