fix: CORS wildcard subdomain matching cache race condition (#7736)

test: add HTTPS test cases for CORS wildcard subdomain matching

This adds comprehensive test coverage for HTTPS subdomain wildcard matching
in TestMatchesOrigin:
- https exact match
- https no match
- https wildcard subdomain match
- https wildcard subdomain no match (base domain)
- https wildcard subdomain no match (different domain)
- protocol mismatch tests (http pattern vs https origin and vice versa)

The matchWildcard function was already working correctly - this just adds
test coverage for the HTTPS cases that were previously untested.

Note: The cache invalidation is already handled synchronously by
setBucketMetadata() which is called via:
- UpdateBucketCORS -> UpdateBucketMetadata -> setBucketMetadata
- ClearBucketCORS -> UpdateBucketMetadata -> setBucketMetadata

Added clarifying comments to document this call chain.
This commit is contained in:
Chris Lu
2025-12-13 14:33:46 -08:00
committed by GitHub
parent f77e6ed2d4
commit f70cd05404
3 changed files with 53 additions and 6 deletions

View File

@@ -263,6 +263,49 @@ func TestMatchesOrigin(t *testing.T) {
origin: "http://other.com",
want: true,
},
// HTTPS test cases
{
name: "https exact match",
allowedOrigins: []string{"https://example.com"},
origin: "https://example.com",
want: true,
},
{
name: "https no match",
allowedOrigins: []string{"https://example.com"},
origin: "https://other.com",
want: false,
},
{
name: "https wildcard subdomain match",
allowedOrigins: []string{"https://*.example.com"},
origin: "https://api.example.com",
want: true,
},
{
name: "https wildcard subdomain no match - base domain",
allowedOrigins: []string{"https://*.example.com"},
origin: "https://example.com",
want: false,
},
{
name: "https wildcard subdomain no match - different domain",
allowedOrigins: []string{"https://*.example.com"},
origin: "https://api.other.com",
want: false,
},
{
name: "protocol mismatch - http pattern https origin",
allowedOrigins: []string{"http://*.example.com"},
origin: "https://api.example.com",
want: false,
},
{
name: "protocol mismatch - https pattern http origin",
allowedOrigins: []string{"https://*.example.com"},
origin: "http://api.example.com",
want: false,
},
}
for _, tt := range tests {
@@ -480,7 +523,7 @@ func TestApplyHeaders(t *testing.T) {
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Expose-Headers": "ETag",
"Access-Control-Max-Age": "3600",
"Vary": "Origin",
"Vary": "Origin",
},
},
{
@@ -494,7 +537,7 @@ func TestApplyHeaders(t *testing.T) {
"Access-Control-Allow-Origin": "http://example.com",
"Access-Control-Allow-Methods": "GET",
"Access-Control-Allow-Credentials": "true",
"Vary": "Origin",
"Vary": "Origin",
},
},
}

View File

@@ -218,13 +218,14 @@ func (s3a *S3ApiServer) getEncryptionConfiguration(bucket string) (*s3_pb.Encryp
// updateEncryptionConfiguration updates the encryption configuration for a bucket
func (s3a *S3ApiServer) updateEncryptionConfiguration(bucket string, encryptionConfig *s3_pb.EncryptionConfiguration) s3err.ErrorCode {
// Update using structured API
// Note: UpdateBucketEncryption -> UpdateBucketMetadata -> setBucketMetadata
// already invalidates the cache synchronously after successful update
err := s3a.UpdateBucketEncryption(bucket, encryptionConfig)
if err != nil {
glog.Errorf("updateEncryptionConfiguration: failed to update encryption config for bucket %s: %v", bucket, err)
return s3err.ErrInternalError
}
// Cache will be updated automatically via metadata subscription
return s3err.ErrNone
}
@@ -242,13 +243,14 @@ func (s3a *S3ApiServer) removeEncryptionConfiguration(bucket string) s3err.Error
}
// Update using structured API
// Note: ClearBucketEncryption -> UpdateBucketMetadata -> setBucketMetadata
// already invalidates the cache synchronously after successful update
err = s3a.ClearBucketEncryption(bucket)
if err != nil {
glog.Errorf("removeEncryptionConfiguration: failed to remove encryption config for bucket %s: %v", bucket, err)
return s3err.ErrInternalError
}
// Cache will be updated automatically via metadata subscription
return s3err.ErrNone
}

View File

@@ -612,26 +612,28 @@ func (s3a *S3ApiServer) getCORSConfiguration(bucket string) (*cors.CORSConfigura
// updateCORSConfiguration updates the CORS configuration for a bucket
func (s3a *S3ApiServer) updateCORSConfiguration(bucket string, corsConfig *cors.CORSConfiguration) s3err.ErrorCode {
// Update using structured API
// Note: UpdateBucketCORS -> UpdateBucketMetadata -> setBucketMetadata
// already invalidates the cache synchronously after successful update
err := s3a.UpdateBucketCORS(bucket, corsConfig)
if err != nil {
glog.Errorf("updateCORSConfiguration: failed to update CORS config for bucket %s: %v", bucket, err)
return s3err.ErrInternalError
}
// Cache will be updated automatically via metadata subscription
return s3err.ErrNone
}
// removeCORSConfiguration removes the CORS configuration for a bucket
func (s3a *S3ApiServer) removeCORSConfiguration(bucket string) s3err.ErrorCode {
// Update using structured API
// Note: ClearBucketCORS -> UpdateBucketMetadata -> setBucketMetadata
// already invalidates the cache synchronously after successful update
err := s3a.ClearBucketCORS(bucket)
if err != nil {
glog.Errorf("removeCORSConfiguration: failed to remove CORS config for bucket %s: %v", bucket, err)
return s3err.ErrInternalError
}
// Cache will be updated automatically via metadata subscription
return s3err.ErrNone
}