Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SLT-331] feat(omnirpc): omnirpc request tracing #3289

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
39 changes: 35 additions & 4 deletions services/omnirpc/http/capture_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,28 @@

import (
"context"
"fmt"

"github.com/ethereum/go-ethereum/common"
"github.com/synapsecns/sanguine/core/bytemap"
"github.com/synapsecns/sanguine/core/metrics"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)

// CaptureClient is a mock client used for checking response values.
type CaptureClient struct {
requests []*CapturedRequest
responseFunc MakeResponseFunc
handler metrics.Handler
}

// MakeResponseFunc is used for mocking responses.
type MakeResponseFunc func(c *CapturedRequest) (Response, error)

// NewCaptureClient creates anew client for testing.
func NewCaptureClient(responseFunc MakeResponseFunc) *CaptureClient {
return &CaptureClient{requests: []*CapturedRequest{}, responseFunc: responseFunc}
func NewCaptureClient(handler metrics.Handler, responseFunc MakeResponseFunc) *CaptureClient {
return &CaptureClient{requests: []*CapturedRequest{}, responseFunc: responseFunc, handler: handler}
}

// Requests turns a list of sent requests. These are not mutation safe.
Expand All @@ -30,6 +36,7 @@
request := CapturedRequest{
Client: c,
StringHeaders: make(map[string]string),
Handler: c.handler,
}
c.requests = append(c.requests, &request)
return &request
Expand All @@ -56,6 +63,8 @@
// RequestURIBytes is the request uri bytes. Notably, this will not include
// RequestURI's set by SetRequestURI
RequestURIBytes []byte
// Metrics is the metrics handler
Handler metrics.Handler
Comment on lines +66 to +67
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enforce the association of metrics.Handler with CapturedRequest.

To prevent issues with uninitialized c.Handler, consider requiring the metrics.Handler to be set during the creation of CapturedRequest instances.

Modify the NewRequest method to accept a metrics.Handler parameter:

// NewRequest creates a new request.
-func (c *CaptureClient) NewRequest() Request {
+func (c *CaptureClient) NewRequest(handler metrics.Handler) Request {
	request := CapturedRequest{
		Client:        c,
		StringHeaders: make(map[string]string),
+		Handler:       handler,
+		Context:       context.Background(),
	}
	c.requests = append(c.requests, &request)
	return &request
}

This ensures that every CapturedRequest has a valid Handler and Context from the beginning.

Also applies to: 161-164

}

var _ Client = &CaptureClient{}
Expand Down Expand Up @@ -92,8 +101,30 @@

// Do calls responseFunc for testing.
func (c *CapturedRequest) Do() (Response, error) {
//nolint: wrapcheck
return c.Client.responseFunc(c)
_, span := c.Handler.Tracer().Start(
c.Context,
"Do",
trace.WithAttributes(
attribute.String("uri", c.RequestURI),
attribute.String("headers", fmt.Sprintf("%v", c.StringHeaders)),
attribute.String("body", common.Bytes2Hex(c.Body)),
),
)
defer func() {
metrics.EndSpan(span)
}()

resp, err := c.Client.responseFunc(c)
if err != nil {
return nil, err
}

Check warning on line 120 in services/omnirpc/http/capture_client.go

View check run for this annotation

Codecov / codecov/patch

services/omnirpc/http/capture_client.go#L119-L120

Added lines #L119 - L120 were not covered by tests

span.SetAttributes(
attribute.String("response", common.Bytes2Hex(resp.Body())),
attribute.Int("status", resp.StatusCode()),
)

return resp, err
}

var _ Request = &CapturedRequest{}
18 changes: 11 additions & 7 deletions services/omnirpc/http/capture_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,27 @@ package http_test

import (
"context"
"testing"

"github.com/brianvoe/gofakeit/v6"
. "github.com/stretchr/testify/assert"
"github.com/synapsecns/sanguine/core/metrics"
"github.com/synapsecns/sanguine/services/omnirpc/http"
"github.com/synapsecns/sanguine/services/omnirpc/http/mocks"
"testing"
)

func TestCaptureClient(t *testing.T) {
testRes := gofakeit.ImageJpeg(50, 50)
testBody := gofakeit.ImageJpeg(50, 50)

client := http.NewCaptureClient(func(c *http.CapturedRequest) (http.Response, error) {
bodyRes := new(mocks.Response)
bodyRes.On("Body").Return(testRes)
client := http.NewCaptureClient(
metrics.NewNullHandler(),
func(c *http.CapturedRequest) (http.Response, error) {
bodyRes := new(mocks.Response)
bodyRes.On("Body").Return(testRes)

return bodyRes, nil
})
return bodyRes, nil
})

testCtx := context.Background()

Expand All @@ -31,8 +35,8 @@ func TestCaptureClient(t *testing.T) {
testURL := gofakeit.URL()

testReq := client.NewRequest()
testReq.SetBody(testBody)
testReq.SetContext(testCtx)
testReq.SetBody(testBody)
testReq.SetHeaderBytes(byteHeaderK, byteHeaderV)
testReq.SetHeader(strHeaderK, strHeaderV)
testReq.SetRequestURI(testURL)
Expand Down
10 changes: 6 additions & 4 deletions services/omnirpc/http/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package http
import (
"context"
"strings"

"github.com/synapsecns/sanguine/core/metrics"
)

// Client contains a post client for interacting with json rpc servers.
Expand Down Expand Up @@ -62,14 +64,14 @@ func init() {

// NewClient creates a client from the client type
// defaults to fast http.
func NewClient(clientType ClientType) Client {
func NewClient(handler metrics.Handler, clientType ClientType) Client {
switch clientType {
case FastHTTP:
return NewFastHTTPClient()
return NewFastHTTPClient(handler)
case Resty:
return NewRestyClient()
return NewRestyClient(handler)
default:
return NewRestyClient()
return NewRestyClient(handler)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle unknown ClientType explicitly to prevent unexpected behavior.

In the default case of the switch statement, the function returns NewRestyClient(handler) when an unknown ClientType is provided. This could lead to unintended behavior if an unrecognized ClientType is used. Consider explicitly handling unknown ClientType values by returning an error or defaulting to a safe, well-documented client type, and possibly logging a warning to alert developers.

Comment on lines +67 to +74
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Action Required: Update NewClient Call Sites to Include the handler Parameter

Several instances of NewClient are missing the new handler parameter. Please update these call sites to include handler metrics.Handler to ensure proper functionality and prevent potential issues.

  • ethergo/client/capture_client.go:

    • ethClient := ethclient.NewClient(c)
    • w3Client := w3.NewClient(c)
  • ethergo/backends/geth/geth.go:

    • rawClient := ethclient.NewClient(rpcClient)
    • w3Client := w3.NewClient(rpcClient)
  • ethergo/chain/client/client.go:

    • c.Client = ethclient.NewClient(c.rpcClient)
    • c.w3Client = w3.NewClient(c.rpcClient)
    • ethClient := ethclient.NewClient(rpcClient)
    • w3Client := w3.NewClient(rpcClient)
    • ethClient := ethclient.NewClient(rpcClient)
    • w3Client := w3.NewClient(rpcClient)
  • core/metrics/otlp.go:

    • return otlptracehttp.NewClient(to.httpOptions...), nil
    • return otlptracegrpc.NewClient(to.grpcOptions...), nil
  • contrib/git-changes-action/detector/git/ref.go:

    • client := github.NewClient(tc)

Please ensure that each NewClient invocation includes the required handler parameter.

🔗 Analysis chain

Ensure all call sites of NewClient are updated with the new handler parameter.

The NewClient function signature now includes handler metrics.Handler. Please verify that all calls to NewClient throughout the codebase are updated to pass the new handler argument to prevent compilation errors or unexpected behavior.

Run the following script to find all usages of NewClient:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all call sites of NewClient that need to be updated.

# Search for usage of 'NewClient(' in Go files
rg --type go 'NewClient\('

Length of output: 5031


Script:

#!/bin/bash
# Description: Find all call sites of NewClient that do not include the handler parameter.

# Search for 'NewClient(' in Go files with fewer than expected arguments
rg --type go 'NewClient\s*\(\s*(?!handler\s*,).*?\)'

Length of output: 312


Script:

#!/bin/bash
# Description: Find all call sites of NewClient that include only one argument.

# Search for 'NewClient(' in Go files with only one argument
rg --type go 'NewClient\s*\(\s*[^,()]+\s*\)'

Length of output: 990

}
}

Expand Down
7 changes: 4 additions & 3 deletions services/omnirpc/http/client_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package http_test

import (
"github.com/brianvoe/gofakeit/v6"
. "github.com/stretchr/testify/assert"
"io"
"net/http"
"net/http/httptest"

"github.com/brianvoe/gofakeit/v6"
. "github.com/stretchr/testify/assert"
)

var jsonOptions = &gofakeit.JSONOptions{
Expand Down Expand Up @@ -42,9 +43,9 @@ func (c *HTTPSuite) TestClient() {
}))

req := client.NewRequest()
req.SetContext(c.GetTestContext())
req.SetRequestURI(svr.URL)
req.SetBody(mockBody)
req.SetContext(c.GetTestContext())
for key, val := range headers {
if gofakeit.Bool() {
req.SetHeader(key, val)
Expand Down
53 changes: 38 additions & 15 deletions services/omnirpc/http/fasthttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ package http
import (
"context"
"fmt"
"sync"
"time"

"github.com/ImVexed/fasturl"
"github.com/puzpuzpuz/xsync"
http2 "github.com/synapsecns/fasthttp-http2"
"github.com/synapsecns/sanguine/core/metrics"
"github.com/valyala/fasthttp"
"sync"
"time"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)

// dialer is an allocated fasthttp dialer for increasing dns cache time.
Expand All @@ -26,6 +30,7 @@ type fastHTTPClient struct {
// no longer needed. This allows Request recycling, reduces GC pressure
// and usually improves performance.
reqPool sync.Pool
handler metrics.Handler
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

'handler' Field Not Properly Initialized in 'fastHTTPClient'

The handler field in fastHTTPClient is not being initialized in the NewFastHTTPClient function, which could lead to potential nil pointer dereferences.

  • File: services/omnirpc/http/fasthttp.go
🔗 Analysis chain

Ensure 'handler' field is properly initialized in 'fastHTTPClient'

The addition of the handler metrics.Handler field to the fastHTTPClient struct is beneficial for metrics and tracing. However, ensure that handler is properly initialized to prevent potential nil pointer dereferences when it is used.

Run the following script to check where NewFastHTTPClient is called and verify that handler is properly initialized:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all calls to NewFastHTTPClient and check initialization of 'handler'

# Search for NewFastHTTPClient usage
rg --type go 'NewFastHTTPClient\((.*)\)' -A 2 -B 2

Length of output: 875

}

// FastClient is an interface for storing both fasthttp.Clients and fasthttp.HostClients.
Expand All @@ -42,16 +47,20 @@ var _ FastClient = &fasthttp.HostClient{}
// while substantially faster than resty, this can be a bad choice in certain cases:
// - Context Cancellation not respected: fasthttp does not support context cancellation, so we hardcode a timeout here
// this is less than ideal and puts additional load on both the application and rpc servers since we pessimistically fetch
func NewFastHTTPClient() Client {
return &fastHTTPClient{clients: xsync.NewMapOf[FastClient](), defaultClient: &fasthttp.Client{
NoDefaultUserAgentHeader: true,
Dial: dialer.Dial,
DialDualStack: false,
ReadTimeout: time.Second * 30,
WriteTimeout: time.Second * 30,
DisableHeaderNamesNormalizing: true,
DisablePathNormalizing: true,
}}
func NewFastHTTPClient(handler metrics.Handler) Client {
return &fastHTTPClient{
clients: xsync.NewMapOf[FastClient](),
defaultClient: &fasthttp.Client{
NoDefaultUserAgentHeader: true,
Dial: dialer.Dial,
DialDualStack: false,
ReadTimeout: time.Second * 30,
WriteTimeout: time.Second * 30,
DisableHeaderNamesNormalizing: true,
DisablePathNormalizing: true,
},
handler: handler,
}
Comment on lines +50 to +63
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add nil check for handler parameter

The function signature update and initialization of the handler field are correct. However, consider adding a nil check for the handler parameter to prevent potential issues if a nil handler is passed.

Apply this diff to add a nil check:

 func NewFastHTTPClient(handler metrics.Handler) Client {
+	if handler == nil {
+		handler = metrics.DefaultHandler() // Replace with appropriate default handler
+	}
 	return &fastHTTPClient{
 		clients: xsync.NewMapOf[FastClient](),
 		defaultClient: &fasthttp.Client{
 			NoDefaultUserAgentHeader:      true,
 			Dial:                          dialer.Dial,
 			DialDualStack:                 false,
 			ReadTimeout:                   time.Second * 30,
 			WriteTimeout:                  time.Second * 30,
 			DisableHeaderNamesNormalizing: true,
 			DisablePathNormalizing:        true,
 		},
 		handler: handler,
 	}
 }

Committable suggestion was skipped due to low confidence.

}

type rawResponse struct {
Expand Down Expand Up @@ -135,9 +144,10 @@ func (f *fastHTTPClient) AcquireRequest() *fastHTTPRequest {
v := f.reqPool.Get()
if v == nil {
return &fastHTTPRequest{
&fasthttp.Request{},
f,
nil,
Request: &fasthttp.Request{},
client: f,
context: nil,
handler: f.handler,
Comment on lines +147 to +150
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Initialize context with a default value and verify handler initialization

The initialization of the handler field looks correct. However, initializing context to nil might lead to nil pointer dereferences if not properly handled in subsequent operations.

Consider initializing context with a default value, such as context.Background(). Also, ensure that f.handler is properly initialized before being used here.

Apply this diff to initialize context with a default value:

 return &fastHTTPRequest{
 	Request: &fasthttp.Request{},
 	client:  f,
-	context: nil,
+	context: context.Background(),
 	handler: f.handler,
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Request: &fasthttp.Request{},
client: f,
context: nil,
handler: f.handler,
Request: &fasthttp.Request{},
client: f,
context: context.Background(),
handler: f.handler,

}
}
//nolint: forcetypeassert
Expand All @@ -158,6 +168,7 @@ type fastHTTPRequest struct {
// we need to respect context cancellation even after response
//nolint: containedctx
context context.Context
handler metrics.Handler
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential nil pointer dereference in handler initialization

The handler field in fastHTTPRequest is initialized to nil. In subsequent methods like SetBody, SetContext, SetHeader, etc., f.handler is used without checking if it's nil. This could lead to a nil pointer dereference if WithMetrics is not called before these methods.

Suggestion:

Ensure that handler is properly initialized before it's used, or add nil checks before invoking methods on handler. Alternatively, set a default metrics.Handler during initialization to prevent nil references.

}

// Reset clears request contents.
Expand Down Expand Up @@ -193,6 +204,18 @@ func (f *fastHTTPRequest) SetRequestURI(uri string) Request {
}

func (f *fastHTTPRequest) Do() (Response, error) {
_, span := f.handler.Tracer().Start(
f.context,
"Do",
trace.WithAttributes(
attribute.String("uri", f.Request.URI().String()),
attribute.String("headers", fmt.Sprintf("%v", f.Request.Header.String())),
attribute.String("body", string(f.Request.Body())),
),
)
defer func() {
metrics.EndSpan(span)
}()
Comment on lines +207 to +218
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Limit or sanitize body content in span attributes

The addition of tracing spans enhances observability, which is good. However, including the entire request body in the span attributes might lead to performance issues for large payloads and potential exposure of sensitive data.

Consider limiting the amount of body content included in the span attributes or implement a sanitization mechanism to prevent sensitive data exposure.

Apply this diff to limit the body content:

 _, span := f.handler.Tracer().Start(
 	f.context,
 	"Do",
 	trace.WithAttributes(
 		attribute.String("uri", f.Request.URI().String()),
 		attribute.String("headers", fmt.Sprintf("%v", f.Request.Header.String())),
-		attribute.String("body", string(f.Request.Body())),
+		attribute.String("body_length", fmt.Sprintf("%d", len(f.Request.Body()))),
 	),
 )
 defer func() {
 	metrics.EndSpan(span)
 }()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_, span := f.handler.Tracer().Start(
f.context,
"Do",
trace.WithAttributes(
attribute.String("uri", f.Request.URI().String()),
attribute.String("headers", fmt.Sprintf("%v", f.Request.Header.String())),
attribute.String("body", string(f.Request.Body())),
),
)
defer func() {
metrics.EndSpan(span)
}()
_, span := f.handler.Tracer().Start(
f.context,
"Do",
trace.WithAttributes(
attribute.String("uri", f.Request.URI().String()),
attribute.String("headers", fmt.Sprintf("%v", f.Request.Header.String())),
attribute.String("body_length", fmt.Sprintf("%d", len(f.Request.Body()))),
),
)
defer func() {
metrics.EndSpan(span)
}()

defer f.Reset()

uri := f.Request.URI()
Expand Down
35 changes: 29 additions & 6 deletions services/omnirpc/http/resty.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,37 @@

import (
"context"
"fmt"

"github.com/go-resty/resty/v2"
"github.com/synapsecns/sanguine/core/metrics"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)

// RestyClient is a resty client for making requests to the http client.
type RestyClient struct {
client *resty.Client
client *resty.Client
handler metrics.Handler
}

// NewRestyClient creates a resty client.
// while much slower than fasthttp, this client requests context cancellation.
func NewRestyClient() Client {
return &RestyClient{client: resty.New()}
func NewRestyClient(handler metrics.Handler) Client {
return &RestyClient{client: resty.New(), handler: handler}
}

type restyRequest struct {
*resty.Request
endpoint string
handler metrics.Handler
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Initialize 'handler' to prevent nil pointer dereference

At line 28, the handler field is added to the restyRequest struct but may not be initialized by default. If WithMetrics() is not called before other methods, handler will be nil. This can lead to a nil pointer dereference when methods like SetHeaderBytes, SetBody, SetContext, SetHeader, SetRequestURI, and Do attempt to use r.handler.

Consider ensuring that handler is properly initialized. You could initialize handler with a default metrics.Handler in the NewRequest() method or enforce that WithMetrics() must be called before any other method by adding checks or documentation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential nil pointer dereference of r.handler in tracing methods

The handler field in restyRequest may be nil if WithMetrics is not called before using methods like SetHeaderBytes, SetBody, SetContext, SetHeader, SetRequestURI, and Do. This could lead to a nil pointer dereference when calling r.handler.Tracer().Start().

To prevent this, ensure that handler is initialized before use. You can initialize handler with a default metrics handler when creating a new request.

Example fix by initializing handler in NewRequest:

func (r RestyClient) NewRequest() Request {
	return &restyRequest{
		Request: r.client.R(),
+		handler: metrics.NoOpHandler(),
	}
}

Alternatively, add a nil check in each method before starting the trace:

func (r *restyRequest) SetHeaderBytes(key, value []byte) Request {
+	if r.handler == nil {
+		return r
+	}
	_, span := r.handler.Tracer().Start(
		r.Request.Context(),
		"SetHeaderBytes",
		trace.WithAttributes(
			attribute.String("key", common.Bytes2Hex(key)),
			attribute.String("value", common.Bytes2Hex(value)),
		))
	defer func() {
		metrics.EndSpan(span)
	}()
	r.Request.SetHeader(string(key), string(value))
	return r
}

Also applies to: 40-49, 55-62, 68-75, 81-90, 96-103, 108-115

}

// NewRequest create a new request.
func (r RestyClient) NewRequest() Request {
return &restyRequest{
Request: r.client.R(),
handler: r.handler,
}
}

Expand Down Expand Up @@ -54,9 +62,24 @@
return r
}

func (r *restyRequest) Do() (Response, error) {
//nolint: wrapcheck
return r.Request.Post(r.endpoint)
func (r *restyRequest) Do() (_ Response, err error) {
_, span := r.handler.Tracer().Start(
r.Request.Context(),
"Do",
trace.WithAttributes(
attribute.String("uri", r.endpoint),
attribute.String("headers", fmt.Sprintf("%v", r.Request.Header)),
attribute.String("body", r.Request.Body.(string)),
),
)
defer func() {
metrics.EndSpanWithErr(span, err)
}()
resp, err := r.Request.Post(r.endpoint)
if err != nil {
return nil, fmt.Errorf("could not get response from %s: %w", r.endpoint, err)
}

Check warning on line 81 in services/omnirpc/http/resty.go

View check run for this annotation

Codecov / codecov/patch

services/omnirpc/http/resty.go#L80-L81

Added lines #L80 - L81 were not covered by tests
return resp, nil
Comment on lines +65 to +82
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

⚠️ Potential issue

Security Issues Detected in Do Method

The Do method in services/omnirpc/http/resty.go logs sensitive information such as uri, headers, and body using trace.WithAttributes. This practice can expose sensitive data and pose security risks.

Issues:

  • Sensitive Data Logging: Logging headers and body can leak confidential information.
  • Endpoint Exposure: Including the uri in error messages may reveal sensitive endpoint details.

Recommendations:

  1. Sanitize or Remove Sensitive Information from Trace Attributes:

    trace.WithAttributes(
        attribute.String("uri", sanitizeURI(r.endpoint)),
        attribute.String("headers", "[REDACTED]"),
        attribute.String("body_length", fmt.Sprintf("%d", len(r.Request.Body.(string)))),
    ),
  2. Sanitize the Endpoint in Error Messages:

    return nil, fmt.Errorf("could not get response from [REDACTED]: %w", err)
  3. Validate Endpoint Before Making Requests:

    if r.endpoint == "" {
        return nil, fmt.Errorf("endpoint is not set")
    }
🔗 Analysis chain

LGTM with security concerns: Do method updated with tracing and improved error handling

The Do method has been updated with tracing functionality and improved error handling, which are good improvements. However, there are some security concerns to address:

  1. Logging sensitive information: The method is logging headers and the entire request body as trace attributes. This could potentially expose sensitive data.

  2. Error message: The error message includes the endpoint, which might contain sensitive information.

Consider the following improvements:

  1. Sanitize or remove sensitive information from trace attributes:

    trace.WithAttributes(
        attribute.String("uri", sanitizeURI(r.endpoint)),
        attribute.String("headers", "[REDACTED]"),
        attribute.String("body_length", fmt.Sprintf("%d", len(r.Request.Body.(string)))),
    ),
  2. Sanitize the endpoint in the error message:

    return nil, fmt.Errorf("could not get response from [REDACTED]: %w", err)
  3. Consider adding a check for r.endpoint being empty before making the request.

To verify the usage of sensitive information in tracing, run the following script:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential logging of sensitive information in tracing

# Test: Search for trace.WithAttributes calls and check for potential sensitive data
rg --type go -e 'trace\.WithAttributes' -A 5 | grep -E '(headers|body|uri)'

Length of output: 1915


Script:

#!/bin/bash
# Description: Identify instances of trace.WithAttributes that log headers, body, or uri in Go files.

# Search for 'trace.WithAttributes' in Go files and filter for 'headers', 'body', or 'uri'
rg --type go 'trace\.WithAttributes' | grep -E '(headers|body|uri)'

Length of output: 983

}

var _ Client = &RestyClient{}
28 changes: 26 additions & 2 deletions services/omnirpc/http/suite_test.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
package http_test

import (
"testing"

"github.com/brianvoe/gofakeit/v6"
"github.com/stretchr/testify/suite"
"github.com/synapsecns/sanguine/core"
"github.com/synapsecns/sanguine/core/config"
"github.com/synapsecns/sanguine/core/metrics"
"github.com/synapsecns/sanguine/core/metrics/localmetrics"
"github.com/synapsecns/sanguine/core/testsuite"
"github.com/synapsecns/sanguine/services/omnirpc/http"
"testing"
)

var buildInfo = config.NewBuildInfo(config.DefaultVersion, config.DefaultCommit, "omnirpc", config.DefaultDate)

// clientSuite defines the basic test suite.
type HTTPSuite struct {
*testsuite.TestSuite
clients []http.Client
metrics metrics.Handler
}

// NewTestSuite creates a new test suite and performs some basic checks afterward.
Expand All @@ -27,8 +35,24 @@ func (c *HTTPSuite) SetupTest() {
c.TestSuite.SetupTest()

for _, clientType := range http.AllClientTypes {
c.clients = append(c.clients, http.NewClient(clientType))
c.clients = append(c.clients, http.NewClient(metrics.NewNullHandler(), clientType))
}
Comment on lines +38 to +39
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use the initialized metrics handler when creating clients

In SetupTest(), the clients are being initialized with metrics.NewNullHandler(), while the metrics handler is set up and stored in c.metrics during SetupSuite(). To ensure consistency and that the clients use the appropriate metrics handler based on the test environment, consider using c.metrics when initializing the clients.

Apply this diff to use the metrics handler stored in c.metrics:

39-			c.clients = append(c.clients, http.NewClient(metrics.NewNullHandler(), clientType))
39+			c.clients = append(c.clients, http.NewClient(c.metrics, clientType))
40		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
c.clients = append(c.clients, http.NewClient(metrics.NewNullHandler(), clientType))
}
c.clients = append(c.clients, http.NewClient(c.metrics, clientType))
}

}

func (c *HTTPSuite) SetupSuite() {
c.TestSuite.SetupSuite()
// don't use metrics on ci for integration tests
isCI := core.GetEnvBool("CI", false)
useMetrics := !isCI
metricsHandler := metrics.Null

if useMetrics {
localmetrics.SetupTestJaeger(c.GetSuiteContext(), c.T())
metricsHandler = metrics.Jaeger
}
var err error
c.metrics, err = metrics.NewByType(c.GetSuiteContext(), buildInfo, metricsHandler)
c.Require().NoError(err)
}

func TestCommonSuite(t *testing.T) {
Expand Down
Loading
Loading