Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion core/capabilities/vault/capability.go
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,10 @@ func NewCapability(
if err != nil {
return nil, fmt.Errorf("could not create request batch size limiter: %w", err)
}
ciphertextLimiter, err := limits.MakeUpperBoundLimiter(limitsFactory, cresettings.Default.VaultCiphertextSizeLimit)
if err != nil {
return nil, fmt.Errorf("could not create ciphertext size limiter: %w", err)
}
return &Capability{
lggr: logger.Named(lggr, "VaultCapability"),
clock: clock,
Expand All @@ -416,6 +420,6 @@ func NewCapability(
requestAuthorizer: requestAuthorizer,
capabilitiesRegistry: capabilitiesRegistry,
publicKey: publicKey,
RequestValidator: NewRequestValidator(limiter),
RequestValidator: NewRequestValidator(limiter, ciphertextLimiter),
}, nil
}
26 changes: 25 additions & 1 deletion core/capabilities/vault/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import (
"github.com/smartcontractkit/tdh2/go/tdh2/tdh2easy"

vaultcommon "github.com/smartcontractkit/chainlink-common/pkg/capabilities/actions/vault"
pkgconfig "github.com/smartcontractkit/chainlink-common/pkg/config"
"github.com/smartcontractkit/chainlink-common/pkg/settings/limits"
"github.com/smartcontractkit/chainlink/v2/core/capabilities/vault/vaulttypes"
)

type RequestValidator struct {
MaxRequestBatchSizeLimiter limits.BoundLimiter[int]
MaxCiphertextLengthLimiter limits.BoundLimiter[pkgconfig.Size]
}

func (r *RequestValidator) ValidateCreateSecretsRequest(publicKey *tdh2easy.PublicKey, request *vaultcommon.CreateSecretsRequest) error {
Expand Down Expand Up @@ -60,6 +62,9 @@ func (r *RequestValidator) validateWriteRequest(publicKey *tdh2easy.PublicKey, i
if req.EncryptedValue == "" {
return errors.New("secret must have encrypted value set at index " + strconv.Itoa(idx) + ":" + req.Id.String())
}
if err := r.validateCiphertextSize(req.EncryptedValue); err != nil {
return fmt.Errorf("secret encrypted value at index %d is invalid: %w", idx, err)
}
err := EnsureRightLabelOnSecret(publicKey, req.EncryptedValue, req.Id.Owner)
if err != nil {
return errors.New("Encrypted Secret at index [" + strconv.Itoa(idx) + "] doesn't have owner as the label. Error: " + err.Error())
Expand All @@ -75,6 +80,21 @@ func (r *RequestValidator) validateWriteRequest(publicKey *tdh2easy.PublicKey, i
return nil
}

func (r *RequestValidator) validateCiphertextSize(encryptedValue string) error {
rawCiphertext, err := hex.DecodeString(encryptedValue)
if err != nil {
return fmt.Errorf("failed to decode encrypted value: %w", err)
}
if err := r.MaxCiphertextLengthLimiter.Check(context.Background(), pkgconfig.Size(len(rawCiphertext))*pkgconfig.Byte); err != nil {
var errBoundLimited limits.ErrorBoundLimited[pkgconfig.Size]
if errors.As(err, &errBoundLimited) {
return fmt.Errorf("ciphertext size exceeds maximum allowed size: %s", errBoundLimited.Limit)
}
return fmt.Errorf("failed to check ciphertext size limit: %w", err)
}
return nil
}

func (r *RequestValidator) ValidateGetSecretsRequest(request *vaultcommon.GetSecretsRequest) error {
if len(request.Requests) == 0 {
return errors.New("no GetSecret request specified in request")
Expand Down Expand Up @@ -129,9 +149,13 @@ func (r *RequestValidator) ValidateDeleteSecretsRequest(request *vaultcommon.Del
return nil
}

func NewRequestValidator(maxRequestBatchSizeLimiter limits.BoundLimiter[int]) *RequestValidator {
func NewRequestValidator(
maxRequestBatchSizeLimiter limits.BoundLimiter[int],
maxCiphertextLengthLimiter limits.BoundLimiter[pkgconfig.Size],
) *RequestValidator {
return &RequestValidator{
MaxRequestBatchSizeLimiter: maxRequestBatchSizeLimiter,
MaxCiphertextLengthLimiter: maxCiphertextLengthLimiter,
}
}

Expand Down
96 changes: 96 additions & 0 deletions core/capabilities/vault/validator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package vault

import (
"encoding/hex"
"testing"

"github.com/stretchr/testify/require"

vaultcommon "github.com/smartcontractkit/chainlink-common/pkg/capabilities/actions/vault"
pkgconfig "github.com/smartcontractkit/chainlink-common/pkg/config"
"github.com/smartcontractkit/chainlink-common/pkg/settings/limits"
)

func TestRequestValidator_CiphertextSizeLimit(t *testing.T) {
validator := NewRequestValidator(
limits.NewUpperBoundLimiter(10),
limits.NewUpperBoundLimiter[pkgconfig.Size](10*pkgconfig.Byte),
)

id := &vaultcommon.SecretIdentifier{
Key: "key",
Namespace: "namespace",
Owner: "0x1111111111111111111111111111111111111111",
}

tests := []struct {
name string
call func(*testing.T, *RequestValidator, string) error
value string
errSubstr string
}{
{
name: "create accepts ciphertext at the limit",
call: func(t *testing.T, validator *RequestValidator, value string) error {
return validator.ValidateCreateSecretsRequest(nil, &vaultcommon.CreateSecretsRequest{
RequestId: "request-id",
EncryptedSecrets: []*vaultcommon.EncryptedSecret{
{Id: id, EncryptedValue: value},
},
})
},
value: hex.EncodeToString(make([]byte, 10)),
},
{
name: "create rejects ciphertext above the limit",
call: func(t *testing.T, validator *RequestValidator, value string) error {
return validator.ValidateCreateSecretsRequest(nil, &vaultcommon.CreateSecretsRequest{
RequestId: "request-id",
EncryptedSecrets: []*vaultcommon.EncryptedSecret{
{Id: id, EncryptedValue: value},
},
})
},
value: hex.EncodeToString(make([]byte, 11)),
errSubstr: "ciphertext size exceeds maximum allowed size",
},
{
name: "update accepts ciphertext at the limit",
call: func(t *testing.T, validator *RequestValidator, value string) error {
return validator.ValidateUpdateSecretsRequest(nil, &vaultcommon.UpdateSecretsRequest{
RequestId: "request-id",
EncryptedSecrets: []*vaultcommon.EncryptedSecret{
{Id: id, EncryptedValue: value},
},
})
},
value: hex.EncodeToString(make([]byte, 10)),
},
{
name: "update rejects ciphertext above the limit",
call: func(t *testing.T, validator *RequestValidator, value string) error {
return validator.ValidateUpdateSecretsRequest(nil, &vaultcommon.UpdateSecretsRequest{
RequestId: "request-id",
EncryptedSecrets: []*vaultcommon.EncryptedSecret{
{Id: id, EncryptedValue: value},
},
})
},
value: hex.EncodeToString(make([]byte, 11)),
errSubstr: "ciphertext size exceeds maximum allowed size",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.call(t, validator, tt.value)
if tt.errSubstr == "" {
require.NoError(t, err)
return
}

require.Error(t, err)
require.ErrorContains(t, err, tt.errSubstr)
})
}
}
6 changes: 5 additions & 1 deletion core/services/gateway/handlers/vault/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ func NewHandler(methodConfig json.RawMessage, donConfig *config.DONConfig, don g
if err != nil {
return nil, fmt.Errorf("could not create request batch size limiter: %w", err)
}
ciphertextLimiter, err := limits.MakeUpperBoundLimiter(limitsFactory, cresettings.Default.VaultCiphertextSizeLimit)
if err != nil {
return nil, fmt.Errorf("could not create ciphertext size limiter: %w", err)
}

writeMethodsEnabled, err := limits.MakeGateLimiter(limitsFactory, cresettings.Default.GatewayVaultManagementEnabled)
if err != nil {
Expand All @@ -218,7 +222,7 @@ func NewHandler(methodConfig json.RawMessage, donConfig *config.DONConfig, don g
metrics: metrics,
aggregator: &baseAggregator{capabilitiesRegistry: capabilitiesRegistry},
clock: clock,
RequestValidator: vaultcap.NewRequestValidator(limiter),
RequestValidator: vaultcap.NewRequestValidator(limiter, ciphertextLimiter),
}, nil
}

Expand Down
Loading