Skip to content

Commit

Permalink
Merge pull request #448 from alphagov/provide-pii-safe-wrapper-object…
Browse files Browse the repository at this point in the history
…-to-tell-analytics-not-to-try-stripping-pii-from-this-argument

Provide `PIISafe` wrapper object
  • Loading branch information
h-lame committed Feb 14, 2018
2 parents 9cd0ae9 + b1ce5b4 commit 5354664
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 5 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Unreleased

- Allow wrapping arguments to analytics as PII safe to tell the analytics code not to attempt to strip PII from the values: ([PR #448](https://github.com/alphagov/govuk_frontend_toolkit/pull/448))
# 7.3.0

- Strip PII from all arguments passed to GA. Emails are stripped by default, postcodes can also be stripped if configured to do so: ([PR #435](https://github.com/alphagov/govuk_frontend_toolkit/pull/435)).
Expand Down
19 changes: 19 additions & 0 deletions docs/analytics.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,3 +249,22 @@ initialize time as follows:

Any value other than the JS literal `true` for `stripPostcodePII` will leave
the analytics module configured not to strip postcodes.

#### Avoding false positives

Sometimes you will have data you want to send to analytics that looks like PII
and would be stripped out. For example on GOV.UK the content_ids that belong
to every document can sometimes contain a string of characters that look like a
UK postcode: in `eed5b92e-8279-4ca9-a141-5c35ed22fcf1` the substring `c35ed` in
the final portion looks like a postcode, `C3 5ED`, and will be transformed into
`eed5b92e-8279-4ca9-a141-5[postcode]22fcf1` which breaks the `content_id`. To
send data that you know is not PII, but it looks like an email address or a UK
postcode you can provide your arguments wrapped in a `GOVUK.Analytics.PIISafe`
object. If any argument to an analytics function is an instance of one of these
objects the value contained within will be extracted and sent directly to the
analytics tracker without attempting to strip PII from it. For example:

```js
GOVUK.analytics.setDimension(1, new GOVUK.Analytics.PIISafe('this-is-not-an@email-address-but-it-looks-like-one'));
GOVUK.analytics.trackEvent('report title clicked', new GOVUK.Analytics.PIISafe('this report title looks like it contains a P0 5TC ode but it does not really'));
````
19 changes: 14 additions & 5 deletions javascripts/govuk/analytics/analytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@
}
}

var PIISafe = function (value) {
this.value = value
}
Analytics.PIISafe = PIISafe

Analytics.prototype.stripPII = function (value) {
if (typeof value === 'string') {
return this.stripPIIFromString(value)
} else if (Object.prototype.toString.call(value) === '[object Array]') {
} else if (Object.prototype.toString.call(value) === '[object Array]' || Object.prototype.toString.call(value) === '[object Arguments]') {
return this.stripPIIFromArray(value)
} else if (typeof value === 'object') {
return this.stripPIIFromObject(value)
Expand All @@ -51,12 +56,16 @@
}

Analytics.prototype.stripPIIFromObject = function (object) {
for (var property in object) {
var value = object[property]
if (object instanceof Analytics.PIISafe) {
return object.value
} else {
for (var property in object) {
var value = object[property]

object[property] = this.stripPII(value)
object[property] = this.stripPII(value)
}
return object
}
return object
}

Analytics.prototype.stripPIIFromArray = function (array) {
Expand Down
43 changes: 43 additions & 0 deletions spec/unit/analytics/analytics.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,24 @@ describe('GOVUK.Analytics', function () {
analytics.setDimension(1, 'SW1+1AA-value', { label: 'RG209NJ', value: ['data', 'data', 'someone has added their personalIV63 6TU postcode'] })
expect(window.ga.calls.mostRecent().args).toEqual(['set', 'dimension1', '[postcode]-value']) // set dimension ignores extra options
})

it('ignores any PIISafe arguments even if they look like emails or postcodes', function () {
analytics = new GOVUK.Analytics({
universalId: 'universal-id',
cookieDomain: '.www.gov.uk',
siteSpeedSampleRate: 100,
stripPostcodePII: true
})

analytics.trackPageview(new GOVUK.Analytics.PIISafe('/path/to/an/embedded/SW1+1AA/postcode/?with=an&postcode=SP4%207DE'), new GOVUK.Analytics.PIISafe('an.email@example.com'), { label: new GOVUK.Analytics.PIISafe('another.email@example.com'), value: ['data', 'data', new GOVUK.Analytics.PIISafe('someone has added their personalIV63 6TU postcode')] })
expect(window.ga.calls.mostRecent().args).toEqual(['send', 'pageview', { page: '/path/to/an/embedded/SW1+1AA/postcode/?with=an&postcode=SP4%207DE', title: 'an.email@example.com', label: 'another.email@example.com', value: ['data', 'data', 'someone has added their personalIV63 6TU postcode'] }])

analytics.trackEvent(new GOVUK.Analytics.PIISafe('SW1+1AA-category'), new GOVUK.Analytics.PIISafe('an.email@example.com-action'), { label: new GOVUK.Analytics.PIISafe('RG209NJ'), value: ['data', 'data', 'someone has added their personalIV63 6TU postcode'] })
expect(window.ga.calls.mostRecent().args).toEqual(['send', { hitType: 'event', eventCategory: 'SW1+1AA-category', eventAction: 'an.email@example.com-action', eventLabel: 'RG209NJ' }]) // trackEvent ignores options other than label or integer values for value

analytics.setDimension(1, new GOVUK.Analytics.PIISafe('an.email@SW1+1AA-value.com'), { label: new GOVUK.Analytics.PIISafe('RG209NJ'), value: ['data', 'data', new GOVUK.Analytics.PIISafe('someone has added their personalIV63 6TU postcode')] })
expect(window.ga.calls.mostRecent().args).toEqual(['set', 'dimension1', 'an.email@SW1+1AA-value.com']) // set dimension ignores extra options
})
})

describe('when tracking social media shares', function () {
Expand Down Expand Up @@ -224,6 +242,31 @@ describe('GOVUK.Analytics', function () {
value: ['data', 'data', 'someone has added their personal[postcode] postcode']
}])
})

it('ignores any PIISafe arguments even if they look like emails or postcodes', function () {
analytics = new GOVUK.Analytics({
universalId: 'universal-id',
cookieDomain: '.www.gov.uk',
siteSpeedSampleRate: 100,
stripPostcodePII: true
})

analytics.trackShare('email', {
to: new GOVUK.Analytics.PIISafe('IV63 6TU'),
label: new GOVUK.Analytics.PIISafe('an.email@example.com'),
value: new GOVUK.Analytics.PIISafe(['data', 'another.email@example.com', 'someone has added their personalTD15 2SE postcode'])
})

expect(window.ga.calls.mostRecent().args).toEqual(['send', {
hitType: 'social',
socialNetwork: 'email',
socialAction: 'share',
socialTarget: jasmine.any(String),
to: 'IV63 6TU',
label: 'an.email@example.com',
value: ['data', 'another.email@example.com', 'someone has added their personalTD15 2SE postcode']
}])
})
})

describe('when adding a linked domain', function () {
Expand Down

0 comments on commit 5354664

Please sign in to comment.