diff --git a/pkg/cookiebanner/service.go b/pkg/cookiebanner/service.go index e58de21ac..1ce6351bb 100644 --- a/pkg/cookiebanner/service.go +++ b/pkg/cookiebanner/service.go @@ -22,6 +22,7 @@ import ( "maps" "net/url" "slices" + "strconv" "strings" "time" @@ -1476,6 +1477,7 @@ func (s *Service) GetActiveBannerConfig( bannerID gid.GID, lang string, regulation Regulation, + sdkVersion string, ) (*BannerConfig, error) { var config *BannerConfig @@ -1529,7 +1531,9 @@ func (s *Service) GetActiveBannerConfig( if cm := ConsentModeForRegulation(regulation); cm != "" { config.ConsentMode = cm } - remapTextsForConsentMode(config.Texts, config.ConsentMode, regulation) + if !isLegacySDK(sdkVersion) { + remapTextsForConsentMode(config.Texts, config.ConsentMode, regulation) + } return config, nil } @@ -1624,6 +1628,41 @@ func remapTextsForConsentMode(texts map[string]string, consentMode string, regul } } +// isLegacySDK returns true when the SDK version is <= 0.2.x. +// Empty or unparseable versions are treated as current. +func isLegacySDK(version string) bool { + if version == "" { + return false + } + + major, minor, ok := parseMajorMinor(version) + if !ok { + return false + } + + return major == 0 && minor <= 2 +} + +func parseMajorMinor(version string) (major, minor int, ok bool) { + v := strings.TrimPrefix(version, "v") + parts := strings.SplitN(v, ".", 3) + if len(parts) < 2 { + return 0, 0, false + } + + maj, err := strconv.Atoi(parts[0]) + if err != nil { + return 0, 0, false + } + + min, err := strconv.Atoi(parts[1]) + if err != nil { + return 0, 0, false + } + + return maj, min, true +} + func remapTextKey(texts map[string]string, src, dst string) { if v, ok := texts[src]; ok && v != "" { texts[dst] = v diff --git a/pkg/cookiebanner/service_test.go b/pkg/cookiebanner/service_test.go index 6dcf8fb4b..c59f57e21 100644 --- a/pkg/cookiebanner/service_test.go +++ b/pkg/cookiebanner/service_test.go @@ -283,3 +283,63 @@ func TestBuildSnapshot_RankInvariant(t *testing.T) { assert.Equal(t, coredata.CookieCategoryKindNecessary, snap.Categories[0].Kind) }) } + +func TestRemapTextsForConsentMode(t *testing.T) { + t.Parallel() + + baseTexts := func() map[string]string { + return map[string]string{ + "banner_title": "We use cookies", + "banner_description": "This site uses cookies.", + "button_accept_all": "Accept All", + "button_reject_all": "Reject All", + "button_customize": "Customize", + "button_dismiss": "Dismiss", + } + } + + t.Run("no regulation clears reject and customize", func(t *testing.T) { + t.Parallel() + + texts := baseTexts() + remapTextsForConsentMode(texts, ConsentModeOptIn, RegulationNone) + + assert.Empty(t, texts["button_reject_all"]) + assert.Empty(t, texts["button_customize"]) + }) + + t.Run("opt out mode clears reject", func(t *testing.T) { + t.Parallel() + + texts := baseTexts() + remapTextsForConsentMode(texts, ConsentModeOptOut, RegulationCCPA) + + assert.Empty(t, texts["button_reject_all"]) + }) +} + +func TestIsLegacySDK(t *testing.T) { + t.Parallel() + + tests := []struct { + version string + want bool + }{ + {"0.1.0", true}, + {"0.2.0", true}, + {"0.2.5", true}, + {"0.3.0", false}, + {"1.0.0", false}, + {"", false}, + {"invalid", false}, + {"v0.2.0", true}, + } + + for _, tt := range tests { + t.Run(tt.version, func(t *testing.T) { + t.Parallel() + + assert.Equal(t, tt.want, isLegacySDK(tt.version)) + }) + } +} diff --git a/pkg/server/api/cookiebanner/v1/handler.go b/pkg/server/api/cookiebanner/v1/handler.go index b4b5314f3..7e4b46b14 100644 --- a/pkg/server/api/cookiebanner/v1/handler.go +++ b/pkg/server/api/cookiebanner/v1/handler.go @@ -71,6 +71,7 @@ func (h *Handler) handleGetConfig(w http.ResponseWriter, r *http.Request) { } lang := r.URL.Query().Get("lang") + sdkVersion := r.Header.Get("X-SDK-Version") cc := h.resolveCountryCode(r) var regulation cookiebanner.Regulation @@ -78,7 +79,7 @@ func (h *Handler) handleGetConfig(w http.ResponseWriter, r *http.Request) { regulation = cookiebanner.RegulationForCountry(*cc) } - config, err := h.cookieBannerSvc.GetActiveBannerConfig(r.Context(), bannerID, lang, regulation) + config, err := h.cookieBannerSvc.GetActiveBannerConfig(r.Context(), bannerID, lang, regulation, sdkVersion) if err != nil { if errors.Is(err, cookiebanner.ErrBannerNotFound) { jsonutil.RenderNotFound(w, fmt.Errorf("banner not found"))