fix: add server name validation and clean invalid data before migration 009 (#591)
## Summary This PR adds two important improvements to ensure data integrity: 1. **Adds server name format validation at the API layer** to match the database constraint from migration 008 2. **Includes migration 008 to clean up invalid data** before applying strict constraints in migration 009 ## Changes ### Server Name Validation - Enforces validation at the API layer matching the database constraint - **Namespace pattern**: `[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9]` - Must start and end with alphanumeric characters - Can contain dots and hyphens in the middle - **Name pattern**: `[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9]` - Must start and end with alphanumeric characters - Can contain dots, underscores, and hyphens in the middle - Provides clear, user-friendly error messages indicating which part is invalid - Uses DRY approach with composed regex patterns ### Migration 008: Data Cleanup - Removes servers with invalid names or empty versions (5 servers affected in production) - Fixes invalid status values by setting them to 'active' (1 server affected) - Removes duplicate name+version combinations - Includes comprehensive safety checks to prevent accidental data loss - Must run before migration 009 which adds strict constraints ## Testing - All validator tests pass - Migration includes safety checks that match production data - CI verification included --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
e1f12d6b71
commit
272ee9322d
2 changed files with 230 additions and 0 deletions
|
|
@ -28,6 +28,7 @@ var (
|
|||
|
||||
// Server name validation errors
|
||||
ErrMultipleSlashesInServerName = errors.New("server name cannot contain multiple slashes")
|
||||
ErrInvalidServerNameFormat = errors.New("server name format is invalid")
|
||||
)
|
||||
|
||||
// RepositorySource represents valid repository sources
|
||||
|
|
|
|||
|
|
@ -14,6 +14,18 @@ import (
|
|||
"github.com/modelcontextprotocol/registry/pkg/model"
|
||||
)
|
||||
|
||||
// Server name validation patterns
|
||||
var (
|
||||
// Component patterns for namespace and name parts
|
||||
namespacePattern = `[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9]`
|
||||
namePartPattern = `[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9]`
|
||||
|
||||
// Compiled regexes
|
||||
namespaceRegex = regexp.MustCompile(`^` + namespacePattern + `$`)
|
||||
namePartRegex = regexp.MustCompile(`^` + namePartPattern + `$`)
|
||||
serverNameRegex = regexp.MustCompile(`^` + namespacePattern + `/` + namePartPattern + `$`)
|
||||
)
|
||||
|
||||
// Regexes to detect semver range syntaxes
|
||||
var (
|
||||
// Case 1: comparator ranges
|
||||
|
|
@ -403,11 +415,28 @@ func parseServerName(serverJSON apiv0.ServerJSON) (string, error) {
|
|||
return "", ErrMultipleSlashesInServerName
|
||||
}
|
||||
|
||||
// Split and check for empty parts
|
||||
parts := strings.SplitN(name, "/", 2)
|
||||
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
|
||||
return "", fmt.Errorf("server name must be in format 'dns-namespace/name' with non-empty namespace and name parts")
|
||||
}
|
||||
|
||||
// Validate name format using regex
|
||||
if !serverNameRegex.MatchString(name) {
|
||||
namespace := parts[0]
|
||||
serverName := parts[1]
|
||||
|
||||
// Check which part is invalid for a better error message
|
||||
if !namespaceRegex.MatchString(namespace) {
|
||||
return "", fmt.Errorf("%w: namespace '%s' is invalid. Namespace must start and end with alphanumeric characters, and may contain dots and hyphens in the middle", ErrInvalidServerNameFormat, namespace)
|
||||
}
|
||||
if !namePartRegex.MatchString(serverName) {
|
||||
return "", fmt.Errorf("%w: name '%s' is invalid. Name must start and end with alphanumeric characters, and may contain dots, underscores, and hyphens in the middle", ErrInvalidServerNameFormat, serverName)
|
||||
}
|
||||
// Fallback in case both somehow pass individually but not together
|
||||
return "", fmt.Errorf("%w: invalid format for '%s'", ErrInvalidServerNameFormat, name)
|
||||
}
|
||||
|
||||
return name, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue