Skip to content

Commit 09f0201

Browse files
committed
fix: move server name pattern validation to code for better error messages
- Add regex validation in parseServerName with specific error messages - Validate namespace allows only alphanumeric, dots and hyphens - Validate name allows only alphanumeric, dots, underscores and hyphens - Update tests to expect descriptive error messages - Add tests for invalid character validation - Fix test server names to use valid format As suggested by @joelverhagen in PR feedback
1 parent 74c0cc2 commit 09f0201

File tree

4 files changed

+54
-7
lines changed

4 files changed

+54
-7
lines changed

internal/api/handlers/v0/edit_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ func TestEditServerEndpoint(t *testing.T) {
237237
},
238238
serverID: testServerID,
239239
expectedStatus: http.StatusBadRequest,
240-
expectedError: "Bad Request",
240+
expectedError: "server name must be in format 'dns-namespace/name'",
241241
},
242242
{
243243
name: "cannot undelete server",

internal/api/handlers/v0/publish_integration_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ func TestPublishIntegration(t *testing.T) {
141141

142142
t.Run("publish fails with missing authorization header", func(t *testing.T) {
143143
publishReq := apiv0.ServerJSON{
144-
Name: "test-server",
144+
Name: "com.example/test-server",
145145
}
146146

147147
body, err := json.Marshal(publishReq)

internal/api/handlers/v0/publish_test.go

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func TestPublishEndpoint(t *testing.T) {
8080
{
8181
name: "successful publish with no auth (AuthMethodNone)",
8282
requestBody: apiv0.ServerJSON{
83-
Name: "example/test-server",
83+
Name: "com.example/test-server",
8484
Description: "A test server without auth",
8585
Repository: model.Repository{
8686
URL: "https://github.com/example/test-server",
@@ -92,7 +92,7 @@ func TestPublishEndpoint(t *testing.T) {
9292
tokenClaims: &auth.JWTClaims{
9393
AuthMethod: auth.MethodNone,
9494
Permissions: []auth.Permission{
95-
{Action: auth.PermissionActionPublish, ResourcePattern: "example/*"},
95+
{Action: auth.PermissionActionPublish, ResourcePattern: "com.example/*"},
9696
},
9797
},
9898
setupRegistryService: func(_ service.RegistryService) {
@@ -127,7 +127,7 @@ func TestPublishEndpoint(t *testing.T) {
127127
{
128128
name: "invalid token",
129129
requestBody: apiv0.ServerJSON{
130-
Name: "test-server",
130+
Name: "com.example/test-server",
131131
Description: "A test server",
132132
Version: "1.0.0",
133133
},
@@ -165,7 +165,7 @@ func TestPublishEndpoint(t *testing.T) {
165165
{
166166
name: "registry service error",
167167
requestBody: apiv0.ServerJSON{
168-
Name: "example/test-server",
168+
Name: "com.example/test-server",
169169
Description: "A test server",
170170
Version: "1.0.0",
171171
Repository: model.Repository{
@@ -183,7 +183,7 @@ func TestPublishEndpoint(t *testing.T) {
183183
setupRegistryService: func(registry service.RegistryService) {
184184
// Pre-publish the same server to cause duplicate version error
185185
existingServer := apiv0.ServerJSON{
186-
Name: "example/test-server",
186+
Name: "com.example/test-server",
187187
Description: "Existing test server",
188188
Version: "1.0.0",
189189
Repository: model.Repository{
@@ -353,6 +353,40 @@ func TestPublishEndpoint(t *testing.T) {
353353
expectedStatus: http.StatusBadRequest,
354354
expectedError: "server name format is invalid: must contain exactly one slash",
355355
},
356+
{
357+
name: "invalid server name - invalid namespace characters",
358+
requestBody: apiv0.ServerJSON{
359+
Name: "com.example@/test-server",
360+
Description: "Server with invalid namespace characters",
361+
Version: "1.0.0",
362+
},
363+
tokenClaims: &auth.JWTClaims{
364+
AuthMethod: auth.MethodNone,
365+
Permissions: []auth.Permission{
366+
{Action: auth.PermissionActionPublish, ResourcePattern: "*"},
367+
},
368+
},
369+
setupRegistryService: func(_ service.RegistryService) {},
370+
expectedStatus: http.StatusBadRequest,
371+
expectedError: "server namespace 'com.example@' contains invalid characters",
372+
},
373+
{
374+
name: "invalid server name - invalid name characters",
375+
requestBody: apiv0.ServerJSON{
376+
Name: "com.example/test@server",
377+
Description: "Server with invalid name characters",
378+
Version: "1.0.0",
379+
},
380+
tokenClaims: &auth.JWTClaims{
381+
AuthMethod: auth.MethodNone,
382+
Permissions: []auth.Permission{
383+
{Action: auth.PermissionActionPublish, ResourcePattern: "*"},
384+
},
385+
},
386+
setupRegistryService: func(_ service.RegistryService) {},
387+
expectedStatus: http.StatusBadRequest,
388+
expectedError: "server name 'test@server' contains invalid characters",
389+
},
356390
}
357391

358392
for _, tc := range testCases {

internal/validators/validators.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,19 @@ func parseServerName(serverJSON apiv0.ServerJSON) (string, error) {
413413
return "", fmt.Errorf("server name must be in format 'dns-namespace/name' with non-empty namespace and name parts")
414414
}
415415

416+
// Validate namespace and name format according to schema
417+
// Pattern: ^[a-zA-Z0-9.-]+/[a-zA-Z0-9._-]+$
418+
namespacePattern := regexp.MustCompile(`^[a-zA-Z0-9.-]+$`)
419+
namePattern := regexp.MustCompile(`^[a-zA-Z0-9._-]+$`)
420+
421+
if !namespacePattern.MatchString(parts[0]) {
422+
return "", fmt.Errorf("server namespace '%s' contains invalid characters: only alphanumeric characters, dots (.) and hyphens (-) are allowed", parts[0])
423+
}
424+
425+
if !namePattern.MatchString(parts[1]) {
426+
return "", fmt.Errorf("server name '%s' contains invalid characters: only alphanumeric characters, dots (.), underscores (_) and hyphens (-) are allowed", parts[1])
427+
}
428+
416429
return name, nil
417430
}
418431

0 commit comments

Comments
 (0)