diff --git a/cmd/secrets/common/browser_flow.go b/cmd/secrets/common/browser_flow.go index f98fc166..0bdd2400 100644 --- a/cmd/secrets/common/browser_flow.go +++ b/cmd/secrets/common/browser_flow.go @@ -240,6 +240,11 @@ func (h *Handler) ExecuteBrowserVaultAuthorization(ctx context.Context, method s // postVaultGatewayWithBearer POSTs the digest-bound JSON-RPC body with the vault JWT and parses the gateway response. func (h *Handler) postVaultGatewayWithBearer(method string, requestBody []byte, accessToken string) error { + requestID, err := jsonRPCRequestID(requestBody) + if err != nil { + return err + } + ui.Dim("Submitting request to vault gateway...") respBody, status, err := h.Gw.PostWithBearer(requestBody, accessToken) if err != nil { @@ -248,5 +253,18 @@ func (h *Handler) postVaultGatewayWithBearer(method string, requestBody []byte, if status != http.StatusOK { return fmt.Errorf("gateway returned a non-200 status code: status_code=%d, body=%s", status, respBody) } - return h.ParseVaultGatewayResponse(method, respBody) + return h.ParseVaultGatewayResponse(method, requestID, respBody) +} + +func jsonRPCRequestID(body []byte) (string, error) { + var req struct { + ID string `json:"id"` + } + if err := json.Unmarshal(body, &req); err != nil { + return "", fmt.Errorf("failed to parse jsonrpc request id: %w", err) + } + if req.ID == "" { + return "", fmt.Errorf("jsonrpc request id is empty") + } + return req.ID, nil } diff --git a/cmd/secrets/common/browser_flow_test.go b/cmd/secrets/common/browser_flow_test.go index 43e96392..be1b2439 100644 --- a/cmd/secrets/common/browser_flow_test.go +++ b/cmd/secrets/common/browser_flow_test.go @@ -97,7 +97,7 @@ func TestPostVaultGatewayWithBearer_ListParsesResponse(t *testing.T) { }, } - err := h.postVaultGatewayWithBearer(vaulttypes.MethodSecretsList, []byte(`{}`), "t") + err := h.postVaultGatewayWithBearer(vaulttypes.MethodSecretsList, []byte(`{"jsonrpc":"2.0","id":"1","method":"x"}`), "t") w.Close() os.Stdout = oldStdout var out strings.Builder @@ -115,7 +115,7 @@ func TestPostVaultGatewayWithBearer_GatewayNon200(t *testing.T) { }, } - err := h.postVaultGatewayWithBearer(vaulttypes.MethodSecretsDelete, []byte(`{}`), "t") + err := h.postVaultGatewayWithBearer(vaulttypes.MethodSecretsDelete, []byte(`{"jsonrpc":"2.0","id":"1","method":"x"}`), "t") require.Error(t, err) assert.Contains(t, err.Error(), "non-200") assert.Contains(t, err.Error(), "403") @@ -129,7 +129,7 @@ func TestPostVaultGatewayWithBearer_InvalidJSONRPC(t *testing.T) { }, } - err := h.postVaultGatewayWithBearer(vaulttypes.MethodSecretsUpdate, []byte(`{}`), "t") + err := h.postVaultGatewayWithBearer(vaulttypes.MethodSecretsUpdate, []byte(`{"jsonrpc":"2.0","id":"1","method":"x"}`), "t") require.Error(t, err) assert.Contains(t, err.Error(), "unmarshal") } diff --git a/cmd/secrets/common/handler.go b/cmd/secrets/common/handler.go index b9135b0f..40f8dfda 100644 --- a/cmd/secrets/common/handler.go +++ b/cmd/secrets/common/handler.go @@ -602,7 +602,7 @@ func (h *Handler) Execute( if status != http.StatusOK { return fmt.Errorf("gateway returned a non-200 status code: status_code=%d, body=%s", status, respBody) } - return h.ParseVaultGatewayResponse(method, respBody) + return h.ParseVaultGatewayResponse(method, requestID, respBody) } if txOut == nil && allowlisted { @@ -683,10 +683,10 @@ func (h *Handler) Execute( return nil } -// ParseVaultGatewayResponse parses the JSON-RPC response, decodes the SignedOCRResponse payload -// into the appropriate proto type (CreateSecretsResponse, UpdateSecretsResponse, DeleteSecretsResponse), -// and logs one line per secret with id/owner/namespace/success/error. -func (h *Handler) ParseVaultGatewayResponse(method string, respBody []byte) error { +// ParseVaultGatewayResponse parses the JSON-RPC response, optionally verifies OCR signatures +// and the JSON-RPC response id, decodes the SignedOCRResponse payload into the appropriate proto +// type, and logs one line per secret with id/owner/namespace/success/error. +func (h *Handler) ParseVaultGatewayResponse(method, requestID string, respBody []byte) error { // Unmarshal JSON-RPC envelope with SignedOCRResponse result var rpcResp jsonrpc2.Response[vaulttypes.SignedOCRResponse] if err := json.Unmarshal(respBody, &rpcResp); err != nil { @@ -699,6 +699,10 @@ func (h *Handler) ParseVaultGatewayResponse(method string, respBody []byte) erro return fmt.Errorf("gateway returned JSON-RPC error: %s", string(b)) } + if err := h.verifyVaultGatewayResponse(h.execCtx, &rpcResp, requestID); err != nil { + return err + } + // Ensure we have a result payload if len(rpcResp.Result.Payload) == 0 { return fmt.Errorf("empty SignedOCRResponse payload") diff --git a/cmd/secrets/common/parse_response_test.go b/cmd/secrets/common/parse_response_test.go index 46f448c6..6ed1b4cb 100644 --- a/cmd/secrets/common/parse_response_test.go +++ b/cmd/secrets/common/parse_response_test.go @@ -2,6 +2,7 @@ package common import ( "bytes" + "context" "encoding/json" "io" "os" @@ -41,7 +42,11 @@ func encodeRPCBodyFromPayload(payload []byte) []byte { func newTestHandler(buf *bytes.Buffer) *Handler { logger := zerolog.New(buf) - return &Handler{Log: &logger} + return &Handler{ + Log: &logger, + execCtx: context.Background(), + skipVaultValidation: true, + } } // Build the payload using the real proto types @@ -129,7 +134,7 @@ func TestParseVaultGatewayResponse_Create_LogsPerItem(t *testing.T) { h := newTestHandler(&buf) body := encodeRPCBodyFromPayload(buildCreatePayloadProto(t)) - if err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsCreate, body); err != nil { + if err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsCreate, "", body); err != nil { t.Fatalf("unexpected error: %v", err) } @@ -174,7 +179,7 @@ func TestParseVaultGatewayResponse_Update_Success(t *testing.T) { h := newTestHandler(nil) body := encodeRPCBodyFromPayload(buildUpdatePayloadProto(t)) - if err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsUpdate, body); err != nil { + if err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsUpdate, "", body); err != nil { t.Fatalf("unexpected error: %v", err) } @@ -203,7 +208,7 @@ func TestParseVaultGatewayResponse_Delete_Success(t *testing.T) { h := newTestHandler(&buf) body := encodeRPCBodyFromPayload(buildDeletePayloadProto(t)) - if err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsDelete, body); err != nil { + if err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsDelete, "", body); err != nil { t.Fatalf("unexpected error: %v", err) } @@ -226,7 +231,7 @@ func TestParseVaultGatewayResponse_JSONRPCError(t *testing.T) { h := newTestHandler(&buf) body := encodeRPCBodyFromError(-32000, "upstream failed") - err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsCreate, body) + err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsCreate, "", body) if err == nil || !strings.Contains(err.Error(), "gateway returned JSON-RPC error") || !strings.Contains(err.Error(), "upstream failed") { t.Fatalf("expected JSON-RPC error surfaced, got: %v", err) @@ -239,7 +244,7 @@ func TestParseVaultGatewayResponse_EmptyPayload(t *testing.T) { // Omit payload entirely -> handler should report "empty SignedOCRResponse payload" raw := encodeRPCBodyFromPayload(nil) - err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsUpdate, raw) + err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsUpdate, "", raw) if err == nil || !strings.Contains(err.Error(), "empty SignedOCRResponse payload") { t.Fatalf("expected empty payload error, got: %v", err) } @@ -250,7 +255,7 @@ func TestParseVaultGatewayResponse_MalformedTopLevelJSON(t *testing.T) { h := newTestHandler(&buf) raw := []byte(`{"jsonrpc":"2.0","id":"1","result": this is not valid}`) - err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsUpdate, raw) + err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsUpdate, "", raw) if err == nil || !strings.Contains(err.Error(), "failed to unmarshal JSON-RPC response") { t.Fatalf("expected unmarshal error, got: %v", err) } @@ -262,7 +267,7 @@ func TestParseVaultGatewayResponse_BadPayloadForCreate(t *testing.T) { // Wrong shape for the proto: responses should be an array. raw := encodeRPCBodyFromPayload([]byte(`{"responses":"not-an-array"}`)) - err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsCreate, raw) + err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsCreate, "", raw) if err == nil || !strings.Contains(err.Error(), "failed to decode create payload") { t.Fatalf("expected proto decode error for create, got: %v", err) } @@ -274,7 +279,7 @@ func TestParseVaultGatewayResponse_UnsupportedMethod_Warns(t *testing.T) { // Non-empty payload so it passes "empty payload" check; method is unknown -> warn. raw := encodeRPCBodyFromPayload([]byte(`{"anything":"ok"}`)) - if err := h.ParseVaultGatewayResponse("totally.unknown.method", raw); err != nil { + if err := h.ParseVaultGatewayResponse("totally.unknown.method", "", raw); err != nil { t.Fatalf("unexpected error: %v", err) } out := buf.String() @@ -337,7 +342,7 @@ func TestParseVaultGatewayResponse_List_SuccessWithIdentifiers(t *testing.T) { h := newTestHandler(&buf) body := encodeRPCBodyFromPayload(buildListPayloadProtoSuccessWithItems(t)) - if err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsList, body); err != nil { + if err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsList, "", body); err != nil { t.Fatalf("unexpected error: %v", err) } @@ -369,7 +374,7 @@ func TestParseVaultGatewayResponse_List_EmptySuccess(t *testing.T) { h := newTestHandler(&buf) body := encodeRPCBodyFromPayload(buildListPayloadProtoEmptySuccess(t)) - if err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsList, body); err != nil { + if err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsList, "", body); err != nil { t.Fatalf("unexpected error: %v", err) } @@ -398,7 +403,7 @@ func TestParseVaultGatewayResponse_List_Failure(t *testing.T) { h := newTestHandler(&buf) body := encodeRPCBodyFromPayload(buildListPayloadProtoFailure(t, "boom")) - if err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsList, body); err != nil { + if err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsList, "", body); err != nil { t.Fatalf("unexpected error: %v", err) } @@ -425,7 +430,7 @@ func TestParseVaultGatewayResponse_BadPayloadForList(t *testing.T) { // Wrong shape: identifiers should be an array raw := encodeRPCBodyFromPayload([]byte(`{"identifiers":"not-an-array"}`)) - err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsList, raw) + err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsList, "", raw) if err == nil || !strings.Contains(err.Error(), "failed to decode list payload") { t.Fatalf("expected decode error for list payload, got: %v", err) } diff --git a/cmd/secrets/common/vault_response_verify.go b/cmd/secrets/common/vault_response_verify.go new file mode 100644 index 00000000..8ab532c5 --- /dev/null +++ b/cmd/secrets/common/vault_response_verify.go @@ -0,0 +1,53 @@ +package common + +import ( + "context" + "fmt" + + "github.com/smartcontractkit/chainlink-common/pkg/jsonrpc2" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/vault/vaulttypes" + + "github.com/smartcontractkit/cre-cli/internal/onchain/capabilitiesregistry" +) + +func (h *Handler) verifyVaultGatewayResponse( + ctx context.Context, + rpcResp *jsonrpc2.Response[vaulttypes.SignedOCRResponse], + requestID string, +) error { + if h.SkipVaultValidation() { + return nil + } + if requestID == "" { + return fmt.Errorf("missing request ID for vault response verification") + } + if rpcResp.ID != requestID { + return fmt.Errorf("jsonrpc id mismatch: got %q want %q", rpcResp.ID, requestID) + } + + resolver, ok := h.VaultDONResolver() + if !ok { + return nil + } + + signed := rpcResp.Result + if signed == nil { + return fmt.Errorf("empty SignedOCRResponse result") + } + // TODO(DEVSVCS-5365) + if len(signed.Signatures) == 0 { + return nil + } + + v, err := resolver.ResolveVaultDON(ctx) + if err != nil { + return fmt.Errorf("resolve vault DON for signature verification: %w", err) + } + + signers := capabilitiesregistry.OCRSignerAddresses(v.Nodes) + minSigs := capabilitiesregistry.MinOCRSignatures(v.DON.F) + if err := vaulttypes.ValidateSignatures(signed, signers, minSigs); err != nil { + return fmt.Errorf("vault response signature verification failed: %w", err) + } + return nil +} diff --git a/cmd/secrets/common/vault_response_verify_test.go b/cmd/secrets/common/vault_response_verify_test.go new file mode 100644 index 00000000..bd423963 --- /dev/null +++ b/cmd/secrets/common/vault_response_verify_test.go @@ -0,0 +1,195 @@ +package common + +import ( + "bytes" + "context" + "encoding/hex" + "encoding/json" + "math/big" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + + vaultcommon "github.com/smartcontractkit/chainlink-common/pkg/capabilities/actions/vault" + capabilitiespb "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" + capreg "github.com/smartcontractkit/chainlink-evm/gethwrappers/workflow/generated/capabilities_registry_wrapper_v2" + "github.com/smartcontractkit/chainlink-protos/cre/go/values" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/vault/vaulttypes" + + "github.com/smartcontractkit/cre-cli/cmd/secrets/common/vaultdon" +) + +func attachMockVaultDONResolverWithOCRSigners(t *testing.T, h *Handler, signers []common.Address) { + t.Helper() + + valueMap, err := values.WrapMap(map[string]any{ + "VaultPublicKey": "deadbeef", + "Threshold": 1, + }) + require.NoError(t, err) + cfgBytes, err := proto.Marshal(&capabilitiespb.CapabilityConfig{ + DefaultConfig: values.ProtoMap(valueMap), + }) + require.NoError(t, err) + + nodes := make([]capreg.INodeInfoProviderNodeInfo, len(signers)) + for i, addr := range signers { + p2pID := [32]byte{byte(i + 1)} + copy(nodes[i].Signer[:], addr.Bytes()) + nodes[i].P2pId = p2pID + } + + reader := &mockCapRegReader{ + donIDs: []*big.Int{big.NewInt(1)}, + dons: map[uint32]capreg.CapabilitiesRegistryDONInfo{ + 1: { + Id: 1, + F: 1, + NodeP2PIds: func() [][32]byte { + ids := make([][32]byte, len(nodes)) + for i := range nodes { + ids[i] = nodes[i].P2pId + } + return ids + }(), + CapabilityConfigurations: []capreg.CapabilitiesRegistryCapabilityConfiguration{ + {CapabilityId: vaultcommon.CapabilityID, Config: cfgBytes}, + }, + }, + }, + nodes: nodes, + } + + h.vaultDONResolver = vaultdon.NewResolver(reader, "zone-a") + h.skipVaultValidation = false +} + +func encodeSignedRPCBody(t *testing.T, requestID string, payload []byte, ctxHex string, sigs ...string) []byte { + t.Helper() + + ctxBytes, err := hex.DecodeString(ctxHex) + require.NoError(t, err) + + signatures := make([][]byte, 0, len(sigs)) + for _, sigHex := range sigs { + sig, err := hex.DecodeString(sigHex) + require.NoError(t, err) + signatures = append(signatures, sig) + } + + result := map[string]any{ + "payload": json.RawMessage(payload), + "context": ctxBytes, + "signatures": signatures, + } + resultJSON, err := json.Marshal(result) + require.NoError(t, err) + + resp := map[string]any{ + "jsonrpc": "2.0", + "id": requestID, + "result": json.RawMessage(resultJSON), + } + body, err := json.Marshal(resp) + require.NoError(t, err) + return body +} + +func TestVerifyVaultGatewayResponse_SkipWhenOptedOut(t *testing.T) { + var buf bytes.Buffer + logger := zerolog.New(&buf) + h := &Handler{Log: &logger, execCtx: context.Background(), skipVaultValidation: true} + + body := encodeSignedRPCBody(t, "req-1", []byte(`{"responses":[]}`), + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "d1067844e2849b404d903730c4cae19f090d53a578a1e8dc16ecbdc0285c1f186599108abbe0073b78bc148a6504907474ed3a6881df917e6d142cff70acfb5900", + ) + + err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsCreate, "other-id", body) + require.NoError(t, err) +} + +func TestVerifyVaultGatewayResponse_RequestIDMismatch(t *testing.T) { + var buf bytes.Buffer + logger := zerolog.New(&buf) + h := &Handler{Log: &logger, execCtx: context.Background()} + + body := encodeRPCBodyFromPayload(buildCreatePayloadProto(t)) + err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsCreate, "expected-id", body) + require.ErrorContains(t, err, "jsonrpc id mismatch") +} + +func TestVerifyVaultGatewayResponse_EmptySignaturesPassThrough(t *testing.T) { + var buf bytes.Buffer + logger := zerolog.New(&buf) + h := &Handler{Log: &logger, execCtx: context.Background()} + attachMockVaultDONResolver(t, h, "deadbeef") + + requestID := "req-empty-sigs" + body := encodeRPCBodyFromPayload(buildCreatePayloadProto(t)) + var resp testRPCResp + require.NoError(t, json.Unmarshal(body, &resp)) + resp.ID = requestID + body, err := json.Marshal(resp) + require.NoError(t, err) + + err = h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsCreate, requestID, body) + require.NoError(t, err) +} + +func TestVerifyVaultGatewayResponse_ValidSignatures(t *testing.T) { + var buf bytes.Buffer + logger := zerolog.New(&buf) + h := &Handler{Log: &logger, execCtx: context.Background()} + attachMockVaultDONResolverWithOCRSigners(t, h, []common.Address{ + common.HexToAddress("0xd6da96fe596705b32bc3a0e11cdefad77feaad79"), + common.HexToAddress("0x327aa349c9718cd36c877d1e90458fe1929768ad"), + common.HexToAddress("0xe9bf394856d73402b30e160d0e05c847796f0e29"), + common.HexToAddress("0xefd5bdb6c3256f04489a6ca32654d547297f48b9"), + }) + + requestID := "req-valid-sigs" + payload := []byte(`{"responses":[{"error":"failed to verify ciphertext: cannot unmarshal data: unexpected end of JSON input","id":{"key":"W","namespace":"","owner":"foo"},"success":false}]}`) + body := encodeSignedRPCBody(t, requestID, payload, + "000ec4f6a2ba011e909eccf64628855b848e08876a1edd938a1372a9e51adff100000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000", + "d1067844e2849b404d903730c4cae19f090d53a578a1e8dc16ecbdc0285c1f186599108abbe0073b78bc148a6504907474ed3a6881df917e6d142cff70acfb5900", + "c7517c188d297093a6f602046fad7feafe19454ee9dc269b19c8e6c01268037d1f7b423eeecbc495dd2d9a65e106bc3eab849ddfd74a10cbd4ad50c7d953bd4b01", + ) + + err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsCreate, requestID, body) + require.NoError(t, err) +} + +func TestVerifyVaultGatewayResponse_InvalidSignatures(t *testing.T) { + var buf bytes.Buffer + logger := zerolog.New(&buf) + h := &Handler{Log: &logger, execCtx: context.Background()} + attachMockVaultDONResolverWithOCRSigners(t, h, []common.Address{ + common.HexToAddress("0xd6da96fe596705b32bc3a0e11cdefad77feaad79"), + }) + + requestID := "req-invalid-sigs" + payload := []byte(`{"responses":[{"error":"failed to verify ciphertext: cannot unmarshal data: unexpected end of JSON input","id":{"key":"W","namespace":"","owner":"foo"},"success":false}]}`) + body := encodeSignedRPCBody(t, requestID, payload, + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "d1067844e2849b404d903730c4cae19f090d53a578a1e8dc16ecbdc0285c1f186599108abbe0073b78bc148a6504907474ed3a6881df917e6d142cff70acfb5900", + "c7517c188d297093a6f602046fad7feafe19454ee9dc269b19c8e6c01268037d1f7b423eeecbc495dd2d9a65e106bc3eab849ddfd74a10cbd4ad50c7d953bd4b01", + ) + + err := h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsCreate, requestID, body) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "signature verification failed")) +} + +func TestJSONRPCRequestID(t *testing.T) { + id, err := jsonRPCRequestID([]byte(`{"jsonrpc":"2.0","id":"abc-123","method":"vault.secrets.list"}`)) + require.NoError(t, err) + require.Equal(t, "abc-123", id) + + _, err = jsonRPCRequestID([]byte(`{"jsonrpc":"2.0","method":"vault.secrets.list"}`)) + require.ErrorContains(t, err, "jsonrpc request id is empty") +} diff --git a/cmd/secrets/delete/delete.go b/cmd/secrets/delete/delete.go index b067c0d9..25724b4f 100644 --- a/cmd/secrets/delete/delete.go +++ b/cmd/secrets/delete/delete.go @@ -188,7 +188,7 @@ func Execute(ctx context.Context, h *common.Handler, inputs DeleteSecretsInputs, if status != http.StatusOK { return fmt.Errorf("gateway returned a non-200 status code: status_code=%d, body=%s", status, respBody) } - return h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsDelete, respBody) + return h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsDelete, requestID, respBody) } ownerAddr := ethcommon.HexToAddress(owner) diff --git a/cmd/secrets/execute/execute.go b/cmd/secrets/execute/execute.go index 83e5ccc8..6648048a 100644 --- a/cmd/secrets/execute/execute.go +++ b/cmd/secrets/execute/execute.go @@ -94,7 +94,7 @@ func New(ctx *runtime.Context) *cobra.Command { } // Parse & print results according to the bundle method - return h.ParseVaultGatewayResponse(b.Method, respBody) + return h.ParseVaultGatewayResponse(b.Method, b.RequestID, respBody) }, } diff --git a/cmd/secrets/list/list.go b/cmd/secrets/list/list.go index a5c0891b..6796ab9a 100644 --- a/cmd/secrets/list/list.go +++ b/cmd/secrets/list/list.go @@ -165,7 +165,7 @@ func Execute(ctx context.Context, h *common.Handler, namespace string, duration if status != http.StatusOK { return fmt.Errorf("gateway returned a non-200 status code: status_code=%d, body=%s", status, respBody) } - return h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsList, respBody) + return h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsList, requestID, respBody) } if txOut == nil && allowlisted {