package main import ( "errors" "net/url" "strconv" "strings" ) var allowedThemeFontFamilies = map[string]struct{}{ "inter": {}, "system ui": {}, "roboto": {}, "open sans": {}, "lato": {}, "montserrat": {}, } var allowedThemeAssetMimeTypes = map[string]struct{}{ "image/png": {}, "image/jpeg": {}, "image/webp": {}, "image/svg+xml": {}, "image/x-icon": {}, "image/vnd.microsoft.icon": {}, } func validateKeycloakThemePolicy(existing, patch Doc, claims *accessClaims) error { scope := resolveThemeString(existing, patch, "scope", keycloakThemeScopePlatform) status := resolveThemeString(existing, patch, "status", keycloakThemeStatusDraft) metadata := resolveThemeMetadata(existing, patch) if hasUnsafeThemeValue(metadata) { return errors.New("theme policy violation: unsafe html or javascript content detected") } if err := validateThemeURLFields(metadata); err != nil { return err } if err := validateThemeFontAndAssetPolicy(metadata); err != nil { return err } requiresPublishLevelChecks := status == keycloakThemeStatusPublished || isThemeExportRequested(existing, patch, metadata) if !requiresPublishLevelChecks { return nil } if err := validateThemeLegalAndSecurityRequirements(metadata); err != nil { return err } if scope == keycloakThemeScopeOrganization { if err := validateOrganizationThemePublicationApproval(claims, metadata); err != nil { return err } } return nil } func resolveThemeString(existing, patch Doc, key, fallback string) string { if value, ok := patch[key].(string); ok && strings.TrimSpace(value) != "" { return strings.TrimSpace(strings.ToLower(value)) } if value, ok := existing[key].(string); ok && strings.TrimSpace(value) != "" { return strings.TrimSpace(strings.ToLower(value)) } return fallback } func resolveThemeMetadata(existing, patch Doc) Doc { if value, ok := patch["metadata"]; ok { metadata, _ := normalizeThemeMetadata(value) if metadata != nil { return metadata } } if value, ok := existing["metadata"]; ok { metadata, _ := normalizeThemeMetadata(value) if metadata != nil { return metadata } } return Doc{} } func hasUnsafeThemeValue(value any) bool { switch v := value.(type) { case string: normalized := strings.ToLower(strings.TrimSpace(v)) unsafeTokens := []string{"