diff --git a/vendorconsent/tcf2/bitfield.go b/vendorconsent/tcf2/bitfield.go index 8842d49..d76a96c 100644 --- a/vendorconsent/tcf2/bitfield.go +++ b/vendorconsent/tcf2/bitfield.go @@ -26,6 +26,14 @@ type consentBitField struct { maxVendorID uint16 } +func (f *consentBitField) MaxVendorID() uint16 { + if f == nil { + return 0 + } + + return f.maxVendorID +} + func (f *consentBitField) VendorConsent(id uint16) bool { if id < 1 || id > f.maxVendorID { return false diff --git a/vendorconsent/tcf2/metadata.go b/vendorconsent/tcf2/metadata.go index 087c336..ebe1435 100644 --- a/vendorconsent/tcf2/metadata.go +++ b/vendorconsent/tcf2/metadata.go @@ -44,6 +44,7 @@ type ConsentMetadata struct { } type vendorConsentsResolver interface { + MaxVendorID() uint16 VendorConsent(id uint16) bool } @@ -118,6 +119,10 @@ func (c ConsentMetadata) ConsentLanguage() string { return string([]byte{leftChar + 65, rightChar + 65}) // Unicode A-Z is 65-90 } +func (c ConsentMetadata) VendorLegitInterestMaxID() uint16 { + return c.vendorLegitimateInterests.MaxVendorID() +} + func (c ConsentMetadata) VendorListVersion() uint16 { // The vendor list version is stored in bits 120 - 131 rightByte := ((c.data[16] & 0xf0) >> 4) | ((c.data[15] & 0x0f) << 4) diff --git a/vendorconsent/tcf2/rangesection.go b/vendorconsent/tcf2/rangesection.go index 85d5381..8bd4676 100644 --- a/vendorconsent/tcf2/rangesection.go +++ b/vendorconsent/tcf2/rangesection.go @@ -9,17 +9,19 @@ import ( func parseRangeSection(metadata ConsentMetadata, maxVendorID uint16, startbit uint) (*rangeSection, uint, error) { data := metadata.data - // This makes an int from bits 230-241 + if len(data) < 31 { return nil, 0, fmt.Errorf("vendor consent strings using RangeSections require at least 31 bytes. Got %d", len(data)) } - numEntries, err := bitutils.ParseUInt12(data, 230) + + // This makes an int from bits [startBit, startBit + 12) + numEntries, err := bitutils.ParseUInt12(data, startbit) if err != nil { return nil, 0, err } // Parse out the "exceptions" here. - currentOffset := uint(242) + currentOffset := startbit + 12 consents := make([]rangeConsent, numEntries) for i := uint16(0); i < numEntries; i++ { thisConsent, bitsConsumed, err := parseRangeConsent(data, currentOffset, maxVendorID) @@ -98,6 +100,14 @@ type rangeSection struct { maxVendorID uint16 } +func (p *rangeSection) MaxVendorID() uint16 { + if p == nil { + return 0 + } + + return p.maxVendorID +} + // VendorConsents implementation func (p rangeSection) VendorConsent(id uint16) bool { if id < 1 || id > p.maxVendorID { diff --git a/vendorconsent/tcf2/rangesection_test.go b/vendorconsent/tcf2/rangesection_test.go index 14aecf4..85216ac 100644 --- a/vendorconsent/tcf2/rangesection_test.go +++ b/vendorconsent/tcf2/rangesection_test.go @@ -8,14 +8,14 @@ import ( func TestRangeSectionConsent(t *testing.T) { // String built using http://iabtcf.com/#/encode // This sample encodes a mix of Single- and Range-typed consent exceptions. - consent, err := Parse(decode(t, "COyiILmOyiILmADACHENAPCAAAAAAAAAAAAAE5QBgALgAqgD8AQACSwEygJyAAAAAA")) + consent, err := Parse(decode(t, "COyfVVoOyfVVoADACHENAwCAAAAAAAAAAAAAE5QBgALgAqgD8AQACSwEygJyAnSAMABgAFkAgQCDASeAmYBOgAA")) assertNilError(t, err) assertUInt8sEqual(t, 2, consent.Version()) assertUInt16sEqual(t, 3, consent.CmpID()) assertUInt16sEqual(t, 2, consent.CmpVersion()) assertUInt8sEqual(t, 7, consent.ConsentScreen()) assertStringsEqual(t, "EN", consent.ConsentLanguage()) - assertUInt16sEqual(t, 15, consent.VendorListVersion()) + assertUInt16sEqual(t, 48, consent.VendorListVersion()) assertUInt16sEqual(t, 626, consent.MaxVendorID()) // The above encoder doesn't support setting purposes. @@ -27,11 +27,24 @@ func TestRangeSectionConsent(t *testing.T) { vendorsWithConsent := buildMap(23, 42, 126, 127, 128, 587, 613, 626) for i := uint16(1); i <= consent.MaxVendorID(); i++ { - _, ok := vendorsWithConsent[uint(i)] - if ok != consent.VendorConsent(i) { + _, expected := vendorsWithConsent[uint(i)] + actual := consent.VendorConsent(i) + if expected != actual { fmt.Printf("Vendor: %d failed\n", i) } - assertBoolsEqual(t, ok, consent.VendorConsent(i)) + assertBoolsEqual(t, expected, actual) + } + + // TODO func VendorLegitInterest() should be added to api.VendorConsents + consentMetadata := consent.(ConsentMetadata) + vendorsLegitimateInterestWithConsent := buildMap(24, 44, 129, 130, 131, 591, 614, 628) + for i := uint16(1); i <= consentMetadata.VendorLegitInterestMaxID(); i++ { + _, expected := vendorsLegitimateInterestWithConsent[uint(i)] + actual := consentMetadata.VendorLegitInterest(i) + if expected != actual { + fmt.Printf("VendorLegitInterest: %d failed\n", i) + } + assertBoolsEqual(t, expected, actual) } }