diff --git a/Makefile b/Makefile index b1ac2864a..a2bd33b70 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ ################## update dependencies #################### -ETHEREUM_SUBMODULE_COMMIT_OR_TAG := morph-v2.2.4 -ETHEREUM_TARGET_VERSION := morph-v2.2.4 +ETHEREUM_SUBMODULE_COMMIT_OR_TAG := morph-v2.2.5 +ETHEREUM_TARGET_VERSION := morph-v2.2.5 TENDERMINT_TARGET_VERSION := v0.3.9 diff --git a/bindings/go.mod b/bindings/go.mod index 19872337b..0bcd4b1b7 100644 --- a/bindings/go.mod +++ b/bindings/go.mod @@ -4,7 +4,7 @@ go 1.24.0 replace github.com/tendermint/tendermint => github.com/morph-l2/tendermint v0.3.9 -require github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3 +require github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe require ( github.com/VictoriaMetrics/fastcache v1.12.2 // indirect diff --git a/bindings/go.sum b/bindings/go.sum index a40552135..a817ef06e 100644 --- a/bindings/go.sum +++ b/bindings/go.sum @@ -111,6 +111,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3 h1:vt1tCsMO0eFZQsNnI3KRk76V52ia19Z/Zbpk92x0w3Y= github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3/go.mod h1:nkVzHjQWCOjvukQW8ittlwX+Xz9gmVHrP7mUi7zoHTs= +github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe h1:dYfgE9WDq58hiU0V0LmFkr2vUesbFaexyXfvirBVuEQ= +github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe/go.mod h1:nkVzHjQWCOjvukQW8ittlwX+Xz9gmVHrP7mUi7zoHTs= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= diff --git a/common/batch/blob.go b/common/batch/blob.go index 9ecdfd27b..74d977341 100644 --- a/common/batch/blob.go +++ b/common/batch/blob.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "fmt" "io" + "math" "morph-l2/common/codec/zstd" @@ -242,11 +243,22 @@ func extractInnerTxFullBytes(firstByte byte, reader io.Reader) ([]byte, error) { return nil, fmt.Errorf("declared tx size %d exceeds remaining %d bytes", size, lr.Len()) } + // Guard the reconstructed length against uint32 overflow before allocating. + // size is a uint32, so 1+sizeByteLen+size can exceed MaxUint32 and wrap to a + // tiny value, leaving fullTxBytes shorter than the copies below and + // panicking. The remaining-bytes guard above keeps this unreachable today, + // but computing in uint64 and rejecting overflow keeps it safe if the size + // type or the upstream bounds ever change. + fullLen := 1 + uint64(sizeByteLen) + uint64(size) + if fullLen > math.MaxUint32 { + return nil, fmt.Errorf("declared tx size %d overflows uint32 length prefix", size) + } + txRaw := make([]byte, size) if err := binary.Read(reader, binary.BigEndian, txRaw); err != nil { return nil, err } - fullTxBytes := make([]byte, 1+uint32(sizeByteLen)+size) + fullTxBytes := make([]byte, fullLen) copy(fullTxBytes[:1], []byte{firstByte}) copy(fullTxBytes[1:1+sizeByteLen], sizeByte) copy(fullTxBytes[1+sizeByteLen:], txRaw) diff --git a/common/go.mod b/common/go.mod index 428712e6a..24b183ef7 100644 --- a/common/go.mod +++ b/common/go.mod @@ -6,7 +6,7 @@ replace github.com/tendermint/tendermint => github.com/morph-l2/tendermint v0.3. require ( github.com/holiman/uint256 v1.2.4 - github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3 + github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe github.com/morph-l2/morph-da-codec/bindings/codec v0.0.0-20260727095527-681727561e38 github.com/stretchr/testify v1.10.0 github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a diff --git a/common/go.sum b/common/go.sum index aa27e8857..0aeb34a38 100644 --- a/common/go.sum +++ b/common/go.sum @@ -150,6 +150,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3 h1:vt1tCsMO0eFZQsNnI3KRk76V52ia19Z/Zbpk92x0w3Y= github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3/go.mod h1:nkVzHjQWCOjvukQW8ittlwX+Xz9gmVHrP7mUi7zoHTs= +github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe h1:dYfgE9WDq58hiU0V0LmFkr2vUesbFaexyXfvirBVuEQ= +github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe/go.mod h1:nkVzHjQWCOjvukQW8ittlwX+Xz9gmVHrP7mUi7zoHTs= github.com/morph-l2/morph-da-codec/bindings/codec v0.0.0-20260727095527-681727561e38 h1:2OIgRP51TPMlpw9E82toxu/SQzN1hlYcxpJKC9jB0cs= github.com/morph-l2/morph-da-codec/bindings/codec v0.0.0-20260727095527-681727561e38/go.mod h1:f6+BHrrHbRmEYplC1nmNjGdatQtMSa2CIK7CX8T1Nfc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= diff --git a/contracts/go.mod b/contracts/go.mod index 6fc5973a9..bd76846dc 100644 --- a/contracts/go.mod +++ b/contracts/go.mod @@ -6,7 +6,7 @@ replace github.com/tendermint/tendermint => github.com/morph-l2/tendermint v0.3. require ( github.com/iden3/go-iden3-crypto v0.0.16 - github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3 + github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe github.com/stretchr/testify v1.10.0 ) diff --git a/contracts/go.sum b/contracts/go.sum index 76c0f803c..2ebb50061 100644 --- a/contracts/go.sum +++ b/contracts/go.sum @@ -138,6 +138,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3 h1:vt1tCsMO0eFZQsNnI3KRk76V52ia19Z/Zbpk92x0w3Y= github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3/go.mod h1:nkVzHjQWCOjvukQW8ittlwX+Xz9gmVHrP7mUi7zoHTs= +github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe h1:dYfgE9WDq58hiU0V0LmFkr2vUesbFaexyXfvirBVuEQ= +github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe/go.mod h1:nkVzHjQWCOjvukQW8ittlwX+Xz9gmVHrP7mUi7zoHTs= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= diff --git a/go-ethereum b/go-ethereum index ffa95e067..5a13ad4d7 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit ffa95e0670a39ad6e6484ece5d664cf02941e8e4 +Subproject commit 5a13ad4d76fee04d9b9c35593973bf4c291bc6ed diff --git a/node/derivation/beacon.go b/node/derivation/beacon.go index f5b85f40a..311f95926 100644 --- a/node/derivation/beacon.go +++ b/node/derivation/beacon.go @@ -223,20 +223,39 @@ func dataAndHashesFromTxs(txs types.Transactions, targetTx *types.Transaction) [ // eliminated by running derivation with confirmations=finalized, not by // trying more beacons. type FallbackBeaconClient struct { - clients []*L1BeaconClient - endpoints []string // parallel to clients, used only for logs/metrics + clients []*L1BeaconClient + // endpoints is parallel to clients and used only for logs/metrics; entries + // are pre-redacted to scheme://host so credentials embedded in the + // configured URLs never reach the metrics endpoint or log output. + endpoints []string log tmlog.Logger metrics *Metrics } +// redactBeaconEndpoint reduces a beacon URL to scheme://host for use in +// metrics labels and logs. Hosted beacon providers commonly embed basic-auth +// userinfo or API keys in the URL (https://user:pass@host, ?apikey=...); +// exposing the raw string on /metrics or in aggregated logs would leak them. +// An endpoint that does not parse as an absolute URL collapses to a +// placeholder rather than echoing the raw string. +func redactBeaconEndpoint(raw string) string { + parsed, err := url.Parse(raw) + if err != nil || parsed.Scheme == "" || parsed.Host == "" { + return "" + } + return parsed.Scheme + "://" + parsed.Host +} + func NewFallbackBeaconClient(endpoints []string, log tmlog.Logger, metrics *Metrics) *FallbackBeaconClient { clients := make([]*L1BeaconClient, 0, len(endpoints)) + redacted := make([]string, 0, len(endpoints)) for _, endpoint := range endpoints { clients = append(clients, NewL1BeaconClient(NewBasicHTTPClient(endpoint, log))) + redacted = append(redacted, redactBeaconEndpoint(endpoint)) } return &FallbackBeaconClient{ clients: clients, - endpoints: endpoints, + endpoints: redacted, log: log, metrics: metrics, } diff --git a/node/derivation/beacon_test.go b/node/derivation/beacon_test.go index 12e2dbaf7..aac90823f 100644 --- a/node/derivation/beacon_test.go +++ b/node/derivation/beacon_test.go @@ -55,6 +55,26 @@ func TestGetBlob(t *testing.T) { } +func TestRedactBeaconEndpoint(t *testing.T) { + cases := []struct { + name string + in string + want string + }{ + {"plain", "https://beacon.example.com", "https://beacon.example.com"}, + {"basic auth stripped", "https://user:secret@beacon.example.com:5052", "https://beacon.example.com:5052"}, + {"api key in path and query stripped", "https://beacon.example.com/eth/key-abc123?apikey=secret", "https://beacon.example.com"}, + {"not a url", "not a url", ""}, + {"missing scheme", "beacon.example.com:5052", ""}, + {"empty", "", ""}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + require.Equal(t, tc.want, redactBeaconEndpoint(tc.in)) + }) + } +} + func testTchRollupLog(l1Client *ethclient.Client, ctx context.Context, from, to uint64) ([]eth.Log, error) { RollupContractAddress := common.HexToAddress("0x511d92b63ae7471fd5239bded29b76a446698a00") query := ethereum.FilterQuery{ diff --git a/node/derivation/config.go b/node/derivation/config.go index fb010bab3..a3f194ba8 100644 --- a/node/derivation/config.go +++ b/node/derivation/config.go @@ -195,6 +195,13 @@ func (c *Config) SetCliContext(ctx *cli.Context) error { if c.VerifyMode == VerifyModeLayer1 { c.MetricsPort = ctx.GlobalUint64(flags.MetricsPort.Name) + // Reject an out-of-range port up front: MetricsPort is a uint64, so a + // misconfigured value would only surface later as an invalid listen + // address whose ListenAndServe fails silently in the background. The + // default (26660) is always in range, so a valid config never trips this. + if c.MetricsPort == 0 || c.MetricsPort > 65535 { + return fmt.Errorf("--%s must be in 1..65535, got %d", flags.MetricsPort.Name, c.MetricsPort) + } } if ctx.GlobalIsSet(flags.DerivationReorgCheckDepth.Name) { diff --git a/node/derivation/config_test.go b/node/derivation/config_test.go index 8701196a5..4148c0c96 100644 --- a/node/derivation/config_test.go +++ b/node/derivation/config_test.go @@ -90,6 +90,32 @@ func TestVerifyMode_RejectsUnknown(t *testing.T) { } } +func TestMetricsPort_AcceptsDefaultInLayer1(t *testing.T) { + cfg := DefaultConfig() + if err := cfg.SetCliContext(newVerifyModeTestContext(t, map[string]string{ + flags.DerivationVerifyMode.Name: VerifyModeLayer1, + })); err != nil { + t.Fatalf("layer1 with default metrics-port rejected: %v", err) + } + if cfg.MetricsPort != 26660 { + t.Fatalf("default metrics-port = %d, want 26660", cfg.MetricsPort) + } +} + +func TestMetricsPort_RejectsOutOfRange(t *testing.T) { + cfg := DefaultConfig() + err := cfg.SetCliContext(newVerifyModeTestContext(t, map[string]string{ + flags.DerivationVerifyMode.Name: VerifyModeLayer1, + flags.MetricsPort.Name: "70000", + })) + if err == nil { + t.Fatal("out-of-range metrics-port accepted, want error") + } + if !strings.Contains(err.Error(), flags.MetricsPort.Name) { + t.Fatalf("error should mention %q; got: %v", flags.MetricsPort.Name, err) + } +} + func TestBeaconRpcList(t *testing.T) { for _, tc := range []struct { name string @@ -126,6 +152,7 @@ func newVerifyModeTestContext(t *testing.T, values map[string]string) *cli.Conte for _, f := range []cli.Flag{ flags.LegacyValidatorMode, flags.DerivationVerifyMode, + flags.MetricsPort, flags.L1BeaconAddr, flags.L2EngineJWTSecret, } { diff --git a/node/derivation/metrics.go b/node/derivation/metrics.go index 57e180518..13d7c1559 100644 --- a/node/derivation/metrics.go +++ b/node/derivation/metrics.go @@ -134,7 +134,7 @@ func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics { Namespace: namespace, Subsystem: metricsSubsystem, Name: "beacon_request_failure_total", - Help: "Beacon requests that failed (transport error or non-200) per endpoint, before falling back to the next configured beacon.", + Help: "Beacon requests that failed (transport error, non-200, or an empty/incomplete sidecar set) per endpoint, before falling back to the next configured beacon.", }, append(append([]string{}, labels...), "endpoint")), } } diff --git a/node/go.mod b/node/go.mod index ac07516e6..fd46c9d89 100644 --- a/node/go.mod +++ b/node/go.mod @@ -13,7 +13,7 @@ require ( github.com/hashicorp/raft v1.7.3 github.com/hashicorp/raft-boltdb/v2 v2.3.1 github.com/mdlayher/vsock v1.2.1 - github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3 + github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.17.0 github.com/spf13/viper v1.13.0 diff --git a/node/go.sum b/node/go.sum index 0abe60095..3751187b9 100644 --- a/node/go.sum +++ b/node/go.sum @@ -416,6 +416,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3 h1:vt1tCsMO0eFZQsNnI3KRk76V52ia19Z/Zbpk92x0w3Y= github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3/go.mod h1:nkVzHjQWCOjvukQW8ittlwX+Xz9gmVHrP7mUi7zoHTs= +github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe h1:dYfgE9WDq58hiU0V0LmFkr2vUesbFaexyXfvirBVuEQ= +github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe/go.mod h1:nkVzHjQWCOjvukQW8ittlwX+Xz9gmVHrP7mUi7zoHTs= github.com/morph-l2/tendermint v0.3.9 h1:0jiFtMpVmVImCAFrMFTIvTX7jfJwzaQFyLQoLccP5A8= github.com/morph-l2/tendermint v0.3.9/go.mod h1:qpiwqfcCB89dBYfqVJOc/HjGxDp3OdDlthgttJJYyRs= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= diff --git a/ops/l2-genesis/go.mod b/ops/l2-genesis/go.mod index fa159f34e..df4e254c3 100644 --- a/ops/l2-genesis/go.mod +++ b/ops/l2-genesis/go.mod @@ -6,7 +6,7 @@ replace github.com/tendermint/tendermint => github.com/morph-l2/tendermint v0.3. require ( github.com/holiman/uint256 v1.2.4 - github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3 + github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe github.com/stretchr/testify v1.10.0 github.com/urfave/cli v1.22.17 ) diff --git a/ops/l2-genesis/go.sum b/ops/l2-genesis/go.sum index 66d15c94a..b95d65ce4 100644 --- a/ops/l2-genesis/go.sum +++ b/ops/l2-genesis/go.sum @@ -141,6 +141,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3 h1:vt1tCsMO0eFZQsNnI3KRk76V52ia19Z/Zbpk92x0w3Y= github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3/go.mod h1:nkVzHjQWCOjvukQW8ittlwX+Xz9gmVHrP7mUi7zoHTs= +github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe h1:dYfgE9WDq58hiU0V0LmFkr2vUesbFaexyXfvirBVuEQ= +github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe/go.mod h1:nkVzHjQWCOjvukQW8ittlwX+Xz9gmVHrP7mUi7zoHTs= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= diff --git a/ops/tools/go.mod b/ops/tools/go.mod index 3c443ebcd..bdfbc2264 100644 --- a/ops/tools/go.mod +++ b/ops/tools/go.mod @@ -5,7 +5,7 @@ go 1.24.0 replace github.com/tendermint/tendermint => github.com/morph-l2/tendermint v0.3.9 require ( - github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3 + github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe github.com/tendermint/tendermint v0.35.9 ) diff --git a/ops/tools/go.sum b/ops/tools/go.sum index 6baf49ae4..1fc284ad2 100644 --- a/ops/tools/go.sum +++ b/ops/tools/go.sum @@ -163,6 +163,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3 h1:vt1tCsMO0eFZQsNnI3KRk76V52ia19Z/Zbpk92x0w3Y= github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3/go.mod h1:nkVzHjQWCOjvukQW8ittlwX+Xz9gmVHrP7mUi7zoHTs= +github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe h1:dYfgE9WDq58hiU0V0LmFkr2vUesbFaexyXfvirBVuEQ= +github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe/go.mod h1:nkVzHjQWCOjvukQW8ittlwX+Xz9gmVHrP7mUi7zoHTs= github.com/morph-l2/tendermint v0.3.9 h1:0jiFtMpVmVImCAFrMFTIvTX7jfJwzaQFyLQoLccP5A8= github.com/morph-l2/tendermint v0.3.9/go.mod h1:qpiwqfcCB89dBYfqVJOc/HjGxDp3OdDlthgttJJYyRs= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= diff --git a/token-price-oracle/client/cex_feed.go b/token-price-oracle/client/cex_feed.go index a2102d946..4848ec43b 100644 --- a/token-price-oracle/client/cex_feed.go +++ b/token-price-oracle/client/cex_feed.go @@ -22,6 +22,12 @@ const ( okxTickerPath = "/api/v5/market/ticker" ) +// maxResponseBodyBytes caps how much of an HTTP price response is read into memory. +// Ticker and Hermes latest-price payloads are a few KB at most; the cap only exists +// so a compromised or misbehaving endpoint cannot stream an unbounded body and +// exhaust memory. +const maxResponseBodyBytes = 1 << 20 // 1 MiB + type cexPriceFetcher func(ctx context.Context, httpClient *http.Client, baseURL string, symbol string) (*big.Float, error) // CEXPriceFeed fetches token prices from a centralized exchange REST API. @@ -243,10 +249,13 @@ func getJSONWithHeaders(ctx context.Context, httpClient *http.Client, requestURL } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBodyBytes+1)) if err != nil { return nil, fmt.Errorf("failed to read response body: %w", err) } + if int64(len(body)) > maxResponseBodyBytes { + return nil, fmt.Errorf("response body exceeds %d byte limit", maxResponseBodyBytes) + } if resp.StatusCode < 200 || resp.StatusCode >= 300 { return nil, fmt.Errorf("HTTP status %d: %s", resp.StatusCode, string(body)) } diff --git a/token-price-oracle/client/cex_feed_test.go b/token-price-oracle/client/cex_feed_test.go index f70028d81..ad41a9c33 100644 --- a/token-price-oracle/client/cex_feed_test.go +++ b/token-price-oracle/client/cex_feed_test.go @@ -100,6 +100,38 @@ func TestFetchOKXPrice(t *testing.T) { } } +func TestGetJSONRejectsOversizedBody(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + // Stream more than the cap so a compromised/misbehaving endpoint cannot + // force an unbounded allocation. + oversized := make([]byte, maxResponseBodyBytes+1) + w.Write(oversized) + })) + defer server.Close() + + if _, err := getJSON(context.Background(), server.Client(), server.URL); err == nil { + t.Fatal("getJSON accepted an oversized response body, want error") + } +} + +func TestGetJSONAcceptsBodyAtLimit(t *testing.T) { + payload := `{"symbol":"BTCUSDT","price":"64385.12"}` + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(payload)) + })) + defer server.Close() + + body, err := getJSON(context.Background(), server.Client(), server.URL) + if err != nil { + t.Fatal(err) + } + if string(body) != payload { + t.Fatalf("body = %q, want %q", string(body), payload) + } +} + func TestParseFixedStablecoinPrice(t *testing.T) { price, err := parseFixedStablecoinPrice("$1.0") if err != nil { diff --git a/token-price-oracle/go.mod b/token-price-oracle/go.mod index 4260f74af..f48bcd810 100644 --- a/token-price-oracle/go.mod +++ b/token-price-oracle/go.mod @@ -8,7 +8,7 @@ replace ( ) require ( - github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3 + github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe github.com/morph-l2/remote-signer-client/go v0.0.0-20260312080033-d078d86ddbe9 github.com/prometheus/client_golang v1.17.0 github.com/sirupsen/logrus v1.9.3 diff --git a/token-price-oracle/go.sum b/token-price-oracle/go.sum index 2b38a0d66..37de0bed1 100644 --- a/token-price-oracle/go.sum +++ b/token-price-oracle/go.sum @@ -145,6 +145,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3 h1:vt1tCsMO0eFZQsNnI3KRk76V52ia19Z/Zbpk92x0w3Y= github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3/go.mod h1:nkVzHjQWCOjvukQW8ittlwX+Xz9gmVHrP7mUi7zoHTs= +github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe h1:dYfgE9WDq58hiU0V0LmFkr2vUesbFaexyXfvirBVuEQ= +github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe/go.mod h1:nkVzHjQWCOjvukQW8ittlwX+Xz9gmVHrP7mUi7zoHTs= github.com/morph-l2/remote-signer-client/go v0.0.0-20260312080033-d078d86ddbe9 h1:d2nKLUgiEJsQmpSWEiGbsC+sZXQCM4y/3EzyXkoMM60= github.com/morph-l2/remote-signer-client/go v0.0.0-20260312080033-d078d86ddbe9/go.mod h1:slD6GmYEwLHn4Yj/kO8/1QF3iaYlVVAXg2ZnGr8SW/8= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= diff --git a/tx-submitter/go.mod b/tx-submitter/go.mod index 3897752a8..3c2122802 100644 --- a/tx-submitter/go.mod +++ b/tx-submitter/go.mod @@ -9,7 +9,7 @@ require ( github.com/crate-crypto/go-eth-kzg v1.4.0 github.com/holiman/uint256 v1.2.4 github.com/morph-l2/externalsign v0.3.1 - github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3 + github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe github.com/prometheus/client_golang v1.17.0 github.com/stretchr/testify v1.10.0 github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a diff --git a/tx-submitter/go.sum b/tx-submitter/go.sum index 990eb7138..22d83b0b5 100644 --- a/tx-submitter/go.sum +++ b/tx-submitter/go.sum @@ -163,6 +163,8 @@ github.com/morph-l2/externalsign v0.3.1 h1:UYFDZFB0L85A4rDvuwLNBiGEi0kSmg9AZ2v8Q github.com/morph-l2/externalsign v0.3.1/go.mod h1:b6NJ4GUiiG/gcSJsp3p8ExsIs4ZdphlrVALASnVoGJE= github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3 h1:vt1tCsMO0eFZQsNnI3KRk76V52ia19Z/Zbpk92x0w3Y= github.com/morph-l2/go-ethereum v1.10.14-0.20260709072051-ffa95e0670a3/go.mod h1:nkVzHjQWCOjvukQW8ittlwX+Xz9gmVHrP7mUi7zoHTs= +github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe h1:dYfgE9WDq58hiU0V0LmFkr2vUesbFaexyXfvirBVuEQ= +github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe/go.mod h1:nkVzHjQWCOjvukQW8ittlwX+Xz9gmVHrP7mUi7zoHTs= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=