Skip to content

Commit 3ed6f62

Browse files
authored
DF-23700 fix for absent protocol (just host) (#106)
1 parent b350dca commit 3ed6f62

3 files changed

Lines changed: 76 additions & 57 deletions

File tree

metrics/client.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ type RPCClientMetrics interface {
4040
// RecordRequest records latency for an RPC call (observed in nanoseconds for Prometheus and Beholder).
4141
// Failures use success="false"; derive error rate from rpc_call_latency_count{success="false"} (or equivalent).
4242
// rpcURL is sanitized before export (userinfo and query removed; path hashed).
43-
RecordRequest(ctx context.Context, rpcURL string, isSendOnly bool, callName string, latency time.Duration, err error)
43+
RecordRequest(ctx context.Context, rpcDomain string, isSendOnly bool, callName string, latency time.Duration, err error)
4444
}
4545

4646
var _ RPCClientMetrics = (*rpcClientMetrics)(nil)
@@ -74,11 +74,11 @@ func NewRPCClientMetrics(cfg RPCClientMetricsConfig) (RPCClientMetrics, error) {
7474
}, nil
7575
}
7676

77-
func (m *rpcClientMetrics) RecordRequest(ctx context.Context, rpcURL string, isSendOnly bool, callName string, latency time.Duration, err error) {
77+
func (m *rpcClientMetrics) RecordRequest(ctx context.Context, rpcDomain string, isSendOnly bool, callName string, latency time.Duration, err error) {
7878
successStr := strconv.FormatBool(err != nil)
7979
sendStr := strconv.FormatBool(isSendOnly)
8080
latencyNs := float64(latency)
81-
safeRPCURL := SanitizeRPCURL(rpcURL)
81+
safeRPCURL := SanitizeRPCURL(rpcDomain)
8282

8383
RPCCallLatency.WithLabelValues(
8484
m.chainFamily, m.chainID, safeRPCURL, sendStr, successStr, callName,
@@ -87,10 +87,10 @@ func (m *rpcClientMetrics) RecordRequest(ctx context.Context, rpcURL string, isS
8787
m.latencyHis.Record(ctx, latencyNs/float64(time.Millisecond), metric.WithAttributes(
8888
attribute.String("chainFamily", m.chainFamily),
8989
attribute.String("chainID", m.chainID),
90-
attribute.String("rpcUrl", safeRPCURL),
90+
attribute.String("rpcDomain", safeRPCURL),
9191
attribute.String("isSendOnly", sendStr),
9292
attribute.String("success", successStr),
93-
attribute.String("rpcCallName", callName),
93+
attribute.String("callName", callName),
9494
))
9595
}
9696

metrics/sanitize_rpc_url.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ import (
99

1010
// SanitizeRPCURL either strips user:passwd or replaces path and params with their sha1-hex, excluding leading / if present
1111
func SanitizeRPCURL(raw string) string {
12+
// url.Parse requires a scheme to correctly populate Host.
13+
// When none is present, prepend a temporary one and strip it afterwards.
14+
// TrimPrefix below is always safe: no real scheme starts with fakeScheme.
15+
const fakeScheme = "x://"
16+
if !strings.Contains(raw, "://") {
17+
raw = fakeScheme + raw
18+
}
19+
1220
u, err := url.Parse(raw)
1321
if err != nil {
1422
return "invalid_rpc_url"
@@ -17,7 +25,7 @@ func SanitizeRPCURL(raw string) string {
1725
if u.User != nil {
1826
// Strip credentials and leave everything else intact.
1927
u.User = nil
20-
return u.String()
28+
return strings.TrimPrefix(u.String(), fakeScheme)
2129
}
2230

2331
// Build the sensitive portion: path (without leading /) plus optional query.
@@ -32,12 +40,12 @@ func SanitizeRPCURL(raw string) string {
3240

3341
if sensitive == "" {
3442
// Nothing to redact.
35-
return u.String()
43+
return strings.TrimPrefix(u.String(), fakeScheme)
3644
}
3745

3846
//nolint:gosec
3947
h := sha1.Sum([]byte(sensitive))
4048
u.Path = "/" + fmt.Sprintf("%x", h)
4149
u.RawQuery = ""
42-
return u.String()
50+
return strings.TrimPrefix(u.String(), fakeScheme)
4351
}

metrics/sanitize_rpc_url_test.go

Lines changed: 60 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -6,59 +6,70 @@ import (
66
"github.com/stretchr/testify/assert"
77
)
88

9-
func TestSanitizeRPCURL_RedactsSecrets(t *testing.T) {
10-
cases := []struct {
11-
input string
12-
want string
13-
}{
14-
// simple path
15-
{"https://bsc-mainnet.core.chainstack.com/MjcwMDk3ZGFhMDA5NjJjMDM1", "https://bsc-mainnet.core.chainstack.com/b0b99e8b33b401b05251f91aec08b6a9581c86dd"},
16-
// less simple path
17-
{"http://172.16.156.14:8000/MmZmNTJmOWRiNzg0NTgxNDYyNzJjMTYzNDlmNGJ/iYWEwOTVmYWE0OQ/bsc/mainnet/", "http://172.16.156.14:8000/bd161d616d46b90a248de0f0a3ebc2daf2b8bb20"},
18-
// path with no / is excluded from sha
19-
{"https://anyblocks-01.mainnet.bnb.bdnodes.net?auth=MDcwMTgzODk3NzIyMjU4YzY2MTQzNGMyNTU2OWE2NGEzYjhlODM0NA", "https://anyblocks-01.mainnet.bnb.bdnodes.net/7c87697e63d8f9c049183bb4f8c171af40715b2b"},
20-
// path with leading / is included in sha
21-
{"https://anyblocks-02.mainnet.bnb.bdnodes.net/somepath/?auth=2Dc8bNAqCC0X74zZfi_4ra6XzuBY8lmXcTE1ic9EO5o", "https://anyblocks-02.mainnet.bnb.bdnodes.net/22c028ea2d53fd106e2bb93bc61d838ed6b01c19"},
22-
// strip creds keep path
23-
{"https://myLittleNop:YjY5MjAwOGJkMzBjNW@broadcast-mirror.fiews.io/?chain_id=56", "https://broadcast-mirror.fiews.io/?chain_id=56"},
24-
// even if no creds, sacrifice path for uniformity
25-
{"https://eu-bsc.rpc.linkriver.internal/rpc", "https://eu-bsc.rpc.linkriver.internal/e64b40f2bd5c8a9560773d16476a86ede7e7c1ba"},
26-
// keeps protocol too
27-
{"wss://bsc-mainnet-proxy.internal.linkpool.io/ws", "wss://bsc-mainnet-proxy.internal.linkpool.io/1457b75dc8c5500c0f1d4503cf801b60deb045a4"},
28-
}
9+
var toSanitizeCases = []struct {
10+
input string
11+
want string
12+
}{
13+
// simple path
14+
{"bsc-mainnet.core.chainstack.com/MjcwMDk3ZGFhMDA5NjJjMDM1", "bsc-mainnet.core.chainstack.com/b0b99e8b33b401b05251f91aec08b6a9581c86dd"},
15+
// less simple path
16+
{"172.16.156.14:8000/MmZmNTJmOWRiNzg0NTgxNDYyNzJjMTYzNDlmNGJ/iYWEwOTVmYWE0OQ/bsc/mainnet/", "172.16.156.14:8000/bd161d616d46b90a248de0f0a3ebc2daf2b8bb20"},
17+
// path with no / is excluded from sha
18+
{"anyblocks-01.mainnet.bnb.bdnodes.net?auth=MDcwMTgzODk3NzIyMjU4YzY2MTQzNGMyNTU2OWE2NGEzYjhlODM0NA", "anyblocks-01.mainnet.bnb.bdnodes.net/7c87697e63d8f9c049183bb4f8c171af40715b2b"},
19+
// path with leading / is included in sha
20+
{"anyblocks-02.mainnet.bnb.bdnodes.net/somepath/?auth=2Dc8bNAqCC0X74zZfi_4ra6XzuBY8lmXcTE1ic9EO5o", "anyblocks-02.mainnet.bnb.bdnodes.net/22c028ea2d53fd106e2bb93bc61d838ed6b01c19"},
21+
// strip creds keep path
22+
{"myLittleNop:YjY5MjAwOGJkMzBjNW@broadcast-mirror.fiews.io/?chain_id=56", "broadcast-mirror.fiews.io/?chain_id=56"},
23+
// even if no creds, sacrifice path for uniformity
24+
{"eu-bsc.rpc.linkriver.internal/rpc", "eu-bsc.rpc.linkriver.internal/e64b40f2bd5c8a9560773d16476a86ede7e7c1ba"},
25+
// keeps protocol too
26+
{"bsc-mainnet-proxy.internal.linkpool.io/ws", "bsc-mainnet-proxy.internal.linkpool.io/1457b75dc8c5500c0f1d4503cf801b60deb045a4"},
27+
}
2928

30-
for _, tc := range cases {
31-
t.Run(tc.input, func(t *testing.T) {
32-
assert.Equal(t, tc.want, SanitizeRPCURL(tc.input))
33-
})
34-
}
29+
var alreadySanitizedCases = []string{
30+
"10.0.1.191:8545",
31+
"144.178.241.22:8545",
32+
"222.106.187.14:12001",
33+
"at2-bsc-main03.blockchain.fiews.net:8545",
34+
"berlioz.stakesystems.io:8745",
35+
"blockchains-1.shultzpro.com:8545",
36+
"dfw3-bsc-main01.blockchain.fiews.net:8545",
37+
"sylvester.stakesystems.io:8745",
38+
"bsc-dataseed.binance.org/",
39+
"chainlink-bsc.rpc.blxrbdn.com",
40+
"puissant-builder.48.club",
41+
"10.0.1.191:8546",
42+
"144.76.108.206:8546",
43+
"172.16.152.140:8546",
44+
"bsc-rpc-2.piertwo.prod:8546",
45+
"bsc.rpc.cinternal.com",
46+
"sylvester.stakesystems.io:8746",
47+
"bsc-rpc.o1.wtf",
3548
}
3649

37-
func TestSanitizeRPCURL_AlreadySanitized(t *testing.T) {
38-
urls := []string{
39-
"http://10.0.1.191:8545",
40-
"http://144.178.241.22:8545",
41-
"http://222.106.187.14:12001",
42-
"http://at2-bsc-main03.blockchain.fiews.net:8545",
43-
"http://berlioz.stakesystems.io:8745",
44-
"http://blockchains-1.shultzpro.com:8545",
45-
"http://dfw3-bsc-main01.blockchain.fiews.net:8545",
46-
"http://sylvester.stakesystems.io:8745",
47-
"https://bsc-dataseed.binance.org/",
48-
"https://chainlink-bsc.rpc.blxrbdn.com",
49-
"https://puissant-builder.48.club",
50-
"ws://10.0.1.191:8546",
51-
"ws://144.76.108.206:8546",
52-
"ws://172.16.152.140:8546",
53-
"ws://bsc-rpc-2.piertwo.prod:8546",
54-
"ws://bsc.rpc.cinternal.com",
55-
"ws://sylvester.stakesystems.io:8746",
56-
"wss://bsc-rpc.o1.wtf",
50+
var protocolParts = []string{
51+
"wss://",
52+
"https://",
53+
"http://",
54+
"",
55+
}
56+
57+
func TestSanitizeRPCURL_RedactsSecrets(t *testing.T) {
58+
for _, prefix := range protocolParts {
59+
for _, tc := range toSanitizeCases {
60+
t.Run(tc.input, func(t *testing.T) {
61+
assert.Equal(t, prefix+tc.want, SanitizeRPCURL(prefix+tc.input))
62+
})
63+
}
5764
}
65+
}
5866

59-
for _, u := range urls {
60-
t.Run(u, func(t *testing.T) {
61-
assert.Equal(t, u, SanitizeRPCURL(u))
62-
})
67+
func TestSanitizeRPCURL_AlreadySanitized(t *testing.T) {
68+
for _, prefix := range protocolParts {
69+
for _, u := range alreadySanitizedCases {
70+
t.Run(u, func(t *testing.T) {
71+
assert.Equal(t, prefix+u, SanitizeRPCURL(prefix+u))
72+
})
73+
}
6374
}
6475
}

0 commit comments

Comments
 (0)