fix(validation): enforce UUID v4 in regex pattern
Update UUID validation regex to specifically enforce UUID v4 format instead of accepting generic UUIDs. Update related test cases to use valid v4 UUIDs.
- commit
- e62cfa88c829ba3839affb8f30e7526659d33af2
- author
- brice · · 2026-02-06 05:56 UTC
- parent
- 0f04747
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +11 -7
M internal/adapters/out/filesystem/blob_test.go +3 -3
| @@ -260,7 +260,7 @@ func TestBlobStorage_AppendBlobChunk_NotFound(t *testing.T) { | |||
| 260 | 260 | storage, err := NewBlobStorage(tmpDir, log) | |
| 261 | 261 | require.NoError(t, err) | |
| 262 | 262 | ||
| 263 | - | _, err = storage.AppendBlobChunk("myapp", "00000000-0000-0000-0000-000000000000", []byte("data")) | |
| 263 | + | _, err = storage.AppendBlobChunk("myapp", "00000000-0000-4000-a000-000000000000", []byte("data")) | |
| 264 | 264 | ||
| 265 | 265 | assert.Error(t, err) | |
| 266 | 266 | assert.Contains(t, err.Error(), "upload not found") | |
| @@ -292,7 +292,7 @@ func TestBlobStorage_GetBlobUpload_NotFound(t *testing.T) { | |||
| 292 | 292 | storage, err := NewBlobStorage(tmpDir, log) | |
| 293 | 293 | require.NoError(t, err) | |
| 294 | 294 | ||
| 295 | - | writer, err := storage.GetBlobUpload("00000000-0000-0000-0000-000000000000") | |
| 295 | + | writer, err := storage.GetBlobUpload("00000000-0000-4000-a000-000000000000") | |
| 296 | 296 | ||
| 297 | 297 | assert.Error(t, err) | |
| 298 | 298 | assert.Nil(t, writer) | |
| @@ -355,7 +355,7 @@ func TestBlobStorage_CancelBlobUpload_NotFound(t *testing.T) { | |||
| 355 | 355 | storage, err := NewBlobStorage(tmpDir, log) | |
| 356 | 356 | require.NoError(t, err) | |
| 357 | 357 | ||
| 358 | - | err = storage.CancelBlobUpload("00000000-0000-0000-0000-000000000000") | |
| 358 | + | err = storage.CancelBlobUpload("00000000-0000-4000-a000-000000000000") | |
| 359 | 359 | ||
| 360 | 360 | assert.Error(t, err) | |
| 361 | 361 | assert.Contains(t, err.Error(), "upload not found") | |
| @@ -260,7 +260,7 @@ func TestBlobStorage_AppendBlobChunk_NotFound(t *testing.T) { | |||||
| 260 | storage, err := NewBlobStorage(tmpDir, log) | 260 | storage, err := NewBlobStorage(tmpDir, log) | ||
| 261 | require.NoError(t, err) | 261 | require.NoError(t, err) | ||
| 262 | 262 | ||||
| 263 | - | _, err = storage.AppendBlobChunk("myapp", "00000000-0000-0000-0000-000000000000", []byte("data")) | 263 | + | _, err = storage.AppendBlobChunk("myapp", "00000000-0000-4000-a000-000000000000", []byte("data")) |
| 264 | 264 | ||||
| 265 | assert.Error(t, err) | 265 | assert.Error(t, err) | ||
| 266 | assert.Contains(t, err.Error(), "upload not found") | 266 | assert.Contains(t, err.Error(), "upload not found") | ||
| @@ -292,7 +292,7 @@ func TestBlobStorage_GetBlobUpload_NotFound(t *testing.T) { | |||||
| 292 | storage, err := NewBlobStorage(tmpDir, log) | 292 | storage, err := NewBlobStorage(tmpDir, log) | ||
| 293 | require.NoError(t, err) | 293 | require.NoError(t, err) | ||
| 294 | 294 | ||||
| 295 | - | writer, err := storage.GetBlobUpload("00000000-0000-0000-0000-000000000000") | 295 | + | writer, err := storage.GetBlobUpload("00000000-0000-4000-a000-000000000000") |
| 296 | 296 | ||||
| 297 | assert.Error(t, err) | 297 | assert.Error(t, err) | ||
| 298 | assert.Nil(t, writer) | 298 | assert.Nil(t, writer) | ||
| @@ -355,7 +355,7 @@ func TestBlobStorage_CancelBlobUpload_NotFound(t *testing.T) { | |||||
| 355 | storage, err := NewBlobStorage(tmpDir, log) | 355 | storage, err := NewBlobStorage(tmpDir, log) | ||
| 356 | require.NoError(t, err) | 356 | require.NoError(t, err) | ||
| 357 | 357 | ||||
| 358 | - | err = storage.CancelBlobUpload("00000000-0000-0000-0000-000000000000") | 358 | + | err = storage.CancelBlobUpload("00000000-0000-4000-a000-000000000000") |
| 359 | 359 | ||||
| 360 | assert.Error(t, err) | 360 | assert.Error(t, err) | ||
| 361 | assert.Contains(t, err.Error(), "upload not found") | 361 | assert.Contains(t, err.Error(), "upload not found") | ||
M pkg/validation/path.go +1 -1
| @@ -29,7 +29,7 @@ var digestRegex = regexp.MustCompile(`^(sha256:[a-f0-9]{64}|sha512:[a-f0-9]{128} | |||
| 29 | 29 | ||
| 30 | 30 | // UUID validation for blob uploads: | |
| 31 | 31 | // - Format: standard UUID v4 (e.g., 550e8400-e29b-41d4-a716-446655440000) | |
| 32 | - | var uuidRegex = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`) | |
| 32 | + | var uuidRegex = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$`) | |
| 33 | 33 | ||
| 34 | 34 | // MaxRepositoryNameLength is the maximum allowed length for repository names. | |
| 35 | 35 | const MaxRepositoryNameLength = 256 | |
| @@ -29,7 +29,7 @@ var digestRegex = regexp.MustCompile(`^(sha256:[a-f0-9]{64}|sha512:[a-f0-9]{128} | |||||
| 29 | 29 | ||||
| 30 | // UUID validation for blob uploads: | 30 | // UUID validation for blob uploads: | ||
| 31 | // - Format: standard UUID v4 (e.g., 550e8400-e29b-41d4-a716-446655440000) | 31 | // - Format: standard UUID v4 (e.g., 550e8400-e29b-41d4-a716-446655440000) | ||
| 32 | - | var uuidRegex = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`) | 32 | + | var uuidRegex = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$`) |
| 33 | 33 | ||||
| 34 | // MaxRepositoryNameLength is the maximum allowed length for repository names. | 34 | // MaxRepositoryNameLength is the maximum allowed length for repository names. | ||
| 35 | const MaxRepositoryNameLength = 256 | 35 | const MaxRepositoryNameLength = 256 | ||
M pkg/validation/path_test.go +7 -3
| @@ -164,9 +164,13 @@ func TestValidateUUID(t *testing.T) { | |||
| 164 | 164 | errMsg string | |
| 165 | 165 | }{ | |
| 166 | 166 | // Valid cases - standard UUID v4 | |
| 167 | - | {"valid uuid", "550e8400-e29b-41d4-a716-446655440000", false, ""}, | |
| 168 | - | {"another valid uuid", "6ba7b810-9dad-11d1-80b4-00c04fd430c8", false, ""}, | |
| 169 | - | {"all zeros", "00000000-0000-0000-0000-000000000000", false, ""}, | |
| 167 | + | {"valid uuid v4", "550e8400-e29b-41d4-a716-446655440000", false, ""}, | |
| 168 | + | {"another valid uuid v4", "f47ac10b-58cc-4372-a567-0e02b2c3d479", false, ""}, | |
| 169 | + | ||
| 170 | + | // Invalid cases - not UUID v4 | |
| 171 | + | {"uuid v1 wrong version", "6ba7b810-9dad-11d1-80b4-00c04fd430c8", true, "invalid UUID format"}, | |
| 172 | + | {"all zeros not v4", "00000000-0000-0000-0000-000000000000", true, "invalid UUID format"}, | |
| 173 | + | {"wrong variant nibble", "550e8400-e29b-41d4-7716-446655440000", true, "invalid UUID format"}, | |
| 170 | 174 | ||
| 171 | 175 | // Invalid cases - path traversal | |
| 172 | 176 | {"path traversal", "../etc/passwd", true, "path traversal"}, | |
| @@ -164,9 +164,13 @@ func TestValidateUUID(t *testing.T) { | |||||
| 164 | errMsg string | 164 | errMsg string | ||
| 165 | }{ | 165 | }{ | ||
| 166 | // Valid cases - standard UUID v4 | 166 | // Valid cases - standard UUID v4 | ||
| 167 | - | {"valid uuid", "550e8400-e29b-41d4-a716-446655440000", false, ""}, | 167 | + | {"valid uuid v4", "550e8400-e29b-41d4-a716-446655440000", false, ""}, |
| 168 | - | {"another valid uuid", "6ba7b810-9dad-11d1-80b4-00c04fd430c8", false, ""}, | 168 | + | {"another valid uuid v4", "f47ac10b-58cc-4372-a567-0e02b2c3d479", false, ""}, |
| 169 | - | {"all zeros", "00000000-0000-0000-0000-000000000000", false, ""}, | 169 | + | |
| 170 | + | // Invalid cases - not UUID v4 | |||
| 171 | + | {"uuid v1 wrong version", "6ba7b810-9dad-11d1-80b4-00c04fd430c8", true, "invalid UUID format"}, | |||
| 172 | + | {"all zeros not v4", "00000000-0000-0000-0000-000000000000", true, "invalid UUID format"}, | |||
| 173 | + | {"wrong variant nibble", "550e8400-e29b-41d4-7716-446655440000", true, "invalid UUID format"}, | |||
| 170 | 174 | ||||
| 171 | // Invalid cases - path traversal | 175 | // Invalid cases - path traversal | ||
| 172 | {"path traversal", "../etc/passwd", true, "path traversal"}, | 176 | {"path traversal", "../etc/passwd", true, "path traversal"}, | ||