From 07c9ac3b9c0a5664dd07604ef879e8d3468367d6 Mon Sep 17 00:00:00 2001 From: Allison King Date: Fri, 13 Oct 2023 14:49:45 -0400 Subject: [PATCH] Narrow vendors disclosed to only the vendors we show in the UI (#4250) --- CHANGELOG.md | 2 + clients/fides-js/src/lib/tcf.ts | 77 ++++++++----------- clients/fides-js/src/lib/tcf/vendors.ts | 21 +++++ .../cypress/e2e/consent-banner-tcf.cy.ts | 14 ++++ 4 files changed, 71 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25c387c38b..80c2e78fe8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ The types of changes are: - Allow Admin UI users to turn on Configure Consent flag [#4246](https://github.com/ethyca/fides/pull/4246) - Styling improvements for the fides.js consent banners and modals [#4222](https://github.com/ethyca/fides/pull/4222) - Changed vendor form on configuring consent page to use two-part selection for consent uses [#4251](https://github.com/ethyca/fides/pull/4251) +- Vendors disclosed string is now narrowed to only the vendors shown in the UI, not the whole GVL [#4250](https://github.com/ethyca/fides/pull/4250) - Changed naming convention "fides_string" instead of "tc_string" for developer friendly consent API's [#4267](https://github.com/ethyca/fides/pull/4267) ### Fixed @@ -50,6 +51,7 @@ The types of changes are: - Updating the unflatten_dict util to accept flattened dict values [#4200](https://github.com/ethyca/fides/pull/4200) - Minor CSS styling fixes for the consent modal [#4252](https://github.com/ethyca/fides/pull/4252) - Additional styling fixes for issues caused by a CSS reset [#4268](https://github.com/ethyca/fides/pull/4268) +- Bug where vendor legitimate interests would not be set unless vendor consents were first set [#4250](https://github.com/ethyca/fides/pull/4250) - Vendor count over-counting in TCF overlay [#4275](https://github.com/ethyca/fides/pull/4275) ## [2.21.0](https://github.com/ethyca/fides/compare/2.20.2...2.21.0) diff --git a/clients/fides-js/src/lib/tcf.ts b/clients/fides-js/src/lib/tcf.ts index 7975423b93..d1acb8613a 100644 --- a/clients/fides-js/src/lib/tcf.ts +++ b/clients/fides-js/src/lib/tcf.ts @@ -10,7 +10,12 @@ import { TCModel, TCString, GVL } from "@iabtechlabtcf/core"; import { makeStub } from "./tcf/stub"; import { EnabledIds } from "./tcf/types"; -import { decodeVendorId, vendorIsAc, vendorGvlEntry } from "./tcf/vendors"; +import { + decodeVendorId, + vendorIsAc, + vendorGvlEntry, + uniqueGvlVendorIds, +} from "./tcf/vendors"; import { PrivacyExperience } from "./consent-types"; import { FIDES_SEPARATOR } from "./tcf/constants"; import { FidesEvent } from "./events"; @@ -69,18 +74,19 @@ export const generateTcString = async ({ tcModel.cmpVersion = CMP_VERSION; tcModel.consentScreen = 1; // todo- On which 'screen' consent was captured; this is a CMP proprietary number encoded into the TC string + // Narrow the GVL to say we've only showed these vendors provided by our experience + tcModel.gvl.narrowVendorsTo(uniqueGvlVendorIds(experience)); + if (tcStringPreferences) { - if ( - tcStringPreferences.vendorsConsent && - tcStringPreferences.vendorsConsent.length > 0 - ) { - tcStringPreferences.vendorsConsent.forEach((vendorId) => { - if (vendorGvlEntry(vendorId, experience.gvl)) { - const { id } = decodeVendorId(vendorId); - tcModel.vendorConsents.set(+id); - } - }); - tcStringPreferences.vendorsLegint.forEach((vendorId) => { + // Set vendors on tcModel + tcStringPreferences.vendorsConsent.forEach((vendorId) => { + if (vendorGvlEntry(vendorId, experience.gvl)) { + const { id } = decodeVendorId(vendorId); + tcModel.vendorConsents.set(+id); + } + }); + tcStringPreferences.vendorsLegint.forEach((vendorId) => { + if (vendorGvlEntry(vendorId, experience.gvl)) { const thisVendor = experience.tcf_vendor_legitimate_interests?.filter( (v) => v.id === vendorId )[0]; @@ -102,39 +108,24 @@ export const generateTcString = async ({ tcModel.vendorLegitimateInterests.set(+id); } } - }); - } - - // Set purpose consent on tcModel - if ( - tcStringPreferences.purposesConsent && - tcStringPreferences.purposesConsent.length > 0 - ) { - tcStringPreferences.purposesConsent.forEach((purposeId) => { - tcModel.purposeConsents.set(+purposeId); - }); - } - if ( - tcStringPreferences.purposesLegint && - tcStringPreferences.purposesLegint.length > 0 - ) { - tcStringPreferences.purposesLegint.forEach((purposeId) => { - const id = +purposeId; - if (!FORBIDDEN_LEGITIMATE_INTEREST_PURPOSE_IDS.includes(id)) { - tcModel.purposeLegitimateInterests.set(id); - } - }); - } + } + }); + + // Set purposes on tcModel + tcStringPreferences.purposesConsent.forEach((purposeId) => { + tcModel.purposeConsents.set(+purposeId); + }); + tcStringPreferences.purposesLegint.forEach((purposeId) => { + const id = +purposeId; + if (!FORBIDDEN_LEGITIMATE_INTEREST_PURPOSE_IDS.includes(id)) { + tcModel.purposeLegitimateInterests.set(id); + } + }); // Set special feature opt-ins on tcModel - if ( - tcStringPreferences.specialFeatures && - tcStringPreferences.specialFeatures.length > 0 - ) { - tcStringPreferences.specialFeatures.forEach((id) => { - tcModel.specialFeatureOptins.set(+id); - }); - } + tcStringPreferences.specialFeatures.forEach((id) => { + tcModel.specialFeatureOptins.set(+id); + }); // note that we cannot set consent for special purposes nor features because the IAB policy states // the user is not given choice by a CMP. diff --git a/clients/fides-js/src/lib/tcf/vendors.ts b/clients/fides-js/src/lib/tcf/vendors.ts index 531ee3573d..0c03fdd61a 100644 --- a/clients/fides-js/src/lib/tcf/vendors.ts +++ b/clients/fides-js/src/lib/tcf/vendors.ts @@ -50,6 +50,27 @@ export const vendorGvlEntry = ( export const vendorIsAc = (vendorId: TCFVendorRelationships["id"]) => decodeVendorId(vendorId).source === VendorSources.AC; +export const uniqueGvlVendorIds = (experience: PrivacyExperience): number[] => { + const { + tcf_vendor_consents: vendorConsents = [], + tcf_vendor_legitimate_interests: vendorLegints = [], + } = experience; + + // List of i.e. [gvl.2, ac.3, gvl.4] + const universalIds = Array.from( + new Set([ + ...vendorConsents.map((v) => v.id), + ...vendorLegints.map((v) => v.id), + ]) + ); + // Filter to just i.e. [gvl.2, gvl.4] + const gvlIds = universalIds.filter((uid) => + vendorGvlEntry(uid, experience.gvl) + ); + // Return [2,4] as numbers + return gvlIds.map((uid) => +decodeVendorId(uid).id); +}; + const transformVendorDataToVendorRecords = ({ consents, legints, diff --git a/clients/privacy-center/cypress/e2e/consent-banner-tcf.cy.ts b/clients/privacy-center/cypress/e2e/consent-banner-tcf.cy.ts index 25d95190b1..52b0af3610 100644 --- a/clients/privacy-center/cypress/e2e/consent-banner-tcf.cy.ts +++ b/clients/privacy-center/cypress/e2e/consent-banner-tcf.cy.ts @@ -422,6 +422,7 @@ describe("Fides-js TCF", () => { }); describe("saving preferences", () => { + const expectedEndOfFidesString = ".IABE,1~"; it("can opt in to all", () => { cy.getCookie(CONSENT_COOKIE_NAME).should("not.exist"); cy.getByTestId("consent-modal").within(() => { @@ -487,6 +488,11 @@ describe("Fides-js TCF", () => { ) .property(`${SYSTEM_1.id}`) .is.eql(true); + + // Confirm vendors_disclosed section + expect( + cookieKeyConsent.fides_tc_string?.endsWith(expectedEndOfFidesString) + ).to.eql(true); }); }); @@ -554,6 +560,10 @@ describe("Fides-js TCF", () => { ) .property(`${SYSTEM_1.id}`) .is.eql(false); + // Confirm vendors_disclosed section + expect( + cookieKeyConsent.fides_tc_string?.endsWith(expectedEndOfFidesString) + ).to.eql(true); }); }); @@ -628,6 +638,10 @@ describe("Fides-js TCF", () => { expect( cookieKeyConsent.tcf_consent.system_consent_preferences ).to.eql({}); + // Confirm vendors_disclosed section + expect( + cookieKeyConsent.fides_tc_string?.endsWith(expectedEndOfFidesString) + ).to.eql(true); }); }); });