|
6 | 6 | "fmt" |
7 | 7 | "net/url" |
8 | 8 | "regexp" |
9 | | - "slices" |
10 | 9 | "strings" |
11 | 10 |
|
12 | 11 | "github.com/modelcontextprotocol/registry/internal/config" |
@@ -108,16 +107,6 @@ func ValidateServerJSON(serverJSON *apiv0.ServerJSON) error { |
108 | 107 | } |
109 | 108 | } |
110 | 109 |
|
111 | | - // Validate reverse-DNS namespace matching for remote URLs |
112 | | - if err := validateRemoteNamespaceMatch(*serverJSON); err != nil { |
113 | | - return err |
114 | | - } |
115 | | - |
116 | | - // Validate reverse-DNS namespace matching for website URL |
117 | | - if err := validateWebsiteURLNamespaceMatch(*serverJSON); err != nil { |
118 | | - return err |
119 | | - } |
120 | | - |
121 | 110 | return nil |
122 | 111 | } |
123 | 112 |
|
@@ -509,99 +498,3 @@ func parseServerName(serverJSON apiv0.ServerJSON) (string, error) { |
509 | 498 |
|
510 | 499 | return name, nil |
511 | 500 | } |
512 | | - |
513 | | -// validateRemoteNamespaceMatch validates that remote URLs match the reverse-DNS namespace |
514 | | -func validateRemoteNamespaceMatch(serverJSON apiv0.ServerJSON) error { |
515 | | - namespace := serverJSON.Name |
516 | | - |
517 | | - for _, remote := range serverJSON.Remotes { |
518 | | - if err := validateRemoteURLMatchesNamespace(remote.URL, namespace); err != nil { |
519 | | - return fmt.Errorf("remote URL %s does not match namespace %s: %w", remote.URL, namespace, err) |
520 | | - } |
521 | | - } |
522 | | - |
523 | | - return nil |
524 | | -} |
525 | | - |
526 | | -// validateWebsiteURLNamespaceMatch validates that website URL matches the reverse-DNS namespace |
527 | | -func validateWebsiteURLNamespaceMatch(serverJSON apiv0.ServerJSON) error { |
528 | | - // Skip validation if website URL is not provided |
529 | | - if serverJSON.WebsiteURL == "" { |
530 | | - return nil |
531 | | - } |
532 | | - |
533 | | - namespace := serverJSON.Name |
534 | | - if err := validateRemoteURLMatchesNamespace(serverJSON.WebsiteURL, namespace); err != nil { |
535 | | - return fmt.Errorf("websiteUrl %s does not match namespace %s: %w", serverJSON.WebsiteURL, namespace, err) |
536 | | - } |
537 | | - |
538 | | - return nil |
539 | | -} |
540 | | - |
541 | | -// validateRemoteURLMatchesNamespace checks if a remote URL's hostname matches the publisher domain from the namespace |
542 | | -func validateRemoteURLMatchesNamespace(remoteURL, namespace string) error { |
543 | | - // Parse the URL to extract the hostname |
544 | | - parsedURL, err := url.Parse(remoteURL) |
545 | | - if err != nil { |
546 | | - return fmt.Errorf("invalid URL format: %w", err) |
547 | | - } |
548 | | - |
549 | | - hostname := parsedURL.Hostname() |
550 | | - if hostname == "" { |
551 | | - return fmt.Errorf("URL must have a valid hostname") |
552 | | - } |
553 | | - |
554 | | - // Skip validation for localhost and local development URLs |
555 | | - if hostname == "localhost" || strings.HasSuffix(hostname, ".localhost") || hostname == "127.0.0.1" { |
556 | | - return nil |
557 | | - } |
558 | | - |
559 | | - // Extract publisher domain from reverse-DNS namespace |
560 | | - publisherDomain := extractPublisherDomainFromNamespace(namespace) |
561 | | - if publisherDomain == "" { |
562 | | - return fmt.Errorf("invalid namespace format: cannot extract domain from %s", namespace) |
563 | | - } |
564 | | - |
565 | | - // Check if the remote URL hostname matches the publisher domain or is a subdomain |
566 | | - if !isValidHostForDomain(hostname, publisherDomain) { |
567 | | - return fmt.Errorf("remote URL host %s does not match publisher domain %s", hostname, publisherDomain) |
568 | | - } |
569 | | - |
570 | | - return nil |
571 | | -} |
572 | | - |
573 | | -// extractPublisherDomainFromNamespace converts reverse-DNS namespace to normal domain format |
574 | | -// e.g., "com.example" -> "example.com" |
575 | | -func extractPublisherDomainFromNamespace(namespace string) string { |
576 | | - // Extract the namespace part before the first slash |
577 | | - namespacePart := namespace |
578 | | - if slashIdx := strings.Index(namespace, "/"); slashIdx != -1 { |
579 | | - namespacePart = namespace[:slashIdx] |
580 | | - } |
581 | | - |
582 | | - // Split into parts and reverse them to get normal domain format |
583 | | - parts := strings.Split(namespacePart, ".") |
584 | | - if len(parts) < 2 { |
585 | | - return "" |
586 | | - } |
587 | | - |
588 | | - // Reverse the parts to convert from reverse-DNS to normal domain |
589 | | - slices.Reverse(parts) |
590 | | - |
591 | | - return strings.Join(parts, ".") |
592 | | -} |
593 | | - |
594 | | -// isValidHostForDomain checks if a hostname is the domain or a subdomain of the publisher domain |
595 | | -func isValidHostForDomain(hostname, publisherDomain string) bool { |
596 | | - // Exact match |
597 | | - if hostname == publisherDomain { |
598 | | - return true |
599 | | - } |
600 | | - |
601 | | - // Subdomain match - hostname should end with "." + publisherDomain |
602 | | - if strings.HasSuffix(hostname, "."+publisherDomain) { |
603 | | - return true |
604 | | - } |
605 | | - |
606 | | - return false |
607 | | -} |
0 commit comments