Skip to content

Commit

Permalink
Aggressively mask input attributes & fix visualization issues
Browse files Browse the repository at this point in the history
Aggressively mask input attributes
  • Loading branch information
sarveshnagpal committed May 21, 2020
2 parents 6b85c5f + 1fca1e0 commit 43e6326
Show file tree
Hide file tree
Showing 20 changed files with 76 additions and 41 deletions.
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"packages": [
"packages/*"
],
"version": "0.5.6",
"version": "0.5.7",
"npmClient": "yarn",
"useWorkspaces": true
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "clarity",
"private": true,
"version": "0.4.2",
"version": "0.5.7",
"repository": "https://github.com/microsoft/clarity.git",
"author": "Sarvesh Nagpal <sarveshn@microsoft.com>",
"license": "MIT",
Expand Down
4 changes: 2 additions & 2 deletions packages/clarity-decode/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "clarity-decode",
"version": "0.5.6",
"version": "0.5.7",
"description": "An analytics library that uses web page interactions to generate aggregated insights",
"author": "Microsoft Corp.",
"license": "MIT",
Expand All @@ -26,7 +26,7 @@
"url": "https://github.com/Microsoft/clarity/issues"
},
"dependencies": {
"clarity-js": "^0.5.6"
"clarity-js": "^0.5.7"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^11.1.0",
Expand Down
8 changes: 4 additions & 4 deletions packages/clarity-devtools/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "clarity-devtools",
"version": "0.5.6",
"version": "0.5.7",
"private": true,
"description": "Adds Clarity debugging support to browser devtools",
"author": "Microsoft Corp.",
Expand All @@ -24,9 +24,9 @@
"url": "https://github.com/Microsoft/clarity/issues"
},
"dependencies": {
"clarity-decode": "^0.5.6",
"clarity-js": "^0.5.6",
"clarity-visualize": "^0.5.6"
"clarity-decode": "^0.5.7",
"clarity-js": "^0.5.7",
"clarity-visualize": "^0.5.7"
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^7.1.3",
Expand Down
4 changes: 2 additions & 2 deletions packages/clarity-devtools/static/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"manifest_version": 2,
"name": "Clarity Developer Tools",
"description": "Get insights about how customers use your website.",
"version": "0.5.6",
"version_name": "0.5.6",
"version": "0.5.7",
"version_name": "0.5.7",
"minimum_chrome_version": "50",
"devtools_page": "devtools.html",
"icons": {
Expand Down
2 changes: 1 addition & 1 deletion packages/clarity-js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "clarity-js",
"version": "0.5.6",
"version": "0.5.7",
"description": "An analytics library that uses web page interactions to generate aggregated insights",
"author": "Microsoft Corp.",
"license": "MIT",
Expand Down
5 changes: 5 additions & 0 deletions packages/clarity-js/src/clarity.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Config } from "@clarity-types/core";
import { Metadata } from "@clarity-types/data";
import * as core from "@src/core";
import configuration from "@src/core/config";
import version from "@src/core/version";
Expand Down Expand Up @@ -115,3 +116,7 @@ export function consent(): void {
measure(data.consent)();
}
}

export function metadata(): Metadata {
return active ? data.metadata() : null;
}
2 changes: 1 addition & 1 deletion packages/clarity-js/src/core/version.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
let version = "0.5.6";
let version = "0.5.7";
export default version;
14 changes: 7 additions & 7 deletions packages/clarity-js/src/data/encode.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Event, Token } from "@clarity-types/data";
import { time } from "@src/core/time";
import { metadata } from "@src/data/metadata";
import * as metadata from "@src/data/metadata";
import * as metric from "@src/data/metric";
import * as ping from "@src/data/ping";
import * as tag from "@src/data/tag";
Expand All @@ -17,12 +17,12 @@ export default function(event: Event): void {
queue(tokens);
break;
case Event.Page:
tokens.push(metadata.page.timestamp);
tokens.push(metadata.page.ua);
tokens.push(metadata.page.url);
tokens.push(metadata.page.referrer);
tokens.push(metadata.page.lean);
tokens.push(metadata.page.title);
tokens.push(metadata.state.page.timestamp);
tokens.push(metadata.state.page.ua);
tokens.push(metadata.state.page.url);
tokens.push(metadata.state.page.referrer);
tokens.push(metadata.state.page.lean);
tokens.push(metadata.state.page.title);
queue(tokens);
break;
case Event.Tag:
Expand Down
2 changes: 1 addition & 1 deletion packages/clarity-js/src/data/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as tag from "@src/data/tag";
import * as target from "@src/data/target";
import * as upgrade from "@src/data/upgrade";
import * as upload from "@src/data/upload";
export { consent } from "@src/data/metadata";
export { consent, metadata } from "@src/data/metadata";
export { tag } from "@src/data/tag";
export { upgrade } from "@src/data/upgrade";

Expand Down
28 changes: 21 additions & 7 deletions packages/clarity-js/src/data/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { BooleanFlag, Envelope, Event, Metadata, PageData, Token, Upload } from "@clarity-types/data";
import { BooleanFlag, Envelope, Event, Metadata, PageState, PageData, Token, Upload } from "@clarity-types/data";
import config from "@src/core/config";
import version from "@src/core/version";
import encode from "@src/data/encode";
import hash from "@src/data/hash";

const CLARITY_STORAGE_KEY: string = "_clarity";
const CLARITY_STORAGE_SEPARATOR: string = "|";
export let metadata: Metadata = null;
export let state: PageState = null;

export function start(): void {
let ts = Math.round(Date.now()); // ensuring that the output of Date.now() is an integer
Expand All @@ -21,18 +21,32 @@ export function start(): void {
let e: Envelope = { sequence: 0, version, pageId, userId, sessionId, projectId, upload, end: BooleanFlag.False };
let p: PageData = { timestamp: ts, ua, url: location.href, referrer: document.referrer, lean, title };

metadata = { page: p, envelope: e };
state = { page: p, envelope: e };
track();
encode(Event.Page);
if (config.onstart) { config.onstart({ userId: e.userId, sessionId: e.sessionId, pageId: e.pageId}); }

// For backward compatibility (starting 0.5.7)
// This configuration option "onstart" will be removed in subsequent versions
// And, is replaced by clarity.metadata() call.
if (config.onstart) { config.onstart({ projectId, userId, sessionId, pageId}); }
}

export function end(): void {
metadata = null;
state = null;
}

export function metadata(): Metadata {
let e = state ? state.envelope : null;
return e ? {
projectId: e.projectId,
userId: e.userId,
sessionId: e.sessionId,
pageId: e.pageId
} : null;
}

export function envelope(last: boolean): Token[] {
let e = metadata.envelope;
let e = state.envelope;
e.upload = last && "sendBeacon" in navigator ? Upload.Beacon : Upload.Async;
e.end = last ? BooleanFlag.True : BooleanFlag.False;
e.sequence++;
Expand All @@ -50,7 +64,7 @@ function track(): void {
let expiry = new Date();
expiry.setDate(expiry.getDate() + config.expire);
let expires = expiry ? "expires=" + expiry.toUTCString() : "";
let value = metadata.envelope.userId + ";" + expires + ";path=/";
let value = state.envelope.userId + ";" + expires + ";path=/";
document.cookie = CLARITY_STORAGE_KEY + "=" + value;
}
}
Expand Down
6 changes: 3 additions & 3 deletions packages/clarity-js/src/data/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import measure from "@src/core/measure";
import { time } from "@src/core/time";
import { clearTimeout, setTimeout } from "@src/core/timeout";
import encode from "@src/data/encode";
import { envelope, metadata } from "@src/data/metadata";
import * as metadata from "@src/data/metadata";
import * as metric from "@src/data/metric";
import * as ping from "@src/data/ping";
import * as target from "@src/data/target";
Expand Down Expand Up @@ -144,9 +144,9 @@ function upload(final: boolean = false): void {
// could inject function arguments for internal tracking (likely stack traces for script errors).
// For these edge cases, we want to ensure that an injected object (e.g. {"key": "value"}) isn't mistaken to be true.
let last = final === true;
let payload: EncodedPayload = {e: JSON.stringify(envelope(last)), d: `[${events.join()}]`};
let payload: EncodedPayload = {e: JSON.stringify(metadata.envelope(last)), d: `[${events.join()}]`};
let data = stringify(payload);
let sequence = metadata.envelope.sequence;
let sequence = metadata.state.envelope.sequence;
metric.count(Metric.TotalBytes, data.length);
send(data, sequence, last);

Expand Down
10 changes: 6 additions & 4 deletions packages/clarity-js/src/layout/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,12 @@ function mask(data: NodeInfo, masked: boolean): boolean {
if (attributes === null || attributes === undefined) { return masked; }

// Check for blacklist fields (e.g. address, phone, etc.) only if the input node is not already masked
if (masked === false && tag === Constant.INPUT_TAG && Constant.NAME_ATTRIBUTE in attributes) {
let value = attributes[Constant.NAME_ATTRIBUTE].toLowerCase();
if (masked === false && tag === Constant.INPUT_TAG) {
let field: string = Constant.EMPTY_STRING;
// Be aggressive in looking up any attribute (id, class, name, etc.) for disallowed names
for (const attribute of Object.keys(attributes)) { field += attribute.toLowerCase(); }
for (let name of DISALLOWED_NAMES) {
if (value.indexOf(name) >= 0) {
if (field.indexOf(name) >= 0) {
masked = true;
continue;
}
Expand All @@ -214,7 +216,7 @@ function mask(data: NodeInfo, masked: boolean): boolean {
// Check for blacklist types (e.g. password, email, etc.) and set the masked property appropriately
if (Constant.TYPE_ATTRIBUTE in attributes && DISALLOWED_TYPES.indexOf(attributes[Constant.TYPE_ATTRIBUTE]) >= 0) { masked = true; }

// Following two conditions superseede any of the above. If there are explicit instructions to mask / unmask a field, we honor that.
// Following two conditions supersede any of the above. If there are explicit instructions to mask / unmask a field, we honor that.
if (Constant.MASK_ATTRIBUTE in attributes) { masked = true; }
if (Constant.UNMASK_ATTRIBUTE in attributes) { masked = false; }

Expand Down
4 changes: 2 additions & 2 deletions packages/clarity-js/types/core.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ClarityInfo, Payload, Token } from "./data";
import { Metadata, Payload, Token } from "./data";

type TaskFunction = () => Promise<void>;
type TaskResolve = () => void;
Expand Down Expand Up @@ -76,6 +76,6 @@ export interface Config {
track?: boolean;
regions?: RegionTracker;
url?: string;
onstart?: (data: ClarityInfo) => void;
onstart?: (data: Metadata) => void;
upload?: (data: string, sequence?: number, last?: boolean) => void;
}
5 changes: 3 additions & 2 deletions packages/clarity-js/types/data.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ export interface EncodedPayload {
d: string;
}

export interface ClarityInfo {
export interface Metadata {
projectId: string;
userId: string;
sessionId: string;
pageId: string;
Expand All @@ -117,7 +118,7 @@ export interface TargetInfo {
node: Node;
}

export interface Metadata {
export interface PageState {
page: PageData;
envelope: Envelope;
}
Expand Down
1 change: 1 addition & 0 deletions packages/clarity-js/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface Clarity {
upgrade: (key: string) => void;
consent: () => void;
tag: (key: string, value: string) => void;
metadata: () => Data.Metadata;
}

interface Helper {
Expand Down
1 change: 1 addition & 0 deletions packages/clarity-js/types/layout.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const enum Constant {
FUNCTION = "function",
INPUT_TAG = "INPUT",
IFRAME_TAG = "IFRAME",
BASE_TAG = "BASE",
NATIVE_CODE = "[native code]",
DOCUMENT_TAG = "*D",
SHADOW_DOM_TAG = "*S",
Expand Down
4 changes: 2 additions & 2 deletions packages/clarity-visualize/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "clarity-visualize",
"version": "0.5.6",
"version": "0.5.7",
"description": "An analytics library that uses web page interactions to generate aggregated insights",
"author": "Microsoft Corp.",
"license": "MIT",
Expand All @@ -27,7 +27,7 @@
"url": "https://github.com/Microsoft/clarity/issues"
},
"dependencies": {
"clarity-decode": "^0.5.6"
"clarity-decode": "^0.5.7"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^11.1.0",
Expand Down
2 changes: 2 additions & 0 deletions packages/clarity-visualize/rollup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default [
],
onwarn(message, warn) {
if (message.code === 'NON_EXISTENT_EXPORT') { return; }
if (message.code === 'CIRCULAR_DEPENDENCY') { return; }
warn(message);
}
},
Expand All @@ -32,6 +33,7 @@ export default [
],
onwarn(message, warn) {
if (message.code === 'NON_EXISTENT_EXPORT') { return; }
if (message.code === 'CIRCULAR_DEPENDENCY') { return; }
warn(message);
}
}
Expand Down
11 changes: 10 additions & 1 deletion packages/clarity-visualize/src/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,19 @@ function createElement(doc: Document, tag: string): HTMLElement {

function insertAfter(data: Layout.DomData, parent: Node, node: Node, previous: Node): void {
let next = previous && previous.parentElement === parent ? previous.nextSibling : null;
next = previous === null && parent ? parent.firstChild : next;
next = previous === null && parent ? firstChild(parent) : next;
insertBefore(data, parent, node, next);
}

function firstChild(node: Node): ChildNode {
let child = node.firstChild;
// BASE tag should always be the first child to ensure resources with relative URLs are loaded correctly
if (child && child.nodeType === Node.ELEMENT_NODE && (child as HTMLElement).tagName === Layout.Constant.BASE_TAG) {
return child.nextSibling;
}
return child;
}

function insertBefore(data: Layout.DomData, parent: Node, node: Node, next: Node): void {
if (parent !== null) {
next = next && next.parentElement !== parent ? null : next;
Expand Down

0 comments on commit 43e6326

Please sign in to comment.