Skip to content

Commit bb64ce1

Browse files
ossamalafhelJoel VerhagenAvish34Adam Jones
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 - Move regex patterns to utils.go for centralization - Add error constants to constants.go for consistency - Remove redundant validation checks Based on feedback from PR reviewers, this implementation: - Maintains HTTP 400 status instead of 422 for better developer experience - Provides clear, actionable error messages - Follows existing codebase patterns for validation - Centralizes validation logic for maintainability Co-authored-by: Joel Verhagen <[email protected]> Co-authored-by: Avish <[email protected]> Co-authored-by: Adam Jones <[email protected]>
1 parent 74c0cc2 commit bb64ce1

File tree

12 files changed

+99
-42
lines changed

12 files changed

+99
-42
lines changed

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
go-version-file: 'go.mod'
2525

2626
- name: Install cosign
27-
uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159
27+
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62
2828

2929
- name: Install Syft
3030
uses: anchore/sbom-action/[email protected]

go.mod

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,17 @@ require (
99
github.com/golang-jwt/jwt/v5 v5.3.0
1010
github.com/google/uuid v1.6.0
1111
github.com/jackc/pgx/v5 v5.7.6
12-
github.com/prometheus/client_golang v1.23.0
12+
github.com/prometheus/client_golang v1.23.2
1313
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
1414
github.com/stretchr/testify v1.11.1
1515
go.opentelemetry.io/contrib/instrumentation/runtime v0.63.0
1616
go.opentelemetry.io/otel v1.38.0
17-
go.opentelemetry.io/otel/exporters/prometheus v0.59.1
17+
go.opentelemetry.io/otel/exporters/prometheus v0.60.0
1818
go.opentelemetry.io/otel/metric v1.38.0
1919
go.opentelemetry.io/otel/sdk v1.38.0
2020
go.opentelemetry.io/otel/sdk/metric v1.38.0
2121
golang.org/x/mod v0.28.0
22-
golang.org/x/oauth2 v0.30.0
22+
golang.org/x/oauth2 v0.31.0
2323
)
2424

2525
require (
@@ -36,15 +36,16 @@ require (
3636
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
3737
github.com/pmezard/go-difflib v1.0.0 // indirect
3838
github.com/prometheus/client_model v0.6.2 // indirect
39-
github.com/prometheus/common v0.65.0 // indirect
40-
github.com/prometheus/otlptranslator v0.0.0-20250717125610-8549f4ab4f8f // indirect
39+
github.com/prometheus/common v0.66.1 // indirect
40+
github.com/prometheus/otlptranslator v0.0.2 // indirect
4141
github.com/prometheus/procfs v0.17.0 // indirect
4242
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
4343
go.opentelemetry.io/otel/trace v1.38.0 // indirect
44+
go.yaml.in/yaml/v2 v2.4.2 // indirect
4445
golang.org/x/crypto v0.41.0 // indirect
4546
golang.org/x/sync v0.16.0 // indirect
4647
golang.org/x/sys v0.35.0 // indirect
4748
golang.org/x/text v0.28.0 // indirect
48-
google.golang.org/protobuf v1.36.6 // indirect
49+
google.golang.org/protobuf v1.36.8 // indirect
4950
gopkg.in/yaml.v3 v3.0.1 // indirect
5051
)

go.sum

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,14 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
4646
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
4747
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4848
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
49-
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
50-
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
49+
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
50+
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
5151
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
5252
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
53-
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
54-
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
55-
github.com/prometheus/otlptranslator v0.0.0-20250717125610-8549f4ab4f8f h1:QQB6SuvGZjK8kdc2YaLJpYhV8fxauOsjE6jgcL6YJ8Q=
56-
github.com/prometheus/otlptranslator v0.0.0-20250717125610-8549f4ab4f8f/go.mod h1:P8AwMgdD7XEr6QRUJ2QWLpiAZTgTE2UYgjlu3svompI=
53+
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
54+
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
55+
github.com/prometheus/otlptranslator v0.0.2 h1:+1CdeLVrRQ6Psmhnobldo0kTp96Rj80DRXRd5OSnMEQ=
56+
github.com/prometheus/otlptranslator v0.0.2/go.mod h1:P8AwMgdD7XEr6QRUJ2QWLpiAZTgTE2UYgjlu3svompI=
5757
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
5858
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
5959
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
@@ -71,8 +71,8 @@ go.opentelemetry.io/contrib/instrumentation/runtime v0.63.0 h1:PeBoRj6af6xMI7qCu
7171
go.opentelemetry.io/contrib/instrumentation/runtime v0.63.0/go.mod h1:ingqBCtMCe8I4vpz/UVzCW6sxoqgZB37nao91mLQ3Bw=
7272
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
7373
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
74-
go.opentelemetry.io/otel/exporters/prometheus v0.59.1 h1:HcpSkTkJbggT8bjYP+BjyqPWlD17BH9C5CYNKeDzmcA=
75-
go.opentelemetry.io/otel/exporters/prometheus v0.59.1/go.mod h1:0FJL+gjuUoM07xzik3KPBaN+nz/CoB15kV6WLMiXZag=
74+
go.opentelemetry.io/otel/exporters/prometheus v0.60.0 h1:cGtQxGvZbnrWdC2GyjZi0PDKVSLWP/Jocix3QWfXtbo=
75+
go.opentelemetry.io/otel/exporters/prometheus v0.60.0/go.mod h1:hkd1EekxNo69PTV4OWFGZcKQiIqg0RfuWExcPKFvepk=
7676
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
7777
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
7878
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
@@ -83,20 +83,22 @@ go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJr
8383
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
8484
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
8585
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
86+
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
87+
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
8688
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
8789
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
8890
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
8991
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
90-
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
91-
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
92+
golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo=
93+
golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
9294
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
9395
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
9496
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
9597
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
9698
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
9799
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
98-
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
99-
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
100+
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
101+
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
100102
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
101103
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
102104
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

internal/api/handlers/v0/edit.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/modelcontextprotocol/registry/internal/config"
1212
"github.com/modelcontextprotocol/registry/internal/database"
1313
"github.com/modelcontextprotocol/registry/internal/service"
14+
"github.com/modelcontextprotocol/registry/internal/validators"
1415
apiv0 "github.com/modelcontextprotocol/registry/pkg/api/v0"
1516
"github.com/modelcontextprotocol/registry/pkg/model"
1617
)
@@ -68,6 +69,10 @@ func RegisterEditEndpoints(api huma.API, registry service.RegistryService, cfg *
6869

6970
// Prevent renaming servers
7071
if currentServer.Name != input.Body.Name {
72+
// Validate the new name format before rejecting the rename
73+
if err := validators.ValidatePublishRequest(ctx, input.Body, cfg); err != nil {
74+
return nil, huma.Error400BadRequest(err.Error())
75+
}
7176
return nil, huma.Error400BadRequest("Cannot rename server")
7277
}
7378

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: 47 additions & 14 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{
@@ -224,7 +224,6 @@ func TestPublishEndpoint(t *testing.T) {
224224
setupRegistryService: func(_ service.RegistryService) {},
225225
expectedStatus: http.StatusOK,
226226
},
227-
// IB-2-registry: Integration test for multi-slash server name rejection
228227
{
229228
name: "invalid server name - multiple slashes (two slashes)",
230229
requestBody: apiv0.ServerJSON{
@@ -245,7 +244,7 @@ func TestPublishEndpoint(t *testing.T) {
245244
},
246245
setupRegistryService: func(_ service.RegistryService) {},
247246
expectedStatus: http.StatusBadRequest,
248-
expectedError: "server name format is invalid: must contain exactly one slash",
247+
expectedError: "server name cannot contain multiple slashes",
249248
},
250249
{
251250
name: "invalid server name - multiple slashes (three slashes)",
@@ -262,7 +261,7 @@ func TestPublishEndpoint(t *testing.T) {
262261
},
263262
setupRegistryService: func(_ service.RegistryService) {},
264263
expectedStatus: http.StatusBadRequest,
265-
expectedError: "server name format is invalid: must contain exactly one slash",
264+
expectedError: "server name cannot contain multiple slashes",
266265
},
267266
{
268267
name: "invalid server name - consecutive slashes",
@@ -279,7 +278,7 @@ func TestPublishEndpoint(t *testing.T) {
279278
},
280279
setupRegistryService: func(_ service.RegistryService) {},
281280
expectedStatus: http.StatusBadRequest,
282-
expectedError: "server name format is invalid: must contain exactly one slash",
281+
expectedError: "server name cannot contain multiple slashes",
283282
},
284283
{
285284
name: "invalid server name - URL-like path",
@@ -296,7 +295,7 @@ func TestPublishEndpoint(t *testing.T) {
296295
},
297296
setupRegistryService: func(_ service.RegistryService) {},
298297
expectedStatus: http.StatusBadRequest,
299-
expectedError: "server name format is invalid: must contain exactly one slash",
298+
expectedError: "server name cannot contain multiple slashes",
300299
},
301300
{
302301
name: "invalid server name - many slashes",
@@ -313,7 +312,7 @@ func TestPublishEndpoint(t *testing.T) {
313312
},
314313
setupRegistryService: func(_ service.RegistryService) {},
315314
expectedStatus: http.StatusBadRequest,
316-
expectedError: "server name format is invalid: must contain exactly one slash",
315+
expectedError: "server name cannot contain multiple slashes",
317316
},
318317
{
319318
name: "invalid server name - with packages and remotes",
@@ -351,7 +350,41 @@ func TestPublishEndpoint(t *testing.T) {
351350
},
352351
setupRegistryService: func(_ service.RegistryService) {},
353352
expectedStatus: http.StatusBadRequest,
354-
expectedError: "server name format is invalid: must contain exactly one slash",
353+
expectedError: "server name cannot contain multiple slashes",
354+
},
355+
{
356+
name: "invalid server name - invalid namespace characters",
357+
requestBody: apiv0.ServerJSON{
358+
Name: "com.example@/test-server",
359+
Description: "Server with invalid namespace characters",
360+
Version: "1.0.0",
361+
},
362+
tokenClaims: &auth.JWTClaims{
363+
AuthMethod: auth.MethodNone,
364+
Permissions: []auth.Permission{
365+
{Action: auth.PermissionActionPublish, ResourcePattern: "*"},
366+
},
367+
},
368+
setupRegistryService: func(_ service.RegistryService) {},
369+
expectedStatus: http.StatusBadRequest,
370+
expectedError: "namespace contains invalid characters",
371+
},
372+
{
373+
name: "invalid server name - invalid name characters",
374+
requestBody: apiv0.ServerJSON{
375+
Name: "com.example/test@server",
376+
Description: "Server with invalid name characters",
377+
Version: "1.0.0",
378+
},
379+
tokenClaims: &auth.JWTClaims{
380+
AuthMethod: auth.MethodNone,
381+
Permissions: []auth.Permission{
382+
{Action: auth.PermissionActionPublish, ResourcePattern: "*"},
383+
},
384+
},
385+
setupRegistryService: func(_ service.RegistryService) {},
386+
expectedStatus: http.StatusBadRequest,
387+
expectedError: "name contains invalid characters",
355388
},
356389
}
357390

@@ -504,8 +537,8 @@ func TestPublishEndpoint_MultipleSlashesEdgeCases(t *testing.T) {
504537
"%s: expected status %d, got %d", tc.description, tc.expectedStatus, rr.Code)
505538

506539
if tc.expectedStatus == http.StatusBadRequest {
507-
assert.Contains(t, rr.Body.String(), "server name format is invalid: must contain exactly one slash",
508-
"%s: should contain specific error message", tc.description)
540+
assert.Contains(t, rr.Body.String(), "server name",
541+
"%s: should contain server name validation error", tc.description)
509542
}
510543
})
511544
}

internal/service/registry_service.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func (s *registryServiceImpl) Publish(req apiv0.ServerJSON) (*apiv0.ServerJSON,
7777
defer cancel()
7878

7979
// Validate the request
80-
if err := validators.ValidatePublishRequest(req, s.cfg); err != nil {
80+
if err := validators.ValidatePublishRequest(ctx, req, s.cfg); err != nil {
8181
return nil, err
8282
}
8383

@@ -206,7 +206,7 @@ func (s *registryServiceImpl) EditServer(id string, req apiv0.ServerJSON) (*apiv
206206
defer cancel()
207207

208208
// Validate the request
209-
if err := validators.ValidatePublishRequest(req, s.cfg); err != nil {
209+
if err := validators.ValidatePublishRequest(ctx, req, s.cfg); err != nil {
210210
return nil, err
211211
}
212212

internal/validators/constants.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ var (
2727
ErrArgumentDefaultStartsWithName = errors.New("argument default cannot start with the argument name")
2828

2929
// Server name validation errors
30-
ErrInvalidServerNameFormat = errors.New("server name format is invalid: must contain exactly one slash")
3130
ErrMultipleSlashesInServerName = errors.New("server name cannot contain multiple slashes")
31+
ErrInvalidNamespaceCharacters = errors.New("namespace contains invalid characters: only alphanumeric characters, dots (.) and hyphens (-) are allowed")
32+
ErrInvalidNameCharacters = errors.New("name contains invalid characters: only alphanumeric characters, dots (.), underscores (_) and hyphens (-) are allowed")
3233
)
3334

3435
// RepositorySource represents valid repository sources

internal/validators/utils.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ var (
1212
// For example: // - GitHub: https://github.com/user/repo
1313
githubURLRegex = regexp.MustCompile(`^https?://(www\.)?github\.com/[\w.-]+/[\w.-]+/?$`)
1414
gitlabURLRegex = regexp.MustCompile(`^https?://(www\.)?gitlab\.com/[\w.-]+/[\w.-]+/?$`)
15+
16+
// Regular expressions for validating server name components
17+
// Pattern: ^[a-zA-Z0-9.-]+/[a-zA-Z0-9._-]+$
18+
serverNamespacePattern = regexp.MustCompile(`^[a-zA-Z0-9.-]+$`)
19+
serverNamePattern = regexp.MustCompile(`^[a-zA-Z0-9._-]+$`)
1520
)
1621

1722
// IsValidRepositoryURL checks if the given URL is valid for the specified repository source

0 commit comments

Comments
 (0)