diff --git a/Changes b/Changes index b37d15c..ccfafaf 100644 --- a/Changes +++ b/Changes @@ -1,3 +1,5 @@ + - add strict mode (disabled by default) to validate the consent string version + 0.084 - fix a bug while parsing valid consent strings range-based, see https://github.com/peczenyj/GDPR-IAB-TCFv2/issues/20 diff --git a/README.pod b/README.pod index 0b64e1a..40f425b 100644 --- a/README.pod +++ b/README.pod @@ -113,9 +113,10 @@ or boolean_values => [ 0, 1 ], date_format => '%Y%m%d', # yyymmdd }, + strict => 1, ); -Parse may receive an optional hash parameter C with the following properties: +Parse may receive an optional hash of parameters: C (boolean) and C (hashref with the following properties): =over @@ -149,6 +150,10 @@ except if the option C is true. =back +On C mode we will validate if the version of the consent string is the version 2 (or die with an exception). + +The C mode is disabled by default. + =head1 METHODS =head2 tc_string diff --git a/lib/GDPR/IAB/TCFv2.pm b/lib/GDPR/IAB/TCFv2.pm index 4b6b464..836e9f6 100644 --- a/lib/GDPR/IAB/TCFv2.pm +++ b/lib/GDPR/IAB/TCFv2.pm @@ -26,7 +26,7 @@ our $VERSION = "0.084"; use constant { CONSENT_STRING_TCF2_SEPARATOR => '.', CONSENT_STRING_TCF2_PREFIX => 'C', - MIN_BYTE_SIZE => 29, + TCF_V2_MIN_BYTE_SIZE => 29, TCF_VERSION => 2, ASSUMED_MAX_VENDOR_ID => 0x7FFF, # 32767 or (1 << 15) -1 MAX_SPECIAL_FEATURE_ID => 12, @@ -90,14 +90,13 @@ sub Parse { my $core_tc_string = _get_core_tc_string($tc_string); - my $data = unpack 'B*', _validate_and_decode_base64($core_tc_string); - my $data_size = length($data); + my ( $data, $data_size ) = _validate_and_decode_base64($core_tc_string); - croak "vendor consent strings are at least @{[ MIN_BYTE_SIZE ]} bytes long" - if $data_size < 8 * MIN_BYTE_SIZE; + my $strict = !!$opts{strict}; my %options = ( - json => $opts{json} || {}, + json => $opts{json} || {}, + strict => $strict, ); $options{json}->{date_format} ||= DATE_FORMAT_ISO_8601; @@ -116,7 +115,7 @@ sub Parse { bless $self, $klass; croak "consent string is not tcf version @{[ TCF_VERSION ]}" - unless $self->version == TCF_VERSION; + if $strict && $self->version != TCF_VERSION; croak 'invalid vendor list version' if $self->vendor_list_version == 0; @@ -534,7 +533,14 @@ sub _validate_and_decode_base64 { \z }x; - return _decode_base64url($s); + my $data = unpack 'B*', _decode_base64url($s); + my $data_size = length($data); + + croak + "vendor consent strings are at least @{[ TCF_V2_MIN_BYTE_SIZE ]} bytes long" + if $data_size < 8 * TCF_V2_MIN_BYTE_SIZE; + + return ( $data, $data_size ); } sub _decode_base64url { @@ -714,9 +720,10 @@ or boolean_values => [ 0, 1 ], date_format => '%Y%m%d', # yyymmdd }, + strict => 1, ); -Parse may receive an optional hash parameter C with the following properties: +Parse may receive an optional hash of parameters: C (boolean) and C (hashref with the following properties): =over @@ -750,6 +757,10 @@ except if the option C is true. =back +On C mode we will validate if the version of the consent string is the version 2 (or die with an exception). + +The C mode is disabled by default. + =head1 METHODS =head2 tc_string diff --git a/t/01-parse.t b/t/01-parse.t index 8f392b1..f4bd902 100644 --- a/t/01-parse.t +++ b/t/01-parse.t @@ -389,12 +389,21 @@ subtest "invalid tcf consent string candidates" => sub { } qr/missing gdpr consent string/, 'empty consent string should throw error'; + lives_ok { + my $consent = GDPR::IAB::TCFv2->Parse( + "BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASAAAAAAAAAA"); + + is $consent->version, 1, "tcf v1"; + + } + 'valid tcf v1 consent string should now throw error without strict flag'; + throws_ok { GDPR::IAB::TCFv2->Parse( - "BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASAAAAAAAAAA"); + "BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASAAAAAAAAAA", strict => 1 ); } qr/consent string is not tcf version 2/, - 'valid tcf v1 consent string should throw error (deprecated)'; + 'valid tcf v1 consent string should throw error with strict flag'; throws_ok { GDPR::IAB::TCFv2->Parse("Clc"); @@ -402,12 +411,19 @@ subtest "invalid tcf consent string candidates" => sub { qr/vendor consent strings are at least 29 bytes long/, 'short (less than 29 bytes) tcf v2 consent string should throw error'; + lives_ok { + my $consent = GDPR::IAB::TCFv2->Parse( + "DOEFEAyOEFEAyAHABDENAI4AAAB9vABAASAAAAAAAAAA", strict => 0 ); + is $consent->version, 3, "tcf v3"; + } + 'possible tcf v3 consent string should not throw error without strict flag'; + throws_ok { GDPR::IAB::TCFv2->Parse( - "DOEFEAyOEFEAyAHABDENAI4AAAB9vABAASAAAAAAAAAA"); + "DOEFEAyOEFEAyAHABDENAI4AAAB9vABAASAAAAAAAAAA", strict => 1 ); } qr/consent string is not tcf version 2/, - 'possible tcf v3 consent string should throw error'; + 'possible tcf v3 consent string should throw error with strict flag'; throws_ok { GDPR::IAB::TCFv2->Parse(