From f70cd054043bb6327b6b0f3b9e54a1f6d502d2a2 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 13 Dec 2025 14:33:46 -0800 Subject: 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. --- weed/s3api/cors/cors_test.go | 47 ++++++++++++++++++++++++++++++++++++-- weed/s3api/s3_bucket_encryption.go | 6 +++-- weed/s3api/s3api_bucket_config.go | 6 +++-- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/weed/s3api/cors/cors_test.go b/weed/s3api/cors/cors_test.go index 8494a284d..7b72ee482 100644 --- a/weed/s3api/cors/cors_test.go +++ b/weed/s3api/cors/cors_test.go @@ -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", }, }, } diff --git a/weed/s3api/s3_bucket_encryption.go b/weed/s3api/s3_bucket_encryption.go index 0d54c2cd5..cc9a0220f 100644 --- a/weed/s3api/s3_bucket_encryption.go +++ b/weed/s3api/s3_bucket_encryption.go @@ -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 } diff --git a/weed/s3api/s3api_bucket_config.go b/weed/s3api/s3api_bucket_config.go index 6076f0108..94fd493a8 100644 --- a/weed/s3api/s3api_bucket_config.go +++ b/weed/s3api/s3api_bucket_config.go @@ -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 } -- cgit v1.2.3