From 978c21a2ff12d1c5b12fd5ad60b70a67b9194042 Mon Sep 17 00:00:00 2001 From: Tiago Peczenyj Date: Tue, 22 Apr 2025 00:58:50 +0200 Subject: [PATCH 1/8] run gofumpt -w . --- consentconstants/errors.go | 6 +-- consentconstants/features.go | 6 +-- consentconstants/purposes.go | 4 +- vendorconsent/benchmark_test.go | 21 ++++++----- vendorconsent/tcf1/metadata.go | 5 +-- vendorconsent/tcf2/consent.go | 1 - vendorconsent/tcf2/metadata.go | 6 +-- vendorconsent/tcf2/metadata_test.go | 57 ++++++++++++++--------------- vendorconsent/tcf2/pubrestrict.go | 1 - vendorlist/eager-parsing.go | 14 +++---- vendorlist/lazy-parsing.go | 4 +- 11 files changed, 58 insertions(+), 67 deletions(-) diff --git a/consentconstants/errors.go b/consentconstants/errors.go index 72acb8e..271bb51 100644 --- a/consentconstants/errors.go +++ b/consentconstants/errors.go @@ -2,7 +2,5 @@ package consentconstants import "errors" -var ( - // ErrEmptyDecodedConsent error raised when the consent string is empty - ErrEmptyDecodedConsent = errors.New("decoded consent cannot be empty") -) +// ErrEmptyDecodedConsent error raised when the consent string is empty +var ErrEmptyDecodedConsent = errors.New("decoded consent cannot be empty") diff --git a/consentconstants/features.go b/consentconstants/features.go index f7f300f..5f5dbc4 100644 --- a/consentconstants/features.go +++ b/consentconstants/features.go @@ -1,6 +1,6 @@ package consentconstants // SpecialFeature is one of the IAB GDPR special features. These appear in: -// 1. `root.specialFeatures[i]` of the vendor list: https://vendorlist.consensu.org/vendorlist.json -// 2. SpecialFeatureOptIns of the Consent string: https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/Consent%20string%20and%20vendor%20list%20formats%20v1.1%20Final.md#vendor-consent-string-format- -type SpecialFeature uint8 \ No newline at end of file +// 1. `root.specialFeatures[i]` of the vendor list: https://vendorlist.consensu.org/vendorlist.json +// 2. SpecialFeatureOptIns of the Consent string: https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/Consent%20string%20and%20vendor%20list%20formats%20v1.1%20Final.md#vendor-consent-string-format- +type SpecialFeature uint8 diff --git a/consentconstants/purposes.go b/consentconstants/purposes.go index 06c47bc..d479fa2 100644 --- a/consentconstants/purposes.go +++ b/consentconstants/purposes.go @@ -1,8 +1,8 @@ package consentconstants // Purpose is one of the IAB GDPR purposes. These appear in: -// 1. `root.purposes[i]` of the vendor list: https://vendorlist.consensu.org/vendorlist.json -// 2. PurposesAllowed of the Consent string: https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/Consent%20string%20and%20vendor%20list%20formats%20v1.1%20Final.md#vendor-consent-string-format- +// 1. `root.purposes[i]` of the vendor list: https://vendorlist.consensu.org/vendorlist.json +// 2. PurposesAllowed of the Consent string: https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/Consent%20string%20and%20vendor%20list%20formats%20v1.1%20Final.md#vendor-consent-string-format- type Purpose uint8 // TCF 1 Purposes: diff --git a/vendorconsent/benchmark_test.go b/vendorconsent/benchmark_test.go index a96fed2..c84df01 100644 --- a/vendorconsent/benchmark_test.go +++ b/vendorconsent/benchmark_test.go @@ -67,11 +67,11 @@ func BenchmarkParse(b *testing.B) { all = append(all, c.consent) } b.Run("all testcases", func(b *testing.B) { - // on https://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go - // section "A note on compiler optimisations" - // we have a warning about the compiler may eliminate - // ParseString function call, to prevent this we assign the result to - // some variables out of the for loop scope + // on https://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go + // section "A note on compiler optimisations" + // we have a warning about the compiler may eliminate + // ParseString function call, to prevent this we assign the result to + // some variables out of the for loop scope var consent api.VendorConsents var err error max := len(all) @@ -101,6 +101,7 @@ var consentFile string func init() { flag.StringVar(&consentFile, "consent-file", "", "ascii consent file") } + func BenchmarkVerify(b *testing.B) { if consentFile == "" { b.SkipNow() @@ -120,11 +121,11 @@ func BenchmarkVerify(b *testing.B) { max := len(consents) b.Run(fmt.Sprintf("testing just parsing %d consents/string", max), func(b *testing.B) { - // on https://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go - // section "A note on compiler optimisations" - // we have a warning about the compiler may eliminate - // ParseString function call, to prevent this we assign the result to - // some variables out of the for loop scope + // on https://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go + // section "A note on compiler optimisations" + // we have a warning about the compiler may eliminate + // ParseString function call, to prevent this we assign the result to + // some variables out of the for loop scope var consent api.VendorConsents var err error for n := 0; n < b.N; n++ { diff --git a/vendorconsent/tcf1/metadata.go b/vendorconsent/tcf1/metadata.go index f9594f5..564fd88 100644 --- a/vendorconsent/tcf1/metadata.go +++ b/vendorconsent/tcf1/metadata.go @@ -9,9 +9,7 @@ import ( "github.com/prebid/go-gdpr/consentconstants" ) -var ( - errInvalidVendorListVersion = errors.New("the consent string encoded a VendorListVersion of 0, but this value must be greater than or equal to 1") -) +var errInvalidVendorListVersion = errors.New("the consent string encoded a VendorListVersion of 0, but this value must be greater than or equal to 1") // Parse the metadata from the consent string. // This returns an error if the input is too short to answer questions about that data. @@ -28,7 +26,6 @@ func parseMetadata(data []byte) (consentMetadata, error) { } if metadata.VendorListVersion() == 0 { return nil, errInvalidVendorListVersion - } return consentMetadata(data), nil } diff --git a/vendorconsent/tcf2/consent.go b/vendorconsent/tcf2/consent.go index 4bd7a95..dc5669d 100644 --- a/vendorconsent/tcf2/consent.go +++ b/vendorconsent/tcf2/consent.go @@ -90,7 +90,6 @@ func Parse(data []byte) (api.VendorConsents, error) { metadata.publisherRestrictions = pubRestrictions return metadata, err - } // IsConsentV2 return true if the consent strings looks like a tcf v2 consent string diff --git a/vendorconsent/tcf2/metadata.go b/vendorconsent/tcf2/metadata.go index 450f105..c363a4e 100644 --- a/vendorconsent/tcf2/metadata.go +++ b/vendorconsent/tcf2/metadata.go @@ -9,9 +9,7 @@ import ( "github.com/prebid/go-gdpr/consentconstants" ) -var ( - errInvalidVendorListVersion = errors.New("the consent string encoded a VendorListVersion of 0, but this value must be greater than or equal to 1") -) +var errInvalidVendorListVersion = errors.New("the consent string encoded a VendorListVersion of 0, but this value must be greater than or equal to 1") // parseMetadata parses the metadata from the consent string. // This returns an error if the input is too short to answer questions about that data. @@ -146,7 +144,7 @@ func (c ConsentMetadata) VendorListVersion() uint16 { // TCFPolicyVersion returns the TCF policy version stored in bits 133 to 138 func (c ConsentMetadata) TCFPolicyVersion() uint8 { // Stored in bits 133-138.. which is [0000xxxx xx00000000] starting at the 17th byte - return uint8(((c.data[16] & 0x0f) << 2) | (c.data[17] & 0xc0) >> 6) + return uint8(((c.data[16] & 0x0f) << 2) | (c.data[17]&0xc0)>>6) } // MaxVendorID returns the maximum value for vendor identifier in bits 214 to 229 diff --git a/vendorconsent/tcf2/metadata_test.go b/vendorconsent/tcf2/metadata_test.go index 2555b17..ea6c18b 100644 --- a/vendorconsent/tcf2/metadata_test.go +++ b/vendorconsent/tcf2/metadata_test.go @@ -62,50 +62,50 @@ func TestLanguageExtremes(t *testing.T) { func TestTCFPolicyVersion(t *testing.T) { baseConsent := "CPtGDMAPtGDMALMAAAENA_C_AAAAAAAAACiQAAAAAAAA" index := 22 // policy version is at the 23rd 6-bit base64 position - tests := []struct{ - name string - base64Char string - expected uint8 + tests := []struct { + name string + base64Char string + expected uint8 }{ { - name: "char_A_bits_000000_is_version_0", - base64Char: "A", - expected: 0, + name: "char_A_bits_000000_is_version_0", + base64Char: "A", + expected: 0, }, { - name: "char_B_bits_000001_is_version_1", - base64Char: "B", - expected: 1, + name: "char_B_bits_000001_is_version_1", + base64Char: "B", + expected: 1, }, { - name: "char_C_bits_000010_is_version_2", - base64Char: "C", - expected: 2, + name: "char_C_bits_000010_is_version_2", + base64Char: "C", + expected: 2, }, { - name: "char_E_bits_000100_is_version_4", - base64Char: "E", - expected: 4, + name: "char_E_bits_000100_is_version_4", + base64Char: "E", + expected: 4, }, { - name: "char_I_bits_001000_is_version_8", - base64Char: "I", - expected: 8, + name: "char_I_bits_001000_is_version_8", + base64Char: "I", + expected: 8, }, { - name: "char_Q_bits_010000_is_version_16", - base64Char: "Q", - expected: 16, + name: "char_Q_bits_010000_is_version_16", + base64Char: "Q", + expected: 16, }, { - name: "char_g_bits_100000_is_version_32", - base64Char: "g", - expected: 32, + name: "char_g_bits_100000_is_version_32", + base64Char: "g", + expected: 32, }, { - name: "char_underscore_bits_111111_is_version_63", - base64Char: "_", - expected: 63, + name: "char_underscore_bits_111111_is_version_63", + base64Char: "_", + expected: 63, }, } for _, tt := range tests { @@ -141,5 +141,4 @@ func TestLITransparency(t *testing.T) { assertBoolsEqual(t, false, consent.PurposeLITransparency(6)) assertBoolsEqual(t, false, consent.PurposeLITransparency(7)) assertBoolsEqual(t, false, consent.PurposeLITransparency(28)) - } diff --git a/vendorconsent/tcf2/pubrestrict.go b/vendorconsent/tcf2/pubrestrict.go index 7ba0415..b7aa29d 100644 --- a/vendorconsent/tcf2/pubrestrict.go +++ b/vendorconsent/tcf2/pubrestrict.go @@ -69,5 +69,4 @@ func (p *pubRestrictions) CheckPubRestriction(purposeID uint8, restrictType uint } } return false - } diff --git a/vendorlist/eager-parsing.go b/vendorlist/eager-parsing.go index 8a8dd9c..e72095b 100644 --- a/vendorlist/eager-parsing.go +++ b/vendorlist/eager-parsing.go @@ -12,8 +12,8 @@ import ( // The returned object can be shared safely between goroutines. // // This is ideal if: -// 1. You plan to call functions on the returned VendorList many times before discarding it. -// 2. You need strong input validation and good error messages. +// 1. You plan to call functions on the returned VendorList many times before discarding it. +// 2. You need strong input validation and good error messages. // // Otherwise, you may get better performance with ParseLazily. func ParseEagerly(data []byte) (api.VendorList, error) { @@ -31,8 +31,8 @@ func ParseEagerly(data []byte) (api.VendorList, error) { parsedList := parsedVendorList{ specVersion: contract.GVLSpecificationVersion, - version: contract.Version, - vendors: make(map[uint16]parsedVendor, len(contract.Vendors)), + version: contract.Version, + vendors: make(map[uint16]parsedVendor, len(contract.Vendors)), } for i := 0; i < len(contract.Vendors); i++ { @@ -123,9 +123,9 @@ func (l parsedVendor) SpecialFeature(featureID consentconstants.SpecialFeature) } type vendorListContract struct { - GVLSpecificationVersion uint16 `json:"gvlSpecificationVersion"` - Version uint16 `json:"vendorListVersion"` - Vendors []vendorListVendorContract `json:"vendors"` + GVLSpecificationVersion uint16 `json:"gvlSpecificationVersion"` + Version uint16 `json:"vendorListVersion"` + Vendors []vendorListVendorContract `json:"vendors"` } type vendorListVendorContract struct { diff --git a/vendorlist/lazy-parsing.go b/vendorlist/lazy-parsing.go index 092b560..18f9db7 100644 --- a/vendorlist/lazy-parsing.go +++ b/vendorlist/lazy-parsing.go @@ -12,8 +12,8 @@ import ( // The returned object can be shared safely between goroutines. // // This is ideal if: -// 1. You only need to look up a few vendors or purpose IDs -// 2. You don't need good errors on malformed input +// 1. You only need to look up a few vendors or purpose IDs +// 2. You don't need good errors on malformed input // // Otherwise, you may get better performance with ParseEagerly. func ParseLazily(data []byte) api.VendorList { From 902c852ba2e6d443c45102a7e2671714acf9aee0 Mon Sep 17 00:00:00 2001 From: Tiago Peczenyj Date: Tue, 22 Apr 2025 00:59:06 +0200 Subject: [PATCH 2/8] run gofumpt -w -extra . --- bitutils/bitutils_test.go | 12 ++++++------ vendorconsent/consent20_test.go | 2 +- vendorconsent/consent_test.go | 12 ++++++------ vendorconsent/tcf1/rangesection_test.go | 2 +- vendorconsent/tcf1/test_utils.go | 12 ++++++------ vendorconsent/tcf2/metadata.go | 4 ++-- vendorconsent/tcf2/pubrestrict.go | 2 +- vendorconsent/tcf2/test_utils.go | 12 ++++++------ vendorlist/shared_test.go | 4 ++-- vendorlist2/shared_test.go | 4 ++-- 10 files changed, 33 insertions(+), 33 deletions(-) diff --git a/bitutils/bitutils_test.go b/bitutils/bitutils_test.go index 641cce9..aba00f3 100644 --- a/bitutils/bitutils_test.go +++ b/bitutils/bitutils_test.go @@ -120,42 +120,42 @@ func assertNilError(t *testing.T, err error) { } } -func assertStringsEqual(t *testing.T, expected string, actual string) { +func assertStringsEqual(t *testing.T, expected, actual string) { t.Helper() if actual != expected { t.Errorf("Strings were not equal. Expected %s, actual %s", expected, actual) } } -func assertBytesEqual(t *testing.T, expected byte, actual byte) { +func assertBytesEqual(t *testing.T, expected, actual byte) { t.Helper() if actual != expected { t.Errorf("bytes were not equal. Expected %d, actual %d", expected, actual) } } -func assertUInt8sEqual(t *testing.T, expected uint8, actual uint8) { +func assertUInt8sEqual(t *testing.T, expected, actual uint8) { t.Helper() if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } } -func assertUInt16sEqual(t *testing.T, expected uint16, actual uint16) { +func assertUInt16sEqual(t *testing.T, expected, actual uint16) { t.Helper() if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } } -func assertIntsEqual(t *testing.T, expected int, actual int) { +func assertIntsEqual(t *testing.T, expected, actual int) { t.Helper() if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } } -func assertBoolsEqual(t *testing.T, expected bool, actual bool) { +func assertBoolsEqual(t *testing.T, expected, actual bool) { t.Helper() if actual != expected { t.Errorf("Bools were not equal. Expected %t, actual %t", expected, actual) diff --git a/vendorconsent/consent20_test.go b/vendorconsent/consent20_test.go index e8660e5..4403193 100644 --- a/vendorconsent/consent20_test.go +++ b/vendorconsent/consent20_test.go @@ -90,7 +90,7 @@ func TestParseEmptyString(t *testing.T) { } } -func assertInvalid20(t *testing.T, urlEncodedString string, expectError string) { +func assertInvalid20(t *testing.T, urlEncodedString, expectError string) { t.Helper() data, err := base64.RawURLEncoding.DecodeString(urlEncodedString) assertNilError(t, err) diff --git a/vendorconsent/consent_test.go b/vendorconsent/consent_test.go index 7f979e6..c00ec3e 100644 --- a/vendorconsent/consent_test.go +++ b/vendorconsent/consent_test.go @@ -84,7 +84,7 @@ func TestParseValidString(t *testing.T) { assertUInt16sEqual(t, 14, parsed.VendorListVersion()) } -func assertInvalid(t *testing.T, urlEncodedString string, expectError string) { +func assertInvalid(t *testing.T, urlEncodedString, expectError string) { t.Helper() data, err := base64.RawURLEncoding.DecodeString(urlEncodedString) assertNilError(t, err) @@ -106,35 +106,35 @@ func decode(t *testing.T, encodedString string) []byte { return data } -func assertStringsEqual(t *testing.T, expected string, actual string) { +func assertStringsEqual(t *testing.T, expected, actual string) { t.Helper() if actual != expected { t.Errorf("Strings were not equal. Expected %s, actual %s", expected, actual) } } -func assertUInt8sEqual(t *testing.T, expected uint8, actual uint8) { +func assertUInt8sEqual(t *testing.T, expected, actual uint8) { t.Helper() if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } } -func assertUInt16sEqual(t *testing.T, expected uint16, actual uint16) { +func assertUInt16sEqual(t *testing.T, expected, actual uint16) { t.Helper() if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } } -func assertIntsEqual(t *testing.T, expected int, actual int) { +func assertIntsEqual(t *testing.T, expected, actual int) { t.Helper() if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } } -func assertBoolsEqual(t *testing.T, expected bool, actual bool) { +func assertBoolsEqual(t *testing.T, expected, actual bool) { t.Helper() if actual != expected { t.Errorf("Bools were not equal. Expected %t, actual %t", expected, actual) diff --git a/vendorconsent/tcf1/rangesection_test.go b/vendorconsent/tcf1/rangesection_test.go index 8095938..f07e44d 100644 --- a/vendorconsent/tcf1/rangesection_test.go +++ b/vendorconsent/tcf1/rangesection_test.go @@ -53,7 +53,7 @@ func TestParseUInt16(t *testing.T) { doParseIntTest(t, []byte{0xf4, 0x13}, 0, 0xf413) } -func doParseIntTest(t *testing.T, data []byte, offset int, expected int) { +func doParseIntTest(t *testing.T, data []byte, offset, expected int) { t.Helper() parsedVal, err := parseUInt16(data, uint(offset)) assertNilError(t, err) diff --git a/vendorconsent/tcf1/test_utils.go b/vendorconsent/tcf1/test_utils.go index 2466e6f..5519ab2 100644 --- a/vendorconsent/tcf1/test_utils.go +++ b/vendorconsent/tcf1/test_utils.go @@ -5,7 +5,7 @@ import ( "testing" ) -func assertInvalid(t *testing.T, urlEncodedString string, expectError string) { +func assertInvalid(t *testing.T, urlEncodedString, expectError string) { t.Helper() data, err := base64.RawURLEncoding.DecodeString(urlEncodedString) assertNilError(t, err) @@ -33,35 +33,35 @@ func assertNilError(t *testing.T, err error) { } } -func assertStringsEqual(t *testing.T, expected string, actual string) { +func assertStringsEqual(t *testing.T, expected, actual string) { t.Helper() if actual != expected { t.Errorf("Strings were not equal. Expected %s, actual %s", expected, actual) } } -func assertUInt8sEqual(t *testing.T, expected uint8, actual uint8) { +func assertUInt8sEqual(t *testing.T, expected, actual uint8) { t.Helper() if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } } -func assertUInt16sEqual(t *testing.T, expected uint16, actual uint16) { +func assertUInt16sEqual(t *testing.T, expected, actual uint16) { t.Helper() if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } } -func assertIntsEqual(t *testing.T, expected int, actual int) { +func assertIntsEqual(t *testing.T, expected, actual int) { t.Helper() if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } } -func assertBoolsEqual(t *testing.T, expected bool, actual bool) { +func assertBoolsEqual(t *testing.T, expected, actual bool) { t.Helper() if actual != expected { t.Errorf("Bools were not equal. Expected %t, actual %t", expected, actual) diff --git a/vendorconsent/tcf2/metadata.go b/vendorconsent/tcf2/metadata.go index c363a4e..b414671 100644 --- a/vendorconsent/tcf2/metadata.go +++ b/vendorconsent/tcf2/metadata.go @@ -51,7 +51,7 @@ type vendorConsentsResolver interface { } type pubRestrictResolver interface { - CheckPubRestriction(purposeID uint8, restrictType uint8, vendor uint16) bool + CheckPubRestriction(purposeID, restrictType uint8, vendor uint16) bool } // Version returns the version stored in the first 6 bits @@ -199,7 +199,7 @@ func (c ConsentMetadata) VendorLegitInterest(id uint16) bool { } // CheckPubRestriction returns the publisher restriction for a given purpose id, restriction type and vendor id -func (c ConsentMetadata) CheckPubRestriction(purposeID uint8, restrictType uint8, vendor uint16) bool { +func (c ConsentMetadata) CheckPubRestriction(purposeID, restrictType uint8, vendor uint16) bool { return c.publisherRestrictions.CheckPubRestriction(purposeID, restrictType, vendor) } diff --git a/vendorconsent/tcf2/pubrestrict.go b/vendorconsent/tcf2/pubrestrict.go index b7aa29d..ee1cb4d 100644 --- a/vendorconsent/tcf2/pubrestrict.go +++ b/vendorconsent/tcf2/pubrestrict.go @@ -57,7 +57,7 @@ type pubRestriction struct { vendors []rangeConsent } -func (p *pubRestrictions) CheckPubRestriction(purposeID uint8, restrictType uint8, vendor uint16) bool { +func (p *pubRestrictions) CheckPubRestriction(purposeID, restrictType uint8, vendor uint16) bool { key := byte(purposeID<<2 | (restrictType & 0x03)) restriction, ok := p.restrictions[key] if !ok { diff --git a/vendorconsent/tcf2/test_utils.go b/vendorconsent/tcf2/test_utils.go index 72dab28..9b15ca3 100644 --- a/vendorconsent/tcf2/test_utils.go +++ b/vendorconsent/tcf2/test_utils.go @@ -5,7 +5,7 @@ import ( "testing" ) -func assertInvalid(t *testing.T, urlEncodedString string, expectError string) { +func assertInvalid(t *testing.T, urlEncodedString, expectError string) { t.Helper() data, err := base64.RawURLEncoding.DecodeString(urlEncodedString) assertNilError(t, err) @@ -39,35 +39,35 @@ func assertError(t *testing.T, err error) { } } -func assertStringsEqual(t *testing.T, expected string, actual string) { +func assertStringsEqual(t *testing.T, expected, actual string) { t.Helper() if actual != expected { t.Errorf("Strings were not equal. Expected %s, actual %s", expected, actual) } } -func assertUInt8sEqual(t *testing.T, expected uint8, actual uint8) { +func assertUInt8sEqual(t *testing.T, expected, actual uint8) { t.Helper() if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } } -func assertUInt16sEqual(t *testing.T, expected uint16, actual uint16) { +func assertUInt16sEqual(t *testing.T, expected, actual uint16) { t.Helper() if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } } -func assertIntsEqual(t *testing.T, expected int, actual int) { +func assertIntsEqual(t *testing.T, expected, actual int) { t.Helper() if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } } -func assertBoolsEqual(t *testing.T, expected bool, actual bool) { +func assertBoolsEqual(t *testing.T, expected, actual bool) { t.Helper() if actual != expected { t.Errorf("Bools were not equal. Expected %t, actual %t", expected, actual) diff --git a/vendorlist/shared_test.go b/vendorlist/shared_test.go index 056b2a0..6332fd0 100644 --- a/vendorlist/shared_test.go +++ b/vendorlist/shared_test.go @@ -57,14 +57,14 @@ const testData = ` } ` -func assertIntsEqual(t *testing.T, expected int, actual int) { +func assertIntsEqual(t *testing.T, expected, actual int) { t.Helper() if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } } -func assertBoolsEqual(t *testing.T, expected bool, actual bool) { +func assertBoolsEqual(t *testing.T, expected, actual bool) { t.Helper() if actual != expected { t.Errorf("Bools were not equal. Expected %t, actual %t", expected, actual) diff --git a/vendorlist2/shared_test.go b/vendorlist2/shared_test.go index f97d3e2..17ac9bc 100644 --- a/vendorlist2/shared_test.go +++ b/vendorlist2/shared_test.go @@ -186,14 +186,14 @@ const testDataSpecVersion3Empty = ` } ` -func assertIntsEqual(t *testing.T, expected int, actual int) { +func assertIntsEqual(t *testing.T, expected, actual int) { t.Helper() if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } } -func assertBoolsEqual(t *testing.T, expected bool, actual bool) { +func assertBoolsEqual(t *testing.T, expected, actual bool) { t.Helper() if actual != expected { t.Errorf("Bools were not equal. Expected %t, actual %t", expected, actual) From c4a3c1733ec70ed916ee444806313fd2d28f7d65 Mon Sep 17 00:00:00 2001 From: Tiago Peczenyj Date: Tue, 22 Apr 2025 00:59:34 +0200 Subject: [PATCH 3/8] run goimports -w -local github.com/prebid/go-gdpr . --- vendorlist/lazy-parsing.go | 1 + vendorlist2/lazy-parsing.go | 1 + 2 files changed, 2 insertions(+) diff --git a/vendorlist/lazy-parsing.go b/vendorlist/lazy-parsing.go index 18f9db7..7122d72 100644 --- a/vendorlist/lazy-parsing.go +++ b/vendorlist/lazy-parsing.go @@ -4,6 +4,7 @@ import ( "strconv" "github.com/buger/jsonparser" + "github.com/prebid/go-gdpr/api" "github.com/prebid/go-gdpr/consentconstants" ) diff --git a/vendorlist2/lazy-parsing.go b/vendorlist2/lazy-parsing.go index 7b1f69e..c8bda47 100644 --- a/vendorlist2/lazy-parsing.go +++ b/vendorlist2/lazy-parsing.go @@ -4,6 +4,7 @@ import ( "strconv" "github.com/buger/jsonparser" + "github.com/prebid/go-gdpr/api" "github.com/prebid/go-gdpr/consentconstants" ) From 91c87109e7bd7adeecd8ccd062eab66d2d8ac3b1 Mon Sep 17 00:00:00 2001 From: Tiago Peczenyj Date: Tue, 22 Apr 2025 01:01:35 +0200 Subject: [PATCH 4/8] simplify code --- vendorconsent/consent_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/vendorconsent/consent_test.go b/vendorconsent/consent_test.go index c00ec3e..e9c898b 100644 --- a/vendorconsent/consent_test.go +++ b/vendorconsent/consent_test.go @@ -165,8 +165,5 @@ func isSet(data []byte, bitIndex uint) bool { // byteToBool returns false if val is 0, and true otherwise func byteToBool(val byte) bool { - if val == 0 { - return false - } - return true + return val != 0 } From e13e58565a014ce434b3895a984a2935c8f2abaa Mon Sep 17 00:00:00 2001 From: Tiago Peczenyj Date: Tue, 22 Apr 2025 01:04:20 +0200 Subject: [PATCH 5/8] fmt.Sprintf can be replaced with string concatenation (perfsprint) --- vendorconsent/benchmark_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendorconsent/benchmark_test.go b/vendorconsent/benchmark_test.go index c84df01..d231227 100644 --- a/vendorconsent/benchmark_test.go +++ b/vendorconsent/benchmark_test.go @@ -84,7 +84,7 @@ func BenchmarkParse(b *testing.B) { for _, tc := range testcases { tc := tc - b.Run(fmt.Sprintf("case %s", tc.label), func(b *testing.B) { + b.Run("case "+tc.label, func(b *testing.B) { var consent api.VendorConsents var err error for n := 0; n < b.N; n++ { From cde13e90c8740085609de18b37499fbd6a71cbe8 Mon Sep 17 00:00:00 2001 From: Tiago Peczenyj Date: Tue, 22 Apr 2025 01:08:20 +0200 Subject: [PATCH 6/8] run golangci linter wsl with --fix --- bitutils/bitutils.go | 16 ++++++++++++++++ bitutils/bitutils_test.go | 6 ++++++ vendorconsent/benchmark_test.go | 11 +++++++++++ vendorconsent/consent20_test.go | 2 ++ vendorconsent/consent_test.go | 11 +++++++++++ vendorconsent/tcf1/bitfield.go | 2 ++ vendorconsent/tcf1/consent.go | 2 ++ vendorconsent/tcf1/metadata.go | 12 ++++++++++++ vendorconsent/tcf1/metadata_test.go | 2 ++ vendorconsent/tcf1/rangesection.go | 15 +++++++++++++++ vendorconsent/tcf1/rangesection_test.go | 2 ++ vendorconsent/tcf1/test_utils.go | 10 ++++++++++ vendorconsent/tcf2/consent.go | 8 ++++++++ vendorconsent/tcf2/metadata.go | 16 +++++++++++++++- vendorconsent/tcf2/metadata_test.go | 5 +++++ vendorconsent/tcf2/pubrestrict.go | 12 ++++++++++++ vendorconsent/tcf2/pubrestrict_test.go | 2 ++ vendorconsent/tcf2/rangesection.go | 11 +++++++++++ vendorconsent/tcf2/rangesection_test.go | 5 +++++ vendorconsent/tcf2/test_utils.go | 10 ++++++++++ vendorlist/eager-parsing.go | 5 +++++ vendorlist/eager-parsing_test.go | 1 + vendorlist/lazy-parsing.go | 6 ++++++ vendorlist/shared_test.go | 4 ++++ vendorlist2/eager-parsing.go | 9 +++++++++ vendorlist2/lazy-parsing.go | 7 +++++++ vendorlist2/shared_test.go | 4 ++++ 27 files changed, 195 insertions(+), 1 deletion(-) diff --git a/bitutils/bitutils.go b/bitutils/bitutils.go index 696e10e..2c0e816 100644 --- a/bitutils/bitutils.go +++ b/bitutils/bitutils.go @@ -9,12 +9,15 @@ import ( func ParseByte4(data []byte, bitStartIndex uint) (byte, error) { startByte := bitStartIndex / 8 bitStartOffset := bitStartIndex % 8 + if bitStartOffset < 5 { if uint(len(data)) < (startByte + 1) { return 0, fmt.Errorf("ParseByte4 expected 4 bits to start at bit %d, but the consent string was only %d bytes long", bitStartIndex, len(data)) } + return (data[startByte] & (0xf0 >> bitStartOffset)) >> (4 - bitStartOffset), nil } + if uint(len(data)) < (startByte+2) && bitStartOffset > 4 { return 0, fmt.Errorf("ParseByte4 expected 4 bits to start at bit %d, but the consent string was only %d bytes long (needs second byte)", bitStartIndex, len(data)) } @@ -23,6 +26,7 @@ func ParseByte4(data []byte, bitStartIndex uint) (byte, error) { bitsConsumed := 8 - bitStartOffset overflow := 4 - bitsConsumed rightBits := (data[startByte+1] & (0xf0 << (4 - overflow))) >> (8 - overflow) + return leftBits | rightBits, nil } @@ -30,12 +34,15 @@ func ParseByte4(data []byte, bitStartIndex uint) (byte, error) { func ParseByte8(data []byte, bitStartIndex uint) (byte, error) { startByte := bitStartIndex / 8 bitStartOffset := bitStartIndex % 8 + if bitStartOffset == 0 { if uint(len(data)) < (startByte + 1) { return 0, fmt.Errorf("ParseByte8 expected 8 bits to start at bit %d, but the consent string was only %d bytes long", bitStartIndex, len(data)) } + return data[startByte], nil } + if uint(len(data)) < (startByte + 2) { return 0, fmt.Errorf("ParseByte8 expected 8 bitst to start at bit %d, but the consent string was only %d bytes long", bitStartIndex, len(data)) } @@ -43,6 +50,7 @@ func ParseByte8(data []byte, bitStartIndex uint) (byte, error) { leftBits := (data[startByte] & (0xff >> bitStartOffset)) << bitStartOffset shiftComplement := 8 - bitStartOffset rightBits := (data[startByte+1] & (0xff << shiftComplement)) >> shiftComplement + return leftBits | rightBits, nil } @@ -55,6 +63,7 @@ func ParseUInt12(data []byte, bitStartIndex uint) (uint16, error) { if endOffset > 0 { endByte++ } + if uint(len(data)) < endByte { return 0, fmt.Errorf("ParseUInt12 expected a 12-bit int to start at bit %d, but the consent string was only %d bytes long", bitStartIndex, len(data)) @@ -64,10 +73,12 @@ func ParseUInt12(data []byte, bitStartIndex uint) (uint16, error) { if err != nil { return 0, fmt.Errorf("ParseUInt12 error on left byte: %s", err) } + rightByte, err := ParseByte8(data, bitStartIndex+4) if err != nil { return 0, fmt.Errorf("ParseUInt12 error on right byte: %s", err) } + return binary.BigEndian.Uint16([]byte{leftByte, rightByte}), nil } @@ -75,12 +86,15 @@ func ParseUInt12(data []byte, bitStartIndex uint) (uint16, error) { func ParseUInt16(data []byte, bitStartIndex uint) (uint16, error) { startByte := bitStartIndex / 8 bitStartOffset := bitStartIndex % 8 + if bitStartOffset == 0 { if uint(len(data)) < (startByte + 2) { return 0, fmt.Errorf("ParseUInt16 expected a 16-bit int to start at bit %d, but the consent string was only %d bytes long", bitStartIndex, len(data)) } + return binary.BigEndian.Uint16(data[startByte : startByte+2]), nil } + if uint(len(data)) < (startByte + 3) { return 0, fmt.Errorf("ParseUInt16 expected a 16-bit int to start at bit %d, but the consent string was only %d bytes long", bitStartIndex, len(data)) } @@ -89,9 +103,11 @@ func ParseUInt16(data []byte, bitStartIndex uint) (uint16, error) { if err != nil { return 0, fmt.Errorf("ParseUInt16 error on left byte: %s", err) } + rightByte, err := ParseByte8(data, bitStartIndex+8) if err != nil { return 0, fmt.Errorf("ParseUInt16 error on right byte: %s", err) } + return binary.BigEndian.Uint16([]byte{leftByte, rightByte}), nil } diff --git a/bitutils/bitutils_test.go b/bitutils/bitutils_test.go index aba00f3..32eccfe 100644 --- a/bitutils/bitutils_test.go +++ b/bitutils/bitutils_test.go @@ -122,6 +122,7 @@ func assertNilError(t *testing.T, err error) { func assertStringsEqual(t *testing.T, expected, actual string) { t.Helper() + if actual != expected { t.Errorf("Strings were not equal. Expected %s, actual %s", expected, actual) } @@ -129,6 +130,7 @@ func assertStringsEqual(t *testing.T, expected, actual string) { func assertBytesEqual(t *testing.T, expected, actual byte) { t.Helper() + if actual != expected { t.Errorf("bytes were not equal. Expected %d, actual %d", expected, actual) } @@ -136,6 +138,7 @@ func assertBytesEqual(t *testing.T, expected, actual byte) { func assertUInt8sEqual(t *testing.T, expected, actual uint8) { t.Helper() + if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } @@ -143,6 +146,7 @@ func assertUInt8sEqual(t *testing.T, expected, actual uint8) { func assertUInt16sEqual(t *testing.T, expected, actual uint16) { t.Helper() + if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } @@ -150,6 +154,7 @@ func assertUInt16sEqual(t *testing.T, expected, actual uint16) { func assertIntsEqual(t *testing.T, expected, actual int) { t.Helper() + if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } @@ -157,6 +162,7 @@ func assertIntsEqual(t *testing.T, expected, actual int) { func assertBoolsEqual(t *testing.T, expected, actual bool) { t.Helper() + if actual != expected { t.Errorf("Bools were not equal. Expected %t, actual %t", expected, actual) } diff --git a/vendorconsent/benchmark_test.go b/vendorconsent/benchmark_test.go index d231227..c882977 100644 --- a/vendorconsent/benchmark_test.go +++ b/vendorconsent/benchmark_test.go @@ -66,6 +66,7 @@ func BenchmarkParse(b *testing.B) { for _, c := range testcases { all = append(all, c.consent) } + b.Run("all testcases", func(b *testing.B) { // on https://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go // section "A note on compiler optimisations" @@ -73,11 +74,14 @@ func BenchmarkParse(b *testing.B) { // ParseString function call, to prevent this we assign the result to // some variables out of the for loop scope var consent api.VendorConsents + var err error + max := len(all) for n := 0; n < b.N; n++ { consent, err = vendorconsent.ParseString(all[n%max]) } + _ = consent _ = err }) @@ -86,10 +90,12 @@ func BenchmarkParse(b *testing.B) { tc := tc b.Run("case "+tc.label, func(b *testing.B) { var consent api.VendorConsents + var err error for n := 0; n < b.N; n++ { consent, err = vendorconsent.ParseString(tc.consent) } + _ = consent _ = err }) @@ -106,13 +112,16 @@ func BenchmarkVerify(b *testing.B) { if consentFile == "" { b.SkipNow() } + readFile, err := os.Open(consentFile) if err != nil { b.FailNow() // abort } + defer readFile.Close() fileScanner := bufio.NewScanner(readFile) fileScanner.Split(bufio.ScanLines) + var consents []string for fileScanner.Scan() { @@ -127,10 +136,12 @@ func BenchmarkVerify(b *testing.B) { // ParseString function call, to prevent this we assign the result to // some variables out of the for loop scope var consent api.VendorConsents + var err error for n := 0; n < b.N; n++ { consent, err = vendorconsent.ParseString(consents[n%max]) } + _ = consent _ = err }) diff --git a/vendorconsent/consent20_test.go b/vendorconsent/consent20_test.go index 4403193..b14779a 100644 --- a/vendorconsent/consent20_test.go +++ b/vendorconsent/consent20_test.go @@ -92,6 +92,7 @@ func TestParseEmptyString(t *testing.T) { func assertInvalid20(t *testing.T, urlEncodedString, expectError string) { t.Helper() + data, err := base64.RawURLEncoding.DecodeString(urlEncodedString) assertNilError(t, err) assertInvalidBytes20(t, data, expectError) @@ -99,6 +100,7 @@ func assertInvalid20(t *testing.T, urlEncodedString, expectError string) { func assertInvalidBytes20(t *testing.T, data []byte, expectError string) { t.Helper() + if consent, err := tcf2.Parse(data); err == nil { t.Errorf("base64 URL-encoded string %s was considered valid, but shouldn't be. MaxVendorID: %d. len(data): %d", base64.RawURLEncoding.EncodeToString(data), consent.MaxVendorID(), len(data)) } else if err.Error() != expectError { diff --git a/vendorconsent/consent_test.go b/vendorconsent/consent_test.go index e9c898b..8d4eb75 100644 --- a/vendorconsent/consent_test.go +++ b/vendorconsent/consent_test.go @@ -86,6 +86,7 @@ func TestParseValidString(t *testing.T) { func assertInvalid(t *testing.T, urlEncodedString, expectError string) { t.Helper() + data, err := base64.RawURLEncoding.DecodeString(urlEncodedString) assertNilError(t, err) assertInvalidBytes(t, data, expectError) @@ -93,6 +94,7 @@ func assertInvalid(t *testing.T, urlEncodedString, expectError string) { func assertInvalidBytes(t *testing.T, data []byte, expectError string) { t.Helper() + if consent, err := tcf1.Parse(data); err == nil { t.Errorf("base64 URL-encoded string %s was considered valid, but shouldn't be. MaxVendorID: %d. len(data): %d", base64.RawURLEncoding.EncodeToString(data), consent.MaxVendorID(), len(data)) } else if err.Error() != expectError { @@ -103,11 +105,13 @@ func assertInvalidBytes(t *testing.T, data []byte, expectError string) { func decode(t *testing.T, encodedString string) []byte { data, err := base64.RawURLEncoding.DecodeString(encodedString) assertNilError(t, err) + return data } func assertStringsEqual(t *testing.T, expected, actual string) { t.Helper() + if actual != expected { t.Errorf("Strings were not equal. Expected %s, actual %s", expected, actual) } @@ -115,6 +119,7 @@ func assertStringsEqual(t *testing.T, expected, actual string) { func assertUInt8sEqual(t *testing.T, expected, actual uint8) { t.Helper() + if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } @@ -122,6 +127,7 @@ func assertUInt8sEqual(t *testing.T, expected, actual uint8) { func assertUInt16sEqual(t *testing.T, expected, actual uint16) { t.Helper() + if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } @@ -129,6 +135,7 @@ func assertUInt16sEqual(t *testing.T, expected, actual uint16) { func assertIntsEqual(t *testing.T, expected, actual int) { t.Helper() + if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } @@ -136,6 +143,7 @@ func assertIntsEqual(t *testing.T, expected, actual int) { func assertBoolsEqual(t *testing.T, expected, actual bool) { t.Helper() + if actual != expected { t.Errorf("Bools were not equal. Expected %t, actual %t", expected, actual) } @@ -143,10 +151,12 @@ func assertBoolsEqual(t *testing.T, expected, actual bool) { func buildMap(keys ...uint) map[uint]struct{} { var s struct{} + m := make(map[uint]struct{}, len(keys)) for _, key := range keys { m[key] = s } + return m } @@ -160,6 +170,7 @@ func assertNilError(t *testing.T, err error) { func isSet(data []byte, bitIndex uint) bool { byteIndex := bitIndex / 8 bitOffset := bitIndex % 8 + return byteToBool(data[byteIndex] & (0x80 >> bitOffset)) } diff --git a/vendorconsent/tcf1/bitfield.go b/vendorconsent/tcf1/bitfield.go index 269a771..e224f23 100644 --- a/vendorconsent/tcf1/bitfield.go +++ b/vendorconsent/tcf1/bitfield.go @@ -21,6 +21,7 @@ func parseBitField(data consentMetadata) (*consentBitField, error) { if (vendorBitsRequired-3)%8 > 0 { otherBytesRequired = otherBytesRequired + 1 } + dataLengthRequired := 22 + otherBytesRequired if uint(len(data)) < uint(dataLengthRequired) { return nil, fmt.Errorf("a BitField for %d vendors requires a consent string of %d bytes. This consent string had %d", vendorBitsRequired, dataLengthRequired, len(data)) @@ -48,6 +49,7 @@ func (f *consentBitField) VendorConsent(id uint16) bool { if id <= 3 { return byteToBool(f.firstThree & (0x08 >> id)) } + return isSet(f.others, uint(id-4)) } diff --git a/vendorconsent/tcf1/consent.go b/vendorconsent/tcf1/consent.go index 505db6c..63ac78d 100644 --- a/vendorconsent/tcf1/consent.go +++ b/vendorconsent/tcf1/consent.go @@ -15,10 +15,12 @@ func ParseString(consent string) (api.VendorConsents, error) { buff := []byte(consent) decoded := buff + n, err := base64.RawURLEncoding.Decode(decoded, buff) if err != nil { return nil, err } + decoded = decoded[:n:n] return Parse(decoded) diff --git a/vendorconsent/tcf1/metadata.go b/vendorconsent/tcf1/metadata.go index 564fd88..bffb6a6 100644 --- a/vendorconsent/tcf1/metadata.go +++ b/vendorconsent/tcf1/metadata.go @@ -17,16 +17,20 @@ func parseMetadata(data []byte) (consentMetadata, error) { if len(data) < 22 { return nil, fmt.Errorf("vendor consent strings are at least 22 bytes long. This one was %d", len(data)) } + metadata := consentMetadata(data) if metadata.MaxVendorID() < 1 { return nil, fmt.Errorf("the consent string encoded a MaxVendorID of %d, but this value must be greater than or equal to 1", metadata.MaxVendorID()) } + if metadata.Version() < 1 { return nil, fmt.Errorf("the consent string encoded a Version of %d, but this value must be greater than or equal to 1", metadata.Version()) } + if metadata.VendorListVersion() == 0 { return nil, errInvalidVendorListVersion } + return consentMetadata(data), nil } @@ -58,6 +62,7 @@ func (c consentMetadata) Created() time.Time { c[3]<<2 | c[4]>>6, c[4]<<2 | c[5]>>6, })) + return time.Unix(deciseconds/decisPerOne, (deciseconds%decisPerOne)*nanosPerDeci) } @@ -73,6 +78,7 @@ func (c consentMetadata) LastUpdated() time.Time { c[7]<<6 | c[8]>>2, c[8]<<6 | c[9]>>2, })) + return time.Unix(deciseconds/decisPerOne, (deciseconds%decisPerOne)*nanosPerDeci) } @@ -80,6 +86,7 @@ func (c consentMetadata) CmpID() uint16 { // Stored in bits 78-89... which is [000000xx xxxxxxxx xx000000] starting at the 10th byte leftByte := ((c[9] & 0x03) << 2) | c[10]>>6 rightByte := (c[10] << 2) | c[11]>>6 + return binary.BigEndian.Uint16([]byte{leftByte, rightByte}) } @@ -87,6 +94,7 @@ func (c consentMetadata) CmpVersion() uint16 { // Stored in bits 90-101.. which is [00xxxxxx xxxxxx00] starting at the 12th byte leftByte := (c[11] >> 2) & 0x0f rightByte := (c[11] << 6) | c[12]>>2 + return binary.BigEndian.Uint16([]byte{leftByte, rightByte}) } @@ -100,6 +108,7 @@ func (c consentMetadata) ConsentLanguage() string { // Each letter is stored as 6 bits, with A=0 and Z=25 leftChar := ((c[13] & 0x0f) << 2) | c[14]>>6 rightChar := c[14] & 0x3f + return string([]byte{leftChar + 65, rightChar + 65}) // Unicode A-Z is 65-90 } @@ -107,6 +116,7 @@ func (c consentMetadata) VendorListVersion() uint16 { // The vendor list version is stored in bits 120 - 131 rightByte := ((c[16] & 0xf0) >> 4) | ((c[15] & 0x0f) << 4) leftByte := c[15] >> 4 + return binary.BigEndian.Uint16([]byte{leftByte, rightByte}) } @@ -119,6 +129,7 @@ func (c consentMetadata) MaxVendorID() uint16 { // The max vendor ID is stored in bits 156 - 171 leftByte := byte((c[19]&0x0f)<<4 + (c[20]&0xf0)>>4) rightByte := byte((c[20]&0x0f)<<4 + (c[21]&0xf0)>>4) + return binary.BigEndian.Uint16([]byte{leftByte, rightByte}) } @@ -132,5 +143,6 @@ func (c consentMetadata) PurposeAllowed(id consentconstants.Purpose) bool { func isSet(data []byte, bitIndex uint) bool { byteIndex := bitIndex / 8 bitOffset := bitIndex % 8 + return byteToBool(data[byteIndex] & (0x80 >> bitOffset)) } diff --git a/vendorconsent/tcf1/metadata_test.go b/vendorconsent/tcf1/metadata_test.go index 5f4eac2..56da09d 100644 --- a/vendorconsent/tcf1/metadata_test.go +++ b/vendorconsent/tcf1/metadata_test.go @@ -8,6 +8,7 @@ import ( func TestCreatedDate(t *testing.T) { consent, err := Parse(decode(t, "BIRAfK8OOHsDFABABBAAABAAAAAAEA")) assertNilError(t, err) + created := consent.Created().UTC() year, month, day := created.Date() assertIntsEqual(t, 1998, year) @@ -21,6 +22,7 @@ func TestCreatedDate(t *testing.T) { func TestLastUpdated(t *testing.T) { consent, err := Parse(decode(t, "BIRAfK8OOHsDFABABBAAABAAAAAAEA")) assertNilError(t, err) + updated := consent.LastUpdated().UTC() year, month, day := updated.Date() assertIntsEqual(t, 2018, year) diff --git a/vendorconsent/tcf1/rangesection.go b/vendorconsent/tcf1/rangesection.go index dce270e..30a2f2b 100644 --- a/vendorconsent/tcf1/rangesection.go +++ b/vendorconsent/tcf1/rangesection.go @@ -12,16 +12,19 @@ func parseRangeSection(data consentMetadata) (*rangeSection, error) { if len(data) < 24 { return nil, fmt.Errorf("vendor consent strings using RangeSections require at least 24 bytes. Got %d", len(data)) } + numEntries := parseNumEntries(data) // Parse out the "exceptions" here. currentOffset := uint(186) exceptions := make([]rangeException, numEntries) + for i := range exceptions { bitsConsumed, err := parseException(&exceptions[i], data, currentOffset) if err != nil { return nil, err } + currentOffset = currentOffset + bitsConsumed } @@ -56,21 +59,27 @@ func parseException(dst *rangeException, data consentMetadata, initialBit uint) if err != nil { return 0, err } + end, err := parseUInt16(data, initialBit+17) if err != nil { return 0, err } + if start == 0 { return 0, fmt.Errorf("bit %d range entry exclusion starts at 0, but the min vendor ID is 1", initialBit) } + if end > data.MaxVendorID() { return 0, fmt.Errorf("bit %d range entry exclusion ends at %d, but the max vendor ID is %d", initialBit, end, data.MaxVendorID()) } + if end <= start { return 0, fmt.Errorf("bit %d range entry excludes vendors [%d, %d]. The start should be less than the end", initialBit, start, end) } + dst.startID = start dst.endID = end + return 33, nil } @@ -78,12 +87,14 @@ func parseException(dst *rangeException, data consentMetadata, initialBit uint) if err != nil { return 0, err } + if vendorID == 0 || vendorID > data.MaxVendorID() { return 0, fmt.Errorf("bit %d range entry excludes vendor %d, but only vendors [1, %d] are valid", initialBit, vendorID, data.MaxVendorID()) } dst.startID = vendorID dst.endID = vendorID + return 17, nil } @@ -91,12 +102,15 @@ func parseException(dst *rangeException, data consentMetadata, initialBit uint) func parseUInt16(data []byte, bitStartIndex uint) (uint16, error) { startByte := bitStartIndex / 8 bitStartOffset := bitStartIndex % 8 + if bitStartOffset == 0 { if uint(len(data)) < (startByte + 2) { return 0, fmt.Errorf("rangeSection expected a 16-bit vendorID to start at bit %d, but the consent string was only %d bytes long", bitStartIndex, len(data)) } + return binary.BigEndian.Uint16(data[startByte : startByte+2]), nil } + if uint(len(data)) < (startByte + 3) { return 0, fmt.Errorf("rangeSection expected a 16-bit vendorID to start at bit %d, but the consent string was only %d bytes long", bitStartIndex, len(data)) } @@ -132,6 +146,7 @@ func (p rangeSection) VendorConsent(id uint16) bool { // TODO check if possible return !p.defaultValue } } + return p.defaultValue } diff --git a/vendorconsent/tcf1/rangesection_test.go b/vendorconsent/tcf1/rangesection_test.go index f07e44d..eafe28d 100644 --- a/vendorconsent/tcf1/rangesection_test.go +++ b/vendorconsent/tcf1/rangesection_test.go @@ -55,8 +55,10 @@ func TestParseUInt16(t *testing.T) { func doParseIntTest(t *testing.T, data []byte, offset, expected int) { t.Helper() + parsedVal, err := parseUInt16(data, uint(offset)) assertNilError(t, err) + if parsedVal != uint16(expected) { t.Errorf("Failed to parse value. Got %d", parsedVal) } diff --git a/vendorconsent/tcf1/test_utils.go b/vendorconsent/tcf1/test_utils.go index 5519ab2..7b58a56 100644 --- a/vendorconsent/tcf1/test_utils.go +++ b/vendorconsent/tcf1/test_utils.go @@ -7,6 +7,7 @@ import ( func assertInvalid(t *testing.T, urlEncodedString, expectError string) { t.Helper() + data, err := base64.RawURLEncoding.DecodeString(urlEncodedString) assertNilError(t, err) assertInvalidBytes(t, data, expectError) @@ -14,6 +15,7 @@ func assertInvalid(t *testing.T, urlEncodedString, expectError string) { func assertInvalidBytes(t *testing.T, data []byte, expectError string) { t.Helper() + if consent, err := Parse(data); err == nil { t.Errorf("base64 URL-encoded string %s was considered valid, but shouldn't be. MaxVendorID: %d. len(data): %d", base64.RawURLEncoding.EncodeToString(data), consent.MaxVendorID(), len(data)) } else if err.Error() != expectError { @@ -24,6 +26,7 @@ func assertInvalidBytes(t *testing.T, data []byte, expectError string) { func decode(t *testing.T, encodedString string) []byte { data, err := base64.RawURLEncoding.DecodeString(encodedString) assertNilError(t, err) + return data } @@ -35,6 +38,7 @@ func assertNilError(t *testing.T, err error) { func assertStringsEqual(t *testing.T, expected, actual string) { t.Helper() + if actual != expected { t.Errorf("Strings were not equal. Expected %s, actual %s", expected, actual) } @@ -42,6 +46,7 @@ func assertStringsEqual(t *testing.T, expected, actual string) { func assertUInt8sEqual(t *testing.T, expected, actual uint8) { t.Helper() + if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } @@ -49,6 +54,7 @@ func assertUInt8sEqual(t *testing.T, expected, actual uint8) { func assertUInt16sEqual(t *testing.T, expected, actual uint16) { t.Helper() + if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } @@ -56,6 +62,7 @@ func assertUInt16sEqual(t *testing.T, expected, actual uint16) { func assertIntsEqual(t *testing.T, expected, actual int) { t.Helper() + if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } @@ -63,6 +70,7 @@ func assertIntsEqual(t *testing.T, expected, actual int) { func assertBoolsEqual(t *testing.T, expected, actual bool) { t.Helper() + if actual != expected { t.Errorf("Bools were not equal. Expected %t, actual %t", expected, actual) } @@ -70,9 +78,11 @@ func assertBoolsEqual(t *testing.T, expected, actual bool) { func buildMap(keys ...uint) map[uint]struct{} { var s struct{} + m := make(map[uint]struct{}, len(keys)) for _, key := range keys { m[key] = s } + return m } diff --git a/vendorconsent/tcf2/consent.go b/vendorconsent/tcf2/consent.go index dc5669d..614aa19 100644 --- a/vendorconsent/tcf2/consent.go +++ b/vendorconsent/tcf2/consent.go @@ -27,10 +27,12 @@ func ParseString(consent string) (api.VendorConsents, error) { buff := []byte(consent) decoded := buff + n, err := base64.RawURLEncoding.Decode(decoded, buff) if err != nil { return nil, err } + decoded = decoded[:n:n] return Parse(decoded) @@ -45,9 +47,11 @@ func Parse(data []byte) (api.VendorConsents, error) { } var vendorConsents vendorConsentsResolver + var vendorLegitInts vendorConsentsResolver var legitIntStart uint + var pubRestrictsStart uint // Bit 229 determines whether or not the consent string encodes Vendor data in a RangeSection or BitField. // We know from parseMetadata that we have at least 29*8=232 bits available @@ -56,12 +60,14 @@ func Parse(data []byte) (api.VendorConsents, error) { } else { vendorConsents, legitIntStart, err = parseBitField(metadata, metadata.MaxVendorID(), 230) } + if err != nil { return nil, err } metadata.vendorConsents = vendorConsents metadata.vendorLegitimateInterestStart = legitIntStart + 17 + legIntMaxVend, err := bitutils.ParseUInt16(data, legitIntStart) if err != nil { return nil, err @@ -70,11 +76,13 @@ func Parse(data []byte) (api.VendorConsents, error) { if legitIntStart+16 >= uint(len(data))*8 { return nil, fmt.Errorf("invalid consent data: no legitimate interest start position") } + if isSet(data, legitIntStart+16) { vendorLegitInts, pubRestrictsStart, err = parseRangeSection(metadata, legIntMaxVend, metadata.vendorLegitimateInterestStart) } else { vendorLegitInts, pubRestrictsStart, err = parseBitField(metadata, legIntMaxVend, metadata.vendorLegitimateInterestStart) } + if err != nil { return nil, err } diff --git a/vendorconsent/tcf2/metadata.go b/vendorconsent/tcf2/metadata.go index b414671..1c31c6d 100644 --- a/vendorconsent/tcf2/metadata.go +++ b/vendorconsent/tcf2/metadata.go @@ -17,19 +17,22 @@ func parseMetadata(data []byte) (ConsentMetadata, error) { if len(data) < 29 { return ConsentMetadata{}, fmt.Errorf("vendor consent strings are at least 29 bytes long. This one was %d", len(data)) } + metadata := ConsentMetadata{ data: data, } if metadata.Version() < 2 { version := metadata.Version() metadata.data = nil + return metadata, fmt.Errorf("the consent string encoded a Version of %d, but this value must be greater than or equal to 2", version) } + if metadata.VendorListVersion() == 0 { metadata.data = nil return metadata, errInvalidVendorListVersion - } + return metadata, nil } @@ -78,6 +81,7 @@ func (c ConsentMetadata) Created() time.Time { c.data[3]<<2 | c.data[4]>>6, c.data[4]<<2 | c.data[5]>>6, })) + return time.Unix(deciseconds/decisPerOne, (deciseconds%decisPerOne)*nanosPerDeci) } @@ -94,6 +98,7 @@ func (c ConsentMetadata) LastUpdated() time.Time { c.data[7]<<6 | c.data[8]>>2, c.data[8]<<6 | c.data[9]>>2, })) + return time.Unix(deciseconds/decisPerOne, (deciseconds%decisPerOne)*nanosPerDeci) } @@ -102,6 +107,7 @@ func (c ConsentMetadata) CmpID() uint16 { // Stored in bits 78-89... which is [000000xx xxxxxxxx xx000000] starting at the 10th byte leftByte := ((c.data[9] & 0x03) << 2) | c.data[10]>>6 rightByte := (c.data[10] << 2) | c.data[11]>>6 + return binary.BigEndian.Uint16([]byte{leftByte, rightByte}) } @@ -110,6 +116,7 @@ func (c ConsentMetadata) CmpVersion() uint16 { // Stored in bits 90-101.. which is [00xxxxxx xxxxxx00] starting at the 12th byte leftByte := (c.data[11] >> 2) & 0x0f rightByte := (c.data[11] << 6) | c.data[12]>>2 + return binary.BigEndian.Uint16([]byte{leftByte, rightByte}) } @@ -125,6 +132,7 @@ func (c ConsentMetadata) ConsentLanguage() string { // Each letter is stored as 6 bits, with A=0 and Z=25 leftChar := ((c.data[13] & 0x0f) << 2) | c.data[14]>>6 rightChar := c.data[14] & 0x3f + return string([]byte{leftChar + 65, rightChar + 65}) // Unicode A-Z is 65-90 } @@ -138,6 +146,7 @@ func (c ConsentMetadata) VendorListVersion() uint16 { // The vendor list version is stored in bits 121 - 132 rightByte := ((c.data[16] & 0xf0) >> 4) | ((c.data[15] & 0x0f) << 4) leftByte := c.data[15] >> 4 + return binary.BigEndian.Uint16([]byte{leftByte, rightByte}) } @@ -152,6 +161,7 @@ func (c ConsentMetadata) MaxVendorID() uint16 { // The max vendor ID is stored in bits 214 - 229 leftByte := ((c.data[26] & 0x07) << 5) | ((c.data[27] & 0xf8) >> 3) rightByte := ((c.data[27] & 0x07) << 5) | ((c.data[28] & 0xf8) >> 3) + return binary.BigEndian.Uint16([]byte{leftByte, rightByte}) } @@ -162,6 +172,7 @@ func (c ConsentMetadata) PurposeAllowed(id consentconstants.Purpose) bool { if id > 24 { return false } + return isSet(c.data, uint(id)+151) } @@ -172,6 +183,7 @@ func (c ConsentMetadata) PurposeLITransparency(id consentconstants.Purpose) bool if id > 24 { return false } + return isSet(c.data, uint(id)+175) } @@ -185,6 +197,7 @@ func (c ConsentMetadata) SpecialFeatureOptIn(id uint16) bool { if id > 12 { return false } + return isSet(c.data, 140+uint(id)-1) } @@ -207,5 +220,6 @@ func (c ConsentMetadata) CheckPubRestriction(purposeID, restrictType uint8, vend func isSet(data []byte, bitIndex uint) bool { byteIndex := bitIndex / 8 bitOffset := bitIndex % 8 + return byteToBool(data[byteIndex] & (0x80 >> bitOffset)) } diff --git a/vendorconsent/tcf2/metadata_test.go b/vendorconsent/tcf2/metadata_test.go index ea6c18b..198887f 100644 --- a/vendorconsent/tcf2/metadata_test.go +++ b/vendorconsent/tcf2/metadata_test.go @@ -8,6 +8,7 @@ import ( func TestCreatedDate(t *testing.T) { consent, err := Parse(decode(t, "COvcSpYOvcSpYC9AAAENAPCAAAAAAAAAAAAACvwDQABAAIAAYABIAC4AJQAagA9ACEAPgAjIBJoCvAK-AAAAAA")) assertNilError(t, err) + created := consent.Created().UTC() year, month, day := created.Date() assertIntsEqual(t, 2020, year) @@ -21,6 +22,7 @@ func TestCreatedDate(t *testing.T) { func TestLastUpdate(t *testing.T) { consent, err := Parse(decode(t, "COvcSpYOvcSpYC9AAAENAPCAAAAAAAAAAAAACvwDQABAAIAAYABIAC4AJQAagA9ACEAPgAjIBJoCvAK-AAAAAA")) assertNilError(t, err) + updated := consent.LastUpdated().UTC() year, month, day := updated.Date() assertIntsEqual(t, 2020, year) @@ -62,6 +64,7 @@ func TestLanguageExtremes(t *testing.T) { func TestTCFPolicyVersion(t *testing.T) { baseConsent := "CPtGDMAPtGDMALMAAAENA_C_AAAAAAAAACiQAAAAAAAA" index := 22 // policy version is at the 23rd 6-bit base64 position + tests := []struct { name string base64Char string @@ -121,6 +124,7 @@ func TestTCFPolicyVersion(t *testing.T) { func TestTCF2Fields(t *testing.T) { baseConsent, err := Parse(decode(t, "COx3XOeOx3XOeLkAAAENAfCIAAAAAHgAAIAAAAAAAAAA")) assertNilError(t, err) + consent := baseConsent.(ConsentMetadata) assertBoolsEqual(t, true, consent.PurposeOneTreatment()) @@ -131,6 +135,7 @@ func TestTCF2Fields(t *testing.T) { func TestLITransparency(t *testing.T) { baseConsent, err := Parse(decode(t, "COx3XOeOx3XOeLkAAAENAfCIAAAAAHgAAIAAAAAAAAAA")) assertNilError(t, err) + consent := baseConsent.(ConsentMetadata) assertBoolsEqual(t, false, consent.PurposeLITransparency(1)) diff --git a/vendorconsent/tcf2/pubrestrict.go b/vendorconsent/tcf2/pubrestrict.go index ee1cb4d..3333412 100644 --- a/vendorconsent/tcf2/pubrestrict.go +++ b/vendorconsent/tcf2/pubrestrict.go @@ -11,6 +11,7 @@ const assumedMaxVendorID uint16 = 32767 func parsePubRestriction(metadata ConsentMetadata, startbit uint) (*pubRestrictions, uint, error) { data := metadata.data + numRestrictions, err := bitutils.ParseUInt12(data, startbit) if err != nil { return nil, 0, fmt.Errorf("Error on parsing the number of publisher restrictions: %s", err.Error()) @@ -19,31 +20,39 @@ func parsePubRestriction(metadata ConsentMetadata, startbit uint) (*pubRestricti // Parse out the "exceptions" here. currentOffset := startbit + 12 restrictions := make(map[byte]pubRestriction, numRestrictions) + for j := uint16(0); j < numRestrictions; j++ { restrictData, err := bitutils.ParseByte8(data, currentOffset) if err != nil { return nil, 0, fmt.Errorf("Error on parsing the publisher restriction purpose/type: %s", err.Error()) } + currentOffset = currentOffset + 8 + numEntries, err := bitutils.ParseUInt12(data, currentOffset) if err != nil { return nil, 0, fmt.Errorf("Error on parsing the number of publisher restriction vendor ranges: %s", err.Error()) } + currentOffset = currentOffset + 12 vendors := make([]rangeConsent, numEntries) + for i := range vendors { bitsConsumed, err := parseRangeConsent(&vendors[i], data, currentOffset, assumedMaxVendorID) if err != nil { return nil, 0, err } + currentOffset = currentOffset + bitsConsumed } + restrictions[restrictData] = pubRestriction{ purposeID: (restrictData & 0xfc) >> 2, restrictType: (restrictData & 0x03), vendors: vendors, } } + return &pubRestrictions{restrictions: restrictions}, currentOffset, nil } @@ -59,14 +68,17 @@ type pubRestriction struct { func (p *pubRestrictions) CheckPubRestriction(purposeID, restrictType uint8, vendor uint16) bool { key := byte(purposeID<<2 | (restrictType & 0x03)) + restriction, ok := p.restrictions[key] if !ok { return false } + for i := 0; i < len(restriction.vendors); i++ { if restriction.vendors[i].Contains(vendor) { return true } } + return false } diff --git a/vendorconsent/tcf2/pubrestrict_test.go b/vendorconsent/tcf2/pubrestrict_test.go index 0657c6c..a2c0373 100644 --- a/vendorconsent/tcf2/pubrestrict_test.go +++ b/vendorconsent/tcf2/pubrestrict_test.go @@ -7,6 +7,7 @@ import ( func TestPubRestrictions(t *testing.T) { baseConsent, err := Parse(decode(t, "COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA")) assertNilError(t, err) + consent := baseConsent.(ConsentMetadata) // Extra random verification to ensure the basics are solid assertUInt8sEqual(t, 2, consent.Version()) @@ -28,6 +29,7 @@ func TestPubRestrictions(t *testing.T) { func TestPubRestrictions2(t *testing.T) { baseConsent, err := Parse(decode(t, "COxPe2TOxPe2TALABAENAPCgAAAAAAAAAAAAAFAAAAoAAA4IACACAIABgACAFA4ADACAAIygAGADwAQBIAIAIB0AEAEBSACACAA")) assertNilError(t, err) + consent := baseConsent.(ConsentMetadata) assertBoolsEqual(t, false, consent.PurposeOneTreatment()) diff --git a/vendorconsent/tcf2/rangesection.go b/vendorconsent/tcf2/rangesection.go index fa14353..aa33628 100644 --- a/vendorconsent/tcf2/rangesection.go +++ b/vendorconsent/tcf2/rangesection.go @@ -22,11 +22,13 @@ func parseRangeSection(metadata ConsentMetadata, maxVendorID uint16, startbit ui // Parse out the "exceptions" here. currentOffset := startbit + 12 consents := make([]rangeConsent, numEntries) + for i := range consents { bitsConsumed, err := parseRangeConsent(&consents[i], data, currentOffset, maxVendorID) if err != nil { return nil, 0, err } + currentOffset = currentOffset + bitsConsumed } @@ -51,21 +53,27 @@ func parseRangeConsent(dst *rangeConsent, data []byte, initialBit uint, maxVendo if err != nil { return 0, err } + end, err := bitutils.ParseUInt16(data, initialBit+17) if err != nil { return 0, err } + if start == 0 { return 0, fmt.Errorf("bit %d range entry exclusion starts at 0, but the min vendor ID is 1", initialBit) } + if end > maxVendorID { return 0, fmt.Errorf("bit %d range entry exclusion ends at %d, but the max vendor ID is %d", initialBit, end, maxVendorID) } + if end <= start { return 0, fmt.Errorf("bit %d range entry excludes vendors [%d, %d]. The start should be less than the end", initialBit, start, end) } + dst.startID = start dst.endID = end + return 33, nil } @@ -73,12 +81,14 @@ func parseRangeConsent(dst *rangeConsent, data []byte, initialBit uint, maxVendo if err != nil { return 0, err } + if vendorID == 0 || vendorID > maxVendorID { return 0, fmt.Errorf("bit %d range entry excludes vendor %d, but only vendors [1, %d] are valid", initialBit, vendorID, maxVendorID) } dst.startID = vendorID dst.endID = vendorID + return 17, nil } @@ -107,6 +117,7 @@ func (p *rangeSection) VendorConsent(id uint16) bool { return true } } + return false } diff --git a/vendorconsent/tcf2/rangesection_test.go b/vendorconsent/tcf2/rangesection_test.go index 85216ac..7daf58f 100644 --- a/vendorconsent/tcf2/rangesection_test.go +++ b/vendorconsent/tcf2/rangesection_test.go @@ -29,21 +29,26 @@ func TestRangeSectionConsent(t *testing.T) { for i := uint16(1); i <= consent.MaxVendorID(); i++ { _, expected := vendorsWithConsent[uint(i)] actual := consent.VendorConsent(i) + if expected != actual { fmt.Printf("Vendor: %d failed\n", 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) } } diff --git a/vendorconsent/tcf2/test_utils.go b/vendorconsent/tcf2/test_utils.go index 9b15ca3..fda641c 100644 --- a/vendorconsent/tcf2/test_utils.go +++ b/vendorconsent/tcf2/test_utils.go @@ -7,6 +7,7 @@ import ( func assertInvalid(t *testing.T, urlEncodedString, expectError string) { t.Helper() + data, err := base64.RawURLEncoding.DecodeString(urlEncodedString) assertNilError(t, err) assertInvalidBytes(t, data, expectError) @@ -14,6 +15,7 @@ func assertInvalid(t *testing.T, urlEncodedString, expectError string) { func assertInvalidBytes(t *testing.T, data []byte, expectError string) { t.Helper() + if consent, err := Parse(data); err == nil { t.Errorf("base64 URL-encoded string %s was considered valid, but shouldn't be. MaxVendorID: %d. len(data): %d", base64.RawURLEncoding.EncodeToString(data), consent.MaxVendorID(), len(data)) } else if err.Error() != expectError { @@ -24,6 +26,7 @@ func assertInvalidBytes(t *testing.T, data []byte, expectError string) { func decode(t *testing.T, encodedString string) []byte { data, err := base64.RawURLEncoding.DecodeString(encodedString) assertNilError(t, err) + return data } @@ -41,6 +44,7 @@ func assertError(t *testing.T, err error) { func assertStringsEqual(t *testing.T, expected, actual string) { t.Helper() + if actual != expected { t.Errorf("Strings were not equal. Expected %s, actual %s", expected, actual) } @@ -48,6 +52,7 @@ func assertStringsEqual(t *testing.T, expected, actual string) { func assertUInt8sEqual(t *testing.T, expected, actual uint8) { t.Helper() + if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } @@ -55,6 +60,7 @@ func assertUInt8sEqual(t *testing.T, expected, actual uint8) { func assertUInt16sEqual(t *testing.T, expected, actual uint16) { t.Helper() + if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } @@ -62,6 +68,7 @@ func assertUInt16sEqual(t *testing.T, expected, actual uint16) { func assertIntsEqual(t *testing.T, expected, actual int) { t.Helper() + if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } @@ -69,6 +76,7 @@ func assertIntsEqual(t *testing.T, expected, actual int) { func assertBoolsEqual(t *testing.T, expected, actual bool) { t.Helper() + if actual != expected { t.Errorf("Bools were not equal. Expected %t, actual %t", expected, actual) } @@ -76,9 +84,11 @@ func assertBoolsEqual(t *testing.T, expected, actual bool) { func buildMap(keys ...uint) map[uint]struct{} { var s struct{} + m := make(map[uint]struct{}, len(keys)) for _, key := range keys { m[key] = s } + return m } diff --git a/vendorlist/eager-parsing.go b/vendorlist/eager-parsing.go index e72095b..9c1e82e 100644 --- a/vendorlist/eager-parsing.go +++ b/vendorlist/eager-parsing.go @@ -25,6 +25,7 @@ func ParseEagerly(data []byte) (api.VendorList, error) { if contract.Version == 0 { return nil, errors.New("data.vendorListVersion was 0 or undefined. Versions should start at 1") } + if len(contract.Vendors) == 0 { return nil, errors.New("data.vendors was undefined or had no elements") } @@ -54,10 +55,13 @@ func parseVendor(contract vendorListVendorContract) parsedVendor { func mapify(input []uint8) map[consentconstants.Purpose]struct{} { m := make(map[consentconstants.Purpose]struct{}, len(input)) + var s struct{} + for _, value := range input { m[consentconstants.Purpose(value)] = s } + return m } @@ -80,6 +84,7 @@ func (l parsedVendorList) Vendor(vendorID uint16) api.Vendor { if ok { return vendor } + return nil } diff --git a/vendorlist/eager-parsing_test.go b/vendorlist/eager-parsing_test.go index 96aaf19..295e1b2 100644 --- a/vendorlist/eager-parsing_test.go +++ b/vendorlist/eager-parsing_test.go @@ -12,6 +12,7 @@ func TestEagerlyParsedVendorList(t *testing.T) { if err != nil { t.Errorf("ParseEagerly returned an unexpected error: %v", err) } + return vendorList }) } diff --git a/vendorlist/lazy-parsing.go b/vendorlist/lazy-parsing.go index 7122d72..e5b1e32 100644 --- a/vendorlist/lazy-parsing.go +++ b/vendorlist/lazy-parsing.go @@ -27,6 +27,7 @@ func (l lazyVendorList) SpecVersion() uint16 { if val, ok := lazyParseInt(l, "gvlSpecificationVersion"); ok { return uint16(val) } + return 0 } @@ -34,11 +35,13 @@ func (l lazyVendorList) Version() uint16 { if val, ok := lazyParseInt(l, "vendorListVersion"); ok { return uint16(val) } + return 0 } func (l lazyVendorList) Vendor(vendorID uint16) api.Vendor { var vendorBytes []byte + jsonparser.ArrayEach(l, func(value []byte, dataType jsonparser.ValueType, offset int, err error) { if val, ok := lazyParseInt(value, "id"); ok { if uint16(val) == vendorID { @@ -50,6 +53,7 @@ func (l lazyVendorList) Vendor(vendorID uint16) api.Vendor { if len(vendorBytes) > 0 { return lazyVendor(vendorBytes) } + return nil } @@ -104,7 +108,9 @@ func lazyParseInt(data []byte, key string) (int, bool) { if err != nil { return 0, false } + return intVal, true } + return 0, false } diff --git a/vendorlist/shared_test.go b/vendorlist/shared_test.go index 6332fd0..147f3c7 100644 --- a/vendorlist/shared_test.go +++ b/vendorlist/shared_test.go @@ -59,6 +59,7 @@ const testData = ` func assertIntsEqual(t *testing.T, expected, actual int) { t.Helper() + if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } @@ -66,6 +67,7 @@ func assertIntsEqual(t *testing.T, expected, actual int) { func assertBoolsEqual(t *testing.T, expected, actual bool) { t.Helper() + if actual != expected { t.Errorf("Bools were not equal. Expected %t, actual %t", expected, actual) } @@ -73,9 +75,11 @@ func assertBoolsEqual(t *testing.T, expected, actual bool) { func assertNil(t *testing.T, value api.Vendor, expectNil bool) { t.Helper() + if expectNil && value != nil { t.Error("The vendor should be nil, but wasn't.") } + if !expectNil && value == nil { t.Errorf("The vendor should not be nil, but was.") } diff --git a/vendorlist2/eager-parsing.go b/vendorlist2/eager-parsing.go index 5f797ae..93bc250 100644 --- a/vendorlist2/eager-parsing.go +++ b/vendorlist2/eager-parsing.go @@ -53,19 +53,25 @@ func parseVendor(contract vendorListVendorContract) parsedVendor { func mapifyPurpose(input []uint8) map[consentconstants.Purpose]struct{} { m := make(map[consentconstants.Purpose]struct{}, len(input)) + var s struct{} + for _, value := range input { m[consentconstants.Purpose(value)] = s } + return m } func mapifySpecialFeature(input []uint8) map[consentconstants.SpecialFeature]struct{} { m := make(map[consentconstants.SpecialFeature]struct{}, len(input)) + var s struct{} + for _, value := range input { m[consentconstants.SpecialFeature(value)] = s } + return m } @@ -88,6 +94,7 @@ func (l parsedVendorList) Vendor(vendorID uint16) api.Vendor { if ok { return vendor } + return nil } @@ -104,6 +111,7 @@ func (l parsedVendor) Purpose(purposeID consentconstants.Purpose) (hasPurpose bo if !hasPurpose { _, hasPurpose = l.flexiblePurposes[purposeID] } + return } @@ -122,6 +130,7 @@ func (l parsedVendor) LegitimateInterest(purposeID consentconstants.Purpose) (ha if !hasLegitimateInterest { _, hasLegitimateInterest = l.flexiblePurposes[purposeID] } + return } diff --git a/vendorlist2/lazy-parsing.go b/vendorlist2/lazy-parsing.go index c8bda47..f254b19 100644 --- a/vendorlist2/lazy-parsing.go +++ b/vendorlist2/lazy-parsing.go @@ -27,6 +27,7 @@ func (l lazyVendorList) SpecVersion() uint16 { if val, ok := lazyParseInt(l, "gvlSpecificationVersion"); ok { return uint16(val) } + return 0 } @@ -34,6 +35,7 @@ func (l lazyVendorList) Version() uint16 { if val, ok := lazyParseInt(l, "vendorListVersion"); ok { return uint16(val) } + return 0 } @@ -42,6 +44,7 @@ func (l lazyVendorList) Vendor(vendorID uint16) api.Vendor { if err == nil && len(vendorBytes) > 0 { return lazyVendor(vendorBytes) } + return nil } @@ -52,6 +55,7 @@ func (l lazyVendor) Purpose(purposeID consentconstants.Purpose) bool { if exists { return true } + return idExists(l, int(purposeID), "flexiblePurposes") } @@ -65,6 +69,7 @@ func (l lazyVendor) LegitimateInterest(purposeID consentconstants.Purpose) bool if exists { return true } + return idExists(l, int(purposeID), "flexiblePurposes") } @@ -106,7 +111,9 @@ func lazyParseInt(data []byte, key string) (int, bool) { if err != nil { return 0, false } + return intVal, true } + return 0, false } diff --git a/vendorlist2/shared_test.go b/vendorlist2/shared_test.go index 17ac9bc..685737f 100644 --- a/vendorlist2/shared_test.go +++ b/vendorlist2/shared_test.go @@ -188,6 +188,7 @@ const testDataSpecVersion3Empty = ` func assertIntsEqual(t *testing.T, expected, actual int) { t.Helper() + if actual != expected { t.Errorf("Ints were not equal. Expected %d, actual %d", expected, actual) } @@ -195,6 +196,7 @@ func assertIntsEqual(t *testing.T, expected, actual int) { func assertBoolsEqual(t *testing.T, expected, actual bool) { t.Helper() + if actual != expected { t.Errorf("Bools were not equal. Expected %t, actual %t", expected, actual) } @@ -202,9 +204,11 @@ func assertBoolsEqual(t *testing.T, expected, actual bool) { func assertNil(t *testing.T, value api.Vendor, expectNil bool) { t.Helper() + if expectNil && value != nil { t.Error("The vendor should be nil, but wasn't.") } + if !expectNil && value == nil { t.Errorf("The vendor should not be nil, but was.") } From 994e0287bad436498601a7f470e01d6f3086a077 Mon Sep 17 00:00:00 2001 From: Tiago Peczenyj Date: Tue, 22 Apr 2025 01:08:43 +0200 Subject: [PATCH 7/8] run golangci linter nlreturn with --fix --- vendorconsent/tcf2/metadata.go | 1 + vendorlist/eager-parsing.go | 4 ++++ vendorlist2/eager-parsing.go | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/vendorconsent/tcf2/metadata.go b/vendorconsent/tcf2/metadata.go index 1c31c6d..87cd822 100644 --- a/vendorconsent/tcf2/metadata.go +++ b/vendorconsent/tcf2/metadata.go @@ -30,6 +30,7 @@ func parseMetadata(data []byte) (ConsentMetadata, error) { if metadata.VendorListVersion() == 0 { metadata.data = nil + return metadata, errInvalidVendorListVersion } diff --git a/vendorlist/eager-parsing.go b/vendorlist/eager-parsing.go index 9c1e82e..cb7e863 100644 --- a/vendorlist/eager-parsing.go +++ b/vendorlist/eager-parsing.go @@ -95,11 +95,13 @@ type parsedVendor struct { func (l parsedVendor) Purpose(purposeID consentconstants.Purpose) (hasPurpose bool) { _, hasPurpose = l.purposeIDs[purposeID] + return } func (l parsedVendor) PurposeStrict(purposeID consentconstants.Purpose) (hasPurpose bool) { _, hasPurpose = l.purposeIDs[purposeID] + return } @@ -109,11 +111,13 @@ func (l parsedVendor) PurposeStrict(purposeID consentconstants.Purpose) (hasPurp // For an explanation of legitimate interest, see https://www.gdpreu.org/the-regulation/key-concepts/legitimate-interest/ func (l parsedVendor) LegitimateInterest(purposeID consentconstants.Purpose) (hasLegitimateInterest bool) { _, hasLegitimateInterest = l.legitimateInterestIDs[purposeID] + return } func (l parsedVendor) LegitimateInterestStrict(purposeID consentconstants.Purpose) (hasLegitimateInterest bool) { _, hasLegitimateInterest = l.legitimateInterestIDs[purposeID] + return } diff --git a/vendorlist2/eager-parsing.go b/vendorlist2/eager-parsing.go index 93bc250..2a54a3b 100644 --- a/vendorlist2/eager-parsing.go +++ b/vendorlist2/eager-parsing.go @@ -118,6 +118,7 @@ func (l parsedVendor) Purpose(purposeID consentconstants.Purpose) (hasPurpose bo // PurposeStrict checks only for the primary purpose, no considering flex purposes. func (l parsedVendor) PurposeStrict(purposeID consentconstants.Purpose) (hasPurpose bool) { _, hasPurpose = l.purposes[purposeID] + return } @@ -137,18 +138,21 @@ func (l parsedVendor) LegitimateInterest(purposeID consentconstants.Purpose) (ha // LegitimateInterestStrict checks only for the primary legitimate, no considering flex purposes. func (l parsedVendor) LegitimateInterestStrict(purposeID consentconstants.Purpose) (hasLegitimateInterest bool) { _, hasLegitimateInterest = l.legitimateInterests[purposeID] + return } // SpecialPurpose returns true if this vendor claims a need for the given special purpose func (l parsedVendor) SpecialPurpose(purposeID consentconstants.Purpose) (hasSpecialPurpose bool) { _, hasSpecialPurpose = l.specialPurposes[purposeID] + return } // SpecialFeature returns true if this vendor claims a need for the given special feature func (l parsedVendor) SpecialFeature(featureID consentconstants.SpecialFeature) (hasSpecialFeature bool) { _, hasSpecialFeature = l.specialFeatures[featureID] + return } From 45168a0cff74902025f43a0de7fec80a73db937e Mon Sep 17 00:00:00 2001 From: Tiago Peczenyj Date: Tue, 22 Apr 2025 01:12:36 +0200 Subject: [PATCH 8/8] fix lint issue gocritic assignOp --- vendorconsent/tcf1/bitfield.go | 2 +- vendorconsent/tcf1/rangesection.go | 4 ++-- vendorconsent/tcf2/pubrestrict.go | 6 +++--- vendorconsent/tcf2/rangesection.go | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/vendorconsent/tcf1/bitfield.go b/vendorconsent/tcf1/bitfield.go index e224f23..1cb26fc 100644 --- a/vendorconsent/tcf1/bitfield.go +++ b/vendorconsent/tcf1/bitfield.go @@ -19,7 +19,7 @@ func parseBitField(data consentMetadata) (*consentBitField, error) { otherBytesRequired := (vendorBitsRequired - 3) / 8 if (vendorBitsRequired-3)%8 > 0 { - otherBytesRequired = otherBytesRequired + 1 + otherBytesRequired++ } dataLengthRequired := 22 + otherBytesRequired diff --git a/vendorconsent/tcf1/rangesection.go b/vendorconsent/tcf1/rangesection.go index 30a2f2b..f93f02e 100644 --- a/vendorconsent/tcf1/rangesection.go +++ b/vendorconsent/tcf1/rangesection.go @@ -25,7 +25,7 @@ func parseRangeSection(data consentMetadata) (*rangeSection, error) { return nil, err } - currentOffset = currentOffset + bitsConsumed + currentOffset += bitsConsumed } return &rangeSection{ @@ -119,7 +119,7 @@ func parseUInt16(data []byte, bitStartIndex uint) (uint16, error) { // Take the rightmost bits of the left byte, and the leftmost bits of the middle byte leftByte := (data[startByte] & (0xff >> bitStartOffset)) << bitStartOffset - leftByte = leftByte | (data[startByte+1] >> shiftComplement) + leftByte |= (data[startByte+1] >> shiftComplement) // Take the rightmost bits of the middle byte, and the leftmost bits of the right byte rightByte := data[startByte+2] & (0xff << shiftComplement) diff --git a/vendorconsent/tcf2/pubrestrict.go b/vendorconsent/tcf2/pubrestrict.go index 3333412..4cca6db 100644 --- a/vendorconsent/tcf2/pubrestrict.go +++ b/vendorconsent/tcf2/pubrestrict.go @@ -27,14 +27,14 @@ func parsePubRestriction(metadata ConsentMetadata, startbit uint) (*pubRestricti return nil, 0, fmt.Errorf("Error on parsing the publisher restriction purpose/type: %s", err.Error()) } - currentOffset = currentOffset + 8 + currentOffset += 8 numEntries, err := bitutils.ParseUInt12(data, currentOffset) if err != nil { return nil, 0, fmt.Errorf("Error on parsing the number of publisher restriction vendor ranges: %s", err.Error()) } - currentOffset = currentOffset + 12 + currentOffset += 12 vendors := make([]rangeConsent, numEntries) for i := range vendors { @@ -43,7 +43,7 @@ func parsePubRestriction(metadata ConsentMetadata, startbit uint) (*pubRestricti return nil, 0, err } - currentOffset = currentOffset + bitsConsumed + currentOffset += bitsConsumed } restrictions[restrictData] = pubRestriction{ diff --git a/vendorconsent/tcf2/rangesection.go b/vendorconsent/tcf2/rangesection.go index aa33628..7d6aace 100644 --- a/vendorconsent/tcf2/rangesection.go +++ b/vendorconsent/tcf2/rangesection.go @@ -29,7 +29,7 @@ func parseRangeSection(metadata ConsentMetadata, maxVendorID uint16, startbit ui return nil, 0, err } - currentOffset = currentOffset + bitsConsumed + currentOffset += bitsConsumed } return &rangeSection{