package config import ( "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "os" "path/filepath" "strings" ) const ( PackageComponentRun = "run" PackageConfigEnv = "RUN_PACKAGE_CONFIG" ) type PackageConfig struct { Kind string `json:"kind"` ServerInstanceID string `json:"serverInstanceId"` PluginID string `json:"pluginId"` RunEndpointID string `json:"runEndpointId,omitempty"` ProfileKey string `json:"profileKey,omitempty"` TargetOS string `json:"targetOs"` TargetArch string `json:"targetArch"` SecretRef string `json:"secretRef"` KeyGeneration int `json:"keyGeneration"` AuthKey string `json:"authKey"` } type PackageIdentity struct { Kind string `json:"kind"` ServerInstanceID string `json:"serverInstanceId"` PluginID string `json:"pluginId"` RunEndpointID string `json:"runEndpointId,omitempty"` ProfileKey string `json:"profileKey,omitempty"` TargetOS string `json:"targetOs"` TargetArch string `json:"targetArch"` SecretRef string `json:"secretRef"` KeyGeneration int `json:"keyGeneration"` KeyFingerprint string `json:"keyFingerprint"` } type ComponentAuthResult struct { ServerInstanceID string Kind string ProfileKey string KeyGeneration int Allowed bool Reason string } func LoadPackageConfig(path string) (PackageConfig, error) { body, err := os.ReadFile(path) if err != nil { return PackageConfig{}, fmt.Errorf("read run package config: %w", err) } var cfg PackageConfig if err := json.Unmarshal(body, &cfg); err != nil { return PackageConfig{}, fmt.Errorf("decode run package config: %w", err) } if err := ValidatePackageConfig(cfg); err != nil { return PackageConfig{}, err } return cfg, nil } func LoadPackageConfigFromEnv() (PackageConfig, bool, error) { path := strings.TrimSpace(os.Getenv(PackageConfigEnv)) if path == "" { return PackageConfig{}, false, nil } cfg, err := LoadPackageConfig(path) return cfg, true, err } func LoadPackageConfigBesideExecutable() (PackageConfig, bool, error) { executable, err := os.Executable() if err != nil { return PackageConfig{}, false, nil } path := filepath.Join(filepath.Dir(executable), "config.json") if _, err := os.Stat(path); err != nil { if os.IsNotExist(err) { return PackageConfig{}, false, nil } return PackageConfig{}, false, err } cfg, err := LoadPackageConfig(path) return cfg, true, err } func ValidatePackageConfig(cfg PackageConfig) error { var violations []string if cfg.Kind != PackageComponentRun { violations = append(violations, "kind is invalid") } if !safeIdentifier(cfg.ServerInstanceID) { violations = append(violations, "serverInstanceId is invalid") } if !safePluginID(cfg.PluginID) { violations = append(violations, "pluginId is invalid") } if cfg.RunEndpointID != "" && !safeIdentifier(cfg.RunEndpointID) { violations = append(violations, "runEndpointId is invalid") } if cfg.ProfileKey != "" && !safeLogicalKey(cfg.ProfileKey) { violations = append(violations, "profileKey is invalid") } if !safeRuntimeTarget(cfg.TargetOS, cfg.TargetArch) { violations = append(violations, "target platform is invalid") } if !strings.HasPrefix(cfg.SecretRef, "secret://runtime-keys/") || strings.ContainsAny(cfg.SecretRef, " \t\r\n") { violations = append(violations, "secretRef is invalid") } if cfg.KeyGeneration <= 0 { violations = append(violations, "keyGeneration must be positive") } if strings.TrimSpace(cfg.AuthKey) == "" { violations = append(violations, "authKey is required") } if len(violations) > 0 { return fmt.Errorf("invalid run package config: %s", strings.Join(violations, "; ")) } return nil } func ApplyPackageConfig(base Config, pkg PackageConfig) Config { base.RegistrationToken = pkg.AuthKey base.ServerInstanceID = pkg.ServerInstanceID base.PluginID = pkg.PluginID base.ComponentKind = pkg.Kind base.ComponentKey = packageComponentKey(pkg) base.KeyGeneration = pkg.KeyGeneration base.SecretRef = pkg.SecretRef if pkg.RunEndpointID != "" { base.RunEndpointID = pkg.RunEndpointID } if base.DisplayName == "" || base.DisplayName == DefaultDisplayName { base.DisplayName = "Run " + pkg.ServerInstanceID } return base } func (cfg PackageConfig) Identity() PackageIdentity { return PackageIdentity{ Kind: cfg.Kind, ServerInstanceID: cfg.ServerInstanceID, PluginID: cfg.PluginID, RunEndpointID: cfg.RunEndpointID, ProfileKey: cfg.ProfileKey, TargetOS: cfg.TargetOS, TargetArch: cfg.TargetArch, SecretRef: cfg.SecretRef, KeyGeneration: cfg.KeyGeneration, KeyFingerprint: fingerprint(cfg.AuthKey), } } func (cfg PackageConfig) IdentityDiagnostics() map[string]string { identity := cfg.Identity() return map[string]string{ "kind": identity.Kind, "serverInstanceId": identity.ServerInstanceID, "pluginId": identity.PluginID, "runEndpointId": identity.RunEndpointID, "profileKey": identity.ProfileKey, "target": identity.TargetOS + "/" + identity.TargetArch, "secretRef": identity.SecretRef, "keyGeneration": fmt.Sprintf("%d", identity.KeyGeneration), "keyFingerprint": identity.KeyFingerprint, } } func AuthenticatePackageGeneration(pkg PackageConfig, auth ComponentAuthResult) error { if err := ValidatePackageConfig(pkg); err != nil { return err } if auth.ServerInstanceID != pkg.ServerInstanceID || auth.Kind != pkg.Kind || auth.ProfileKey != packageComponentKey(pkg) { return fmt.Errorf("component authentication scope does not match package") } if !auth.Allowed { return fmt.Errorf("component authentication rejected: %s", auth.Reason) } if auth.KeyGeneration != pkg.KeyGeneration { return fmt.Errorf("component key generation is no longer current") } return nil } func packageComponentKey(PackageConfig) string { return "" } func fingerprint(value string) string { sum := sha256.Sum256([]byte(value)) return hex.EncodeToString(sum[:])[:12] } func safeIdentifier(value string) bool { value = strings.TrimSpace(value) if value == "" || len(value) > 120 { return false } for _, char := range value { if (char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || (char >= '0' && char <= '9') || char == '_' || char == '-' || char == '.' { continue } return false } return true } func safePluginID(value string) bool { return strings.HasPrefix(value, "game.") && safeIdentifier(value) } func safeLogicalKey(value string) bool { value = strings.TrimSpace(value) if value == "" || len(value) > 120 || strings.HasPrefix(value, "/") || strings.Contains(value, "..") || strings.Contains(value, `\`) { return false } for _, char := range value { if (char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || (char >= '0' && char <= '9') || char == '_' || char == '-' || char == '.' || char == '/' { continue } return false } return true } func safeRuntimeTarget(osName string, arch string) bool { switch osName { case "windows", "linux", "darwin": default: return false } switch arch { case "amd64", "arm64": return true default: return false } }