mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
74 lines
2.8 KiB
Go
74 lines
2.8 KiB
Go
package interceptors
|
|||
|
|
|
||
|
|
import "strings"
|
||
|
|
|
||
|
|
// packageInfo represents parsed package information from a registry URL.
|
||
|
|
// All ecosystem-specific package info types must implement this interface.
|
||
|
|
type packageInfo interface {
|
||
|
|
// GetName returns the package name
|
||
|
|
GetName() string
|
||
|
|
|
||
|
|
// GetVersion returns the package version (may be empty for metadata requests)
|
||
|
|
GetVersion() string
|
||
|
|
|
||
|
|
// IsFileDownload returns true if this is a file download request (tarball, wheel, etc.)
|
||
|
|
// Returns false for metadata requests (package index, version info, etc.)
|
||
|
|
IsFileDownload() bool
|
||
|
|
}
|
||
|
|
|
||
|
|
// registryURLParser parses registry-specific URLs to extract package information.
|
||
|
|
// Each registry (npm, pypi, etc.) implements this interface with its own URL parsing logic.
|
||
|
|
type registryURLParser interface {
|
||
|
|
// ParseURL parses a URL path and returns package information.
|
||
|
|
// Returns an error if the URL cannot be parsed.
|
||
|
|
ParseURL(urlPath string) (packageInfo, error)
|
||
|
|
}
|
||
|
|
|
||
|
|
// registryConfig defines configuration for a package registry endpoint.
|
||
|
|
// This is the common configuration structure used by all ecosystem interceptors.
|
||
|
|
type registryConfig struct {
|
||
|
|
// Host is the hostname of the registry
|
||
|
|
Host string
|
||
|
|
|
||
|
|
// SupportedForAnalysis indicates whether this registry supports malware analysis.
|
||
|
|
// Some registries (like private registries or test instances) may not support analysis.
|
||
|
|
SupportedForAnalysis bool
|
||
|
|
|
||
|
|
// Parser is the URL parser for this registry
|
||
|
|
Parser registryURLParser
|
||
|
|
}
|
||
|
|
|
||
|
|
// registryConfigMap is a map of hostname to registry configuration
|
||
|
|
type registryConfigMap map[string]*registryConfig
|
||
|
|
|
||
|
|
// GetConfigForHostname returns the configuration for a hostname with subdomain matching support.
|
||
|
|
// It first checks for an exact match, then checks if the hostname is a subdomain of any configured registry.
|
||
|
|
func (m registryConfigMap) GetConfigForHostname(hostname string) *registryConfig {
|
||
|
|
// Check exact match first
|
||
|
|
if config, exists := m[hostname]; exists {
|
||
|
|
return config
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check subdomain match: hostname could be "cdn.registry.example.org" matching "registry.example.org".
|
||
|
|
// Defensive: Since Go map iteration order is non-deterministic, if multiple endpoints could match
|
||
|
|
// (e.g., both "example.org" and "registry.example.org"), we select the longest (most specific) one
|
||
|
|
// to ensure consistent behavior. In practice, our configured endpoints don't overlap.
|
||
|
|
var bestConfig *registryConfig
|
||
|
|
bestLen := 0
|
||
|
|
for endpoint, config := range m {
|
||
|
|
if strings.HasSuffix(hostname, "."+endpoint) {
|
||
|
|
if len(endpoint) > bestLen {
|
||
|
|
bestLen = len(endpoint)
|
||
|
|
bestConfig = config
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return bestConfig
|
||
|
|
}
|
||
|
|
|
||
|
|
// ContainsHostname checks if the hostname matches any configured registry (exact or subdomain match)
|
||
|
|
func (m registryConfigMap) ContainsHostname(hostname string) bool {
|
||
|
|
return m.GetConfigForHostname(hostname) != nil
|
||
|
|
}
|