Merge pull request #2195 from versity/sis/object-lock-default-retention-too-large

fix: validate object lock default retention upper limits
This commit is contained in:
Ben McClelland
2026-06-17 15:35:03 -07:00
committed by GitHub
3 changed files with 75 additions and 29 deletions
+11
View File
@@ -35,6 +35,11 @@ type BucketLockConfig struct {
CreatedAt *time.Time
}
const (
maxObjectLockRetentionDays int32 = 36500
maxObjectLockRetentionYears int32 = 100
)
func ParseBucketLockConfigurationInput(input []byte) ([]byte, error) {
var lockConfig types.ObjectLockConfiguration
if err := xml.Unmarshal(input, &lockConfig); err != nil {
@@ -62,9 +67,15 @@ func ParseBucketLockConfigurationInput(input []byte) ([]byte, error) {
if retention.Days != nil && *retention.Days <= 0 {
return nil, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionDays, fmt.Sprint(*retention.Days))
}
if retention.Days != nil && *retention.Days > maxObjectLockRetentionDays {
return nil, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionDaysTooLarge, fmt.Sprint(*retention.Days))
}
if retention.Years != nil && *retention.Years <= 0 {
return nil, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionYears, fmt.Sprint(*retention.Years))
}
if retention.Years != nil && *retention.Years > maxObjectLockRetentionYears {
return nil, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionYearsTooLarge, fmt.Sprint(*retention.Years))
}
config.DefaultRetention = retention
now := time.Now()
+10
View File
@@ -43,6 +43,8 @@ const (
InvalidArgPastObjectLockRetainDate
InvalidArgObjectLockRetentionDays
InvalidArgObjectLockRetentionYears
InvalidArgObjectLockRetentionDaysTooLarge
InvalidArgObjectLockRetentionYearsTooLarge
InvalidArgMissingObjectLockRetainDate
InvalidArgMissingObjectLockMode
InvalidArgLegalHoldStatus
@@ -145,6 +147,14 @@ var invalidArgErrResponses = map[InvalidArgErrorCode]InvalidArgumentError{
Description: "Default retention period must be a positive integer value.",
ArgumentName: "Years",
},
InvalidArgObjectLockRetentionDaysTooLarge: {
Description: "Default retention period too large.",
ArgumentName: "Days",
},
InvalidArgObjectLockRetentionYearsTooLarge: {
Description: "Default retention period too large.",
ArgumentName: "Years",
},
InvalidArgLegalHoldStatus: {
Description: "Legal Hold must be either of 'ON' or 'OFF'",
ArgumentName: "x-amz-object-lock-legal-hold",
+54 -29
View File
@@ -211,42 +211,67 @@ func PutObjectLockConfiguration_both_years_and_days(s *S3Conf) error {
}
func PutObjectLockConfiguration_invalid_years_days(s *S3Conf) error {
testName := "PutObjectLockConfiguration_invalid_years"
testName := "PutObjectLockConfiguration_invalid_years_days"
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
var days, years int32 = -3, -5
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
_, err := s3client.PutObjectLockConfiguration(ctx, &s3.PutObjectLockConfigurationInput{
Bucket: &bucket,
ObjectLockConfiguration: &types.ObjectLockConfiguration{
ObjectLockEnabled: types.ObjectLockEnabledEnabled,
Rule: &types.ObjectLockRule{
DefaultRetention: &types.DefaultRetention{
Days: &days,
Mode: types.ObjectLockRetentionModeCompliance,
putObjectLockConfiguration := func(retention *types.DefaultRetention) error {
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
_, err := s3client.PutObjectLockConfiguration(ctx, &s3.PutObjectLockConfigurationInput{
Bucket: &bucket,
ObjectLockConfiguration: &types.ObjectLockConfiguration{
ObjectLockEnabled: types.ObjectLockEnabledEnabled,
Rule: &types.ObjectLockRule{
DefaultRetention: retention,
},
},
},
})
cancel()
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionDays, fmt.Sprint(days))); err != nil {
})
cancel()
return err
}
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
_, err = s3client.PutObjectLockConfiguration(ctx, &s3.PutObjectLockConfigurationInput{
Bucket: &bucket,
ObjectLockConfiguration: &types.ObjectLockConfiguration{
ObjectLockEnabled: types.ObjectLockEnabledEnabled,
Rule: &types.ObjectLockRule{
DefaultRetention: &types.DefaultRetention{
Years: &years,
Mode: types.ObjectLockRetentionModeCompliance,
},
tests := []struct {
name string
retention *types.DefaultRetention
err s3err.InvalidArgumentError
}{
{
name: "negative days",
retention: &types.DefaultRetention{
Days: getPtr[int32](-3),
Mode: types.ObjectLockRetentionModeCompliance,
},
err: s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionDays, "-3"),
},
})
cancel()
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionYears, fmt.Sprint(years))); err != nil {
return err
{
name: "too many days",
retention: &types.DefaultRetention{
Days: getPtr[int32](36501),
Mode: types.ObjectLockRetentionModeCompliance,
},
err: s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionDaysTooLarge, "36501"),
},
{
name: "negative years",
retention: &types.DefaultRetention{
Years: getPtr[int32](-5),
Mode: types.ObjectLockRetentionModeCompliance,
},
err: s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionYears, "-5"),
},
{
name: "too many years",
retention: &types.DefaultRetention{
Years: getPtr[int32](101),
Mode: types.ObjectLockRetentionModeCompliance,
},
err: s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionYearsTooLarge, "101"),
},
}
for _, test := range tests {
err := putObjectLockConfiguration(test.retention)
if err := checkApiErr(err, test.err); err != nil {
return fmt.Errorf("%s: %w", test.name, err)
}
}
return nil