Skip to content

Commit 4d73f07

Browse files
JAORMXclaude
andcommitted
Address additional review feedback from PR #5959
- Use proto.Size() check for identifying_properties validation (more robust than counting properties, catches large values) - Remove CustomValidators from EntityCreationOptions (no planned use case, reduces complexity) - Use ReplaceAllProperties instead of SaveAllProperties (ensures clean slate for entity creation) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 7c13f96 commit 4d73f07

File tree

3 files changed

+28
-26
lines changed

3 files changed

+28
-26
lines changed

internal/controlplane/handlers_entity_instances.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/google/uuid"
1313
"google.golang.org/grpc/codes"
14+
"google.golang.org/protobuf/proto"
1415

1516
"github.com/mindersec/minder/internal/engine/engcontext"
1617
"github.com/mindersec/minder/internal/entities/models"
@@ -245,25 +246,27 @@ func (s *Server) RegisterEntity(
245246

246247
// parseIdentifyingProperties converts proto properties to Properties object
247248
func parseIdentifyingProperties(req *pb.RegisterEntityRequest) (*properties.Properties, error) {
248-
if req.GetIdentifyingProperties() == nil {
249+
identifyingProps := req.GetIdentifyingProperties()
250+
if identifyingProps == nil {
249251
return nil, errors.New("identifying_properties is required")
250252
}
251253

252-
propsMap := req.GetIdentifyingProperties().AsMap()
253-
254-
// Validate reasonable property count to prevent resource exhaustion
255-
const maxPropertyCount = 100
256-
if len(propsMap) > maxPropertyCount {
257-
return nil, fmt.Errorf("too many identifying properties: got %d, max %d",
258-
len(propsMap), maxPropertyCount)
254+
// Validate total size to prevent resource exhaustion
255+
// Using proto.Size provides a better bound than counting properties,
256+
// as it accounts for arbitrarily large values in a Struct
257+
const maxProtoSize = 32 * 1024 // 32KB should be plenty for identifying properties
258+
if protoSize := proto.Size(identifyingProps); protoSize > maxProtoSize {
259+
return nil, fmt.Errorf("identifying_properties too large: %d bytes, max %d bytes",
260+
protoSize, maxProtoSize)
259261
}
260262

261-
// Validate property keys are reasonable (alphanumeric, slash, underscore, hyphen)
263+
propsMap := identifyingProps.AsMap()
264+
265+
// Validate property keys are reasonable length
262266
for key := range propsMap {
263267
if len(key) > 200 {
264268
return nil, fmt.Errorf("property key too long: %d characters", len(key))
265269
}
266-
// Note: Additional key sanitization could be added here if needed
267270
}
268271

269272
return properties.NewProperties(propsMap), nil

internal/controlplane/handlers_entity_instances_test.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -363,19 +363,22 @@ func TestParseIdentifyingProperties(t *testing.T) {
363363
errContains: "identifying_properties is required",
364364
},
365365
{
366-
name: "rejects too many properties",
366+
name: "rejects properties that are too large",
367367
request: &pb.RegisterEntityRequest{
368368
IdentifyingProperties: func() *structpb.Struct {
369-
props := make(map[string]any)
370-
for i := 0; i < 101; i++ {
371-
props[string(rune('a'+i%26))+string(rune(i))] = "value"
369+
// Create a value large enough to exceed 32KB limit
370+
largeValue := string(make([]byte, 33*1024))
371+
for i := range largeValue {
372+
largeValue = string(append([]byte(largeValue[:i]), 'x'))
372373
}
373-
s, _ := structpb.NewStruct(props)
374+
s, _ := structpb.NewStruct(map[string]any{
375+
"large_key": largeValue,
376+
})
374377
return s
375378
}(),
376379
},
377380
wantErr: true,
378-
errContains: "too many identifying properties",
381+
errContains: "identifying_properties too large",
379382
},
380383
{
381384
name: "rejects property key that is too long",

internal/entities/service/entity_creator.go

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,6 @@ type EntityCreationOptions struct {
3737

3838
// Whether to publish reconciliation events
3939
PublishReconciliationEvent bool
40-
41-
// Custom validators to run (in addition to default validators)
42-
CustomValidators []EntityValidator
4340
}
4441

4542
// EntityCreator creates entities in a consistent, reusable way
@@ -125,8 +122,8 @@ func (e *entityCreator) CreateEntity(
125122
return nil, fmt.Errorf("error fetching properties: %w", err)
126123
}
127124

128-
// 4. Run validators (default + custom)
129-
if err := e.runValidators(ctx, entityType, allProps, projectID, opts.CustomValidators); err != nil {
125+
// 4. Run validators
126+
if err := e.runValidators(ctx, entityType, allProps, projectID); err != nil {
130127
return nil, err
131128
}
132129

@@ -175,8 +172,9 @@ func (e *entityCreator) CreateEntity(
175172
return nil, fmt.Errorf("error creating entity: %w", err)
176173
}
177174

178-
// Save properties
179-
if err := e.propSvc.SaveAllProperties(ctx, ent.ID, registeredProps,
175+
// Replace properties - use Replace to ensure a clean slate
176+
// (removes any stale properties from previous failed attempts)
177+
if err := e.propSvc.ReplaceAllProperties(ctx, ent.ID, registeredProps,
180178
propService.CallBuilder().WithStoreOrTransaction(t)); err != nil {
181179
return nil, fmt.Errorf("error saving properties: %w", err)
182180
}
@@ -206,10 +204,8 @@ func (e *entityCreator) runValidators(
206204
entityType pb.Entity,
207205
allProps *properties.Properties,
208206
projectID uuid.UUID,
209-
customValidators []EntityValidator,
210207
) error {
211-
allValidators := append(e.validators, customValidators...)
212-
for _, validator := range allValidators {
208+
for _, validator := range e.validators {
213209
if err := validator.Validate(ctx, entityType, allProps, projectID); err != nil {
214210
return err
215211
}

0 commit comments

Comments
 (0)