Skip to content

Commit

Permalink
changes for not tracking sensitive data
Browse files Browse the repository at this point in the history
  • Loading branch information
prabrishac committed Jul 5, 2020
1 parent 991ea79 commit fcd0af3
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 37 deletions.
40 changes: 31 additions & 9 deletions tests/html/autotrack-test.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,22 @@
{
logLevel: "DEBUG",
valTrackingList: [
"username",
"subject",
"user-password",
"googleClientId",
"country",
"radio"
]
"radio",
"user-pwd2",
"Description",
"user-pwd"
],
useAutoTracking: true
}
);
</script>

<script src="browser.js"></script>
<!-- <script>
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
posthog.init('8ng06D71u4W2CXnq9OcvtkVO0kn9lisgW3KTA2E85Vs', {api_host: 'http://localhost:8000'})
</script> -->
</head>
<body>
<div>
Expand All @@ -41,7 +45,7 @@

<form id="myForm">
Username:<br />
<input type="text" name="username" />
<input type="text" name="ssntest" />
<br />
Email id:<br />
<input type="text" name="email_id" />
Expand All @@ -54,9 +58,17 @@
class="rudder-no-track"
/>
<br />
<input type="radio" name="gender" id="male" />
<label for="user-password2">Password: </label><br />
<input
type="password"
name="user-pwd2"
id="user-password2"
class="rudder-include"
/>
<br />
<input type="radio" name="gender" id="male" class="rudder-include"/>
<label for="male">Male</label><br />
<input type="radio" name="gender" id="female" />
<input type="radio" name="gender" id="female" class="rudder-include"/>
<label for="female">Female</label>
<input type="checkbox" name="subject" id="maths" />
<label for="maths">Maths</label>
Expand All @@ -70,7 +82,16 @@
cols="50"
name="Description"
id="Description"
class="rudder-include"
></textarea>
<div> This is password</div>
<div> This should be tracked</div>
<div> This is 1111-2222-3333 - adhar number </div>
<div name="testssn"> This is bad!</div>
<div>
<label name="ssn"> This too is bad!</label>
</div>
<div ssn="testssn" key1="value1" key2="AAAAA1111Q"> This is good! key2 attribute will not be tracked!</div>
<label for="country">Country:</label>
<select id="country" id="country">
<option value="India">India</option>
Expand All @@ -82,6 +103,7 @@
name="googleClientId"
class="googleClientId"
value="my-gcid"
class="rudder-include"
/>
<input type="submit" value="Submit" />
</form>
Expand Down
148 changes: 120 additions & 28 deletions utils/autotrack.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,63 @@ function isTextNode(el) {
}

function shouldTrackElement(el) {

if (!el.parentNode || isTag(el, "body")) return false;

let curEl = el;
while (curEl.parentNode && !isTag(curEl, "body")) {
let classes = getClassName(el).split(" ");

// if explicitly specified "rudder-no-track", even at parent level, dont track the child nodes too.
if (classes.indexOf("rudder-no-track") >= 0) {
return false;
}
curEl = curEl.parentNode;
}

// if explicitly set "rudder-include", at element level, then track the element even if the element is hidden or sensitive.
let classes = getClassName(el).split(" ");
if (classes.indexOf("rudder-include") >= 0) {
return true;
}

// for general elements, do not track input/select/textarea(s)
if (
isTag(el, 'input') ||
isTag(el, 'select') ||
isTag(el, 'textarea') ||
el.getAttribute('contenteditable') === 'true'
) {
return false;
} else if (el.getAttribute('contenteditable') === 'inherit'){
for(curEl = el.parentNode; curEl.parentNode && !isTag(curEl, "body"); curEl = curEl.parentNode){
if(curEl.getAttribute('contenteditable') === 'true'){
return false;
}
}
}

// do not track hidden/password elements
let type = el.type || '';
if (typeof type === 'string') { // it's possible for el.type to be a DOM element if el is a form with a child input[name="type"]
switch(type.toLowerCase()) {
case 'hidden':
return false;
case 'password':
return false;
}
}

// filter out data from fields that look like sensitive field -
// safeguard - match with regex with possible strings as id or name of an element for creditcard, password, ssn, pan, adhar
let name = el.name || el.id || '';
if (typeof name === 'string') { // it's possible for el.name or el.id to be a DOM element if el is a form with a child input[name="name"]
let sensitiveNameRegex = /^adhar|cc|cardnum|ccnum|creditcard|csc|cvc|cvv|exp|pan|pass|pwd|routing|seccode|securitycode|securitynum|socialsec|socsec|ssn/i;
if (sensitiveNameRegex.test(name.replace(/[^a-zA-Z0-9]/g, ''))) {
return false;
}
}

return true;
}

Expand Down Expand Up @@ -98,8 +154,8 @@ function trackWindowEvent(e, rudderanalytics) {
for (let i = 0; i < target.elements.length; i++) {
const formElement = target.elements[i];
if (
isElToBeTracked(formElement) &&
isElValueToBeTracked(formElement, rudderanalytics.trackValues)
shouldTrackElement(formElement) &&
isValueToBeTrackedFromTrackingList(formElement, rudderanalytics.trackValues)
) {
const name = formElement.id ? formElement.id : formElement.name;
if (name && typeof name === "string") {
Expand All @@ -121,42 +177,38 @@ function trackWindowEvent(e, rudderanalytics) {
}
}
}
const targetElementList = [target];
const targetElementList = [];
let curEl = target;
if(isExplicitNoTrack(curEl)){
return false;
}
while (curEl.parentNode && !isTag(curEl, "body")) {
targetElementList.push(curEl.parentNode);
if(shouldTrackElement(curEl)){
targetElementList.push(curEl);
}
curEl = curEl.parentNode;
}

const elementsJson = [];
let href;
let explicitNoTrack = false;

targetElementList.forEach((el) => {
const shouldTrackEl = shouldTrackElement(el);

// if the element or a parent element is an anchor tag
// include the href as a property
if (el.tagName.toLowerCase() === "a") {
href = el.getAttribute("href");
href = shouldTrackEl && href;
href = isValueToBeTracked(href) && href;
}

// allow users to programatically prevent tracking of elements by adding class 'rudder-no-track'

explicitNoTrack = explicitNoTrack || !isElToBeTracked(el);

// explicitNoTrack = !isElToBeTracked(el);

elementsJson.push(getPropertiesFromElement(el, rudderanalytics));
});

if (explicitNoTrack) {
if (targetElementList && targetElementList.length == 0) {
return false;
}

let elementText = "";
const text = getText(target); // target.innerText//target.textContent//getSafeText(target);
const text = getText(target);
if (text && text.length) {
elementText = text;
}
Expand All @@ -178,7 +230,51 @@ function trackWindowEvent(e, rudderanalytics) {
}
}

function isElValueToBeTracked(el, includeList) {
function isExplicitNoTrack(el){
const classes = getClassName(el).split(" ");
if (classes.indexOf("rudder-no-track") >= 0) {
return true;
}
return false;
}

function isValueToBeTracked(value){
if(value === null || value === undefined){
return false;
}
if (typeof value === 'string') {
value = value.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');

// check to see if input value looks like a credit card number
// see: https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9781449327453/ch04s20.html
var ccRegex = /^(?:(4[0-9]{12}(?:[0-9]{3})?)|(5[1-5][0-9]{14})|(6(?:011|5[0-9]{2})[0-9]{12})|(3[47][0-9]{13})|(3(?:0[0-5]|[68][0-9])[0-9]{11})|((?:2131|1800|35[0-9]{3})[0-9]{11}))$/;
if (ccRegex.test((value || '').replace(/[- ]/g, ''))) {
return false;
}

// check to see if input value looks like a social security number
var ssnRegex = /(^\d{3}-?\d{2}-?\d{4}$)/;
if (ssnRegex.test(value)) {
return false;
}

// check to see if input value looks like a adhar number
var adharRegex = /(^\d{4}-?\d{4}-?\d{4}$)/;
if (adharRegex.test(value)) {
return false;
}

// check to see if input value looks like a PAN number
var panRegex = /(^\w{5}-?\d{4}-?\w{1}$)/;
if (panRegex.test(value)) {
return false;
}
}

return true;
}

function isValueToBeTrackedFromTrackingList(el, includeList) {
const elAttributesLength = el.attributes.length;
for (let i = 0; i < elAttributesLength; i++) {
const { value } = el.attributes[i];
Expand All @@ -189,19 +285,15 @@ function isElValueToBeTracked(el, includeList) {
return false;
}

function isElToBeTracked(el) {
const classes = getClassName(el).split(" ");
if (classes.indexOf("rudder-no-track") >= 0) {
return false;
}
return true;
}

function getText(el) {
let text = "";
el.childNodes.forEach(function (value) {
if (value.nodeType === Node.TEXT_NODE) {
text += value.nodeValue;
let textContent = value.nodeValue.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');

// take each word from the text content and check whether the value should be tracked. Also, replace the whitespaces.
let textValue = textContent.split(/(\s+)/).filter(isValueToBeTracked).join('').replace(/[\r\n]/g, ' ');
text += textValue;
}
});
return text.trim();
Expand All @@ -217,12 +309,12 @@ function getPropertiesFromElement(elem, rudderanalytics) {
for (let i = 0; i < attrLength; i++) {
const { name } = elem.attributes[i];
const { value } = elem.attributes[i];
if (value) {
if (value && isValueToBeTracked(value)) {
props[`attr__${name}`] = value;
}
if (
(name == "name" || name == "id") &&
isElValueToBeTracked(elem, rudderanalytics.trackValues)
isValueToBeTrackedFromTrackingList(elem, rudderanalytics.trackValues)
) {
props.field_value =
name == "id"
Expand Down

0 comments on commit fcd0af3

Please sign in to comment.