From ee1bc3d7d1faae0a3fc54a9bd44df9dd027680e6 Mon Sep 17 00:00:00 2001 From: Nikita Volodin Date: Thu, 25 Apr 2024 17:32:04 -0400 Subject: [PATCH] fix(prose/golang): access response headers while processing reponse body Previously, we were accessing ContentType header of the request, while attempting to analyze the body of both request and response. Now, we correctly save and access response headers for response body, while still using request headers for request body. --- .../pkg/envoyfilter/filter.go | 20 ++++++++--- .../internal/common/packet_processing.go | 35 +++++++++++++++---- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/privacy-profile-composer/pkg/envoyfilter/filter.go b/privacy-profile-composer/pkg/envoyfilter/filter.go index 8e51e21b..9ced8312 100644 --- a/privacy-profile-composer/pkg/envoyfilter/filter.go +++ b/privacy-profile-composer/pkg/envoyfilter/filter.go @@ -32,7 +32,8 @@ type Filter struct { // Runtime state of the filter parentSpanContext model.SpanContext - headerMetadata common.HeaderMetadata + reqHeaderMetadata common.RequestHeaderMetadata + resHeaderMetadata common.ResponseHeaderMetadata thirdPartyURL string processDecodeBody bool decodeDataBuffer string @@ -52,7 +53,7 @@ func (f *Filter) DecodeHeaders(header api.RequestHeaderMap, endStream bool) api. span.Tag("SIDECAR_DIRECTION", string(f.config.direction)) span.Tag("DATA_FLOW", "DECODE_HEADERS") - f.headerMetadata = common.ExtractHeaderData(header) + f.reqHeaderMetadata = common.ExtractRequestHeaderData(header) // common.LogDecodeHeaderData(header) @@ -88,7 +89,7 @@ func (f *Filter) DecodeHeaders(header api.RequestHeaderMap, endStream bool) api. return api.Continue } - f.thirdPartyURL = f.headerMetadata.Host + f.thirdPartyURL = f.reqHeaderMetadata.Host f.processDecodeBody = true default: @@ -169,6 +170,8 @@ func (f *Filter) EncodeHeaders(header api.ResponseHeaderMap, endStream bool) api span.Tag("SIDECAR_DIRECTION", string(f.config.direction)) span.Tag("DATA_FLOW", "ENCODE_HEADERS") + f.resHeaderMetadata = common.ExtractResponseHeaderData(header) + if endStream { // here we have a header-only request return api.Continue @@ -262,14 +265,21 @@ func (f *Filter) processBody(ctx context.Context, body string, isDecode bool) (s proseTags[PROSE_SIDECAR_DIRECTION] = string(f.config.direction) - jsonBody, err := common.GetJSONBody(ctx, f.headerMetadata, body) + var contentType *string + if isDecode { + contentType = f.reqHeaderMetadata.ContentType + } else { + contentType = f.resHeaderMetadata.ContentType + } + + jsonBody, err := common.GetJSONBody(ctx, contentType, body) if err != nil { proseTags[PROSE_JSON_BODY_ERROR] = fmt.Sprintf("%s", err) return false, err, proseTags } // Run Presidio and add tags for PII types or an error from Presidio - piiTypes, err := common.PiiAnalysis(ctx, f.config.presidioUrl, f.headerMetadata.SvcName, jsonBody) + piiTypes, err := common.PiiAnalysis(ctx, f.config.presidioUrl, f.reqHeaderMetadata.SvcName, jsonBody) if err != nil { proseTags[PROSE_PRESIDIO_ERROR] = fmt.Sprintf("%s", err) return false, err, proseTags diff --git a/privacy-profile-composer/pkg/envoyfilter/internal/common/packet_processing.go b/privacy-profile-composer/pkg/envoyfilter/internal/common/packet_processing.go index 44c96d04..f6cf5027 100644 --- a/privacy-profile-composer/pkg/envoyfilter/internal/common/packet_processing.go +++ b/privacy-profile-composer/pkg/envoyfilter/internal/common/packet_processing.go @@ -14,7 +14,7 @@ import ( const anyPurpose = "ANY" const unknownSvcName = "UNKNOWN SVC" -type HeaderMetadata struct { +type RequestHeaderMetadata struct { Host string Method string Path string @@ -27,6 +27,11 @@ type HeaderMetadata struct { Purpose string } +type ResponseHeaderMetadata struct { + ContentType *string + ContentLength *string +} + type SidecarDirection string const ( @@ -51,8 +56,8 @@ func LogEncodeHeaderData(header api.ResponseHeaderMap) { //}) } -func ExtractHeaderData(header api.RequestHeaderMap) HeaderMetadata { - metadata := HeaderMetadata{ +func ExtractRequestHeaderData(header api.RequestHeaderMap) RequestHeaderMetadata { + metadata := RequestHeaderMetadata{ Host: header.Host(), Method: header.Method(), Path: header.Path(), @@ -99,6 +104,22 @@ func ExtractHeaderData(header api.RequestHeaderMap) HeaderMetadata { return metadata } +func ExtractResponseHeaderData(headers api.ResponseHeaderMap) ResponseHeaderMetadata { + metadata := ResponseHeaderMetadata{} + + contentType, exists := headers.Get("content-type") + if exists { + metadata.ContentType = &contentType + } + + contentLength, exists := headers.Get("content-length") + if exists { + metadata.ContentLength = &contentLength + } + + return metadata +} + func GetDirection(callbacks api.FilterCallbackHandler) (SidecarDirection, error) { directionEnum, err := callbacks.GetProperty("xds.listener_direction") if err != nil { @@ -128,15 +149,15 @@ func GetDirection(callbacks api.FilterCallbackHandler) (SidecarDirection, error) "check the Envoy docs for the range of values for this key", directionInt) } -func GetJSONBody(ctx context.Context, headerMetadata HeaderMetadata, body string) (interface{}, error) { +func GetJSONBody(ctx context.Context, contentType *string, body string) (interface{}, error) { span, ctx := GlobalTracer.StartSpanFromContext(ctx, "getJSONBody") defer span.Finish() - if headerMetadata.ContentType == nil { + if contentType == nil { return nil, fmt.Errorf("cannot analyze body, since 'ContentType' header is not set") } - switch *headerMetadata.ContentType { + switch *contentType { case "application/json": var data interface{} @@ -154,6 +175,6 @@ func GetJSONBody(ctx context.Context, headerMetadata HeaderMetadata, body string return query, nil default: - return nil, fmt.Errorf("cannot analyze a body with ContentType '%s'", *headerMetadata.ContentType) + return nil, fmt.Errorf("cannot analyze a body with ContentType '%s'", *contentType) } }