Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Narrow vendors disclosed to only the vendors we show in the UI #4250

Merged
merged 8 commits into from
Oct 13, 2023
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
77 changes: 34 additions & 43 deletions clients/fides-js/src/lib/tcf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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
) {
Comment on lines -73 to -76
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need these ifs: it's always an array, and even if it's empty, the forEach below won't do anything

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];
Expand All @@ -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.
Expand Down
21 changes: 21 additions & 0 deletions clients/fides-js/src/lib/tcf/vendors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
14 changes: 14 additions & 0 deletions clients/privacy-center/cypress/e2e/consent-banner-tcf.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand Down Expand Up @@ -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);
});
});

Expand Down Expand Up @@ -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);
});
});

Expand Down Expand Up @@ -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);
});
});
});
Expand Down