Skip to content

Commit

Permalink
Merge pull request #15 from Countly/salt-checksum
Browse files Browse the repository at this point in the history
Added salt
  • Loading branch information
turtledreams authored Jun 11, 2024
2 parents 040d8d2 + 0379319 commit 271f385
Show file tree
Hide file tree
Showing 7 changed files with 264 additions and 136 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## 24.4.0

! Minor breaking change ! For implementations using `salt` the browser compatability is tied to SubtleCrypto's `digest` method support

* Added the `salt` init config flag to add checksums to requests (for secure contexts only)
* Added support for Feedback Widgets' terms and conditions

## 23.12.6
Expand Down
88 changes: 88 additions & 0 deletions cypress/e2e/salt.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/* eslint-disable cypress/no-unnecessary-waiting */
/* eslint-disable require-jsdoc */
var Countly = require("../../Countly.js");
var Utils = require("../../modules/Utils.js");
// import * as Countly from "../../dist/countly_umd.js";
var hp = require("../support/helper.js");
const crypto = require('crypto');

function initMain(salt) {
Countly.init({
app_key: "YOUR_APP_KEY",
url: "https://your.domain.count.ly",
debug: true,
salt: salt
});
}
const salt = "salt";

/**
* Tests for salt consists of:
* 1. Init without salt
* Create events and intercept the SDK requests. Request params should be normal and there should be no checksum
* 2. Init with salt
* Create events and intercept the SDK requests. Request params should be normal and there should be a checksum with length 64
* 3. Node and Web Crypto comparison
* Compare the checksums calculated by node crypto api and SDK's web crypto api for the same data. Should be equal
*/
describe("Salt Tests", () => {
it("Init without salt", () => {
hp.haltAndClearStorage(() => {
initMain(null);
var rqArray = [];
hp.events();
cy.intercept("GET", "**/i?**", (req) => {
const { url } = req;
rqArray.push(url.split("?")[1]); // get the query string
});
cy.wait(1000).then(() => {
cy.log(rqArray).then(() => {
for (const rq of rqArray) {
const paramsObject = hp.turnSearchStringToObject(rq);
hp.check_commons(paramsObject);
expect(paramsObject.checksum256).to.be.not.ok;
}
});
});
});
});
it("Init with salt", () => {
hp.haltAndClearStorage(() => {
initMain(salt);
var rqArray = [];
hp.events();
cy.intercept("GET", "**/i?**", (req) => {
const { url } = req;
rqArray.push(url.split("?")[1]);
});
cy.wait(1000).then(() => {
cy.log(rqArray).then(() => {
for (const rq of rqArray) {
const paramsObject = hp.turnSearchStringToObject(rq);
hp.check_commons(paramsObject);
expect(paramsObject.checksum256).to.be.ok;
expect(paramsObject.checksum256.length).to.equal(64);
// TODO: directly check the checksum with the node crypto api. Will need some extra decoding logic
}
});
});
});
});
it('Node and Web Crypto comparison', () => {
const hash = sha256("text" + salt); // node crypto api
Utils.calculateChecksum("text", salt).then((hash2) => { // SDK uses web crypto api
expect(hash2).to.equal(hash);
});
});
});

/**
* Calculate sha256 hash of given data
* @param {*} data - data to hash
* @returns {string} - sha256 hash
*/
function sha256(data) {
const hash = crypto.createHash('sha256');
hash.update(data);
return hash.digest('hex');
}
43 changes: 1 addition & 42 deletions cypress/e2e/web_worker_requests.cy.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { appKey } from "../support/helper";
import { turnSearchStringToObject, check_commons } from "../support/helper";

const myEvent = {
key: "buttonClick",
Expand Down Expand Up @@ -83,44 +83,3 @@ describe("Web Worker Request Intercepting Tests", () => {
});
});
});

/**
* Check common params for all requests
* @param {Object} paramsObject - object from search string
*/
function check_commons(paramsObject) {
expect(paramsObject.timestamp).to.be.ok;
expect(paramsObject.timestamp.toString().length).to.equal(13);
expect(paramsObject.hour).to.be.within(0, 23);
expect(paramsObject.dow).to.be.within(0, 7);
expect(paramsObject.app_key).to.equal(appKey);
expect(paramsObject.device_id).to.be.ok;
expect(paramsObject.sdk_name).to.equal("javascript_native_web");
expect(paramsObject.sdk_version).to.be.ok;
expect(paramsObject.t).to.be.within(0, 3);
expect(paramsObject.av).to.equal(0); // av is 0 as we parsed parsable things
if (!paramsObject.hc) { // hc is direct request
expect(paramsObject.rr).to.be.above(-1);
}
expect(paramsObject.metrics._ua).to.be.ok;
}

/**
* Turn search string into object with values parsed
* @param {String} searchString - search string
* @returns {object} - object from search string
*/
function turnSearchStringToObject(searchString) {
const searchParams = new URLSearchParams(searchString);
const paramsObject = {};
for (const [key, value] of searchParams.entries()) {
try {
paramsObject[key] = JSON.parse(value); // try to parse value
}
catch (e) {
paramsObject[key] = value;
}
}
return paramsObject;
}

44 changes: 43 additions & 1 deletion cypress/support/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,46 @@ function validateDefaultUtmTags(aq, source, medium, campaign, term, content) {
}
}

/**
* Check common params for all requests
* @param {Object} paramsObject - object from search string
*/
function check_commons(paramsObject) {
expect(paramsObject.timestamp).to.be.ok;
expect(paramsObject.timestamp.toString().length).to.equal(13);
expect(paramsObject.hour).to.be.within(0, 23);
expect(paramsObject.dow).to.be.within(0, 7);
expect(paramsObject.app_key).to.equal(appKey);
expect(paramsObject.device_id).to.be.ok;
expect(paramsObject.sdk_name).to.equal("javascript_native_web");
expect(paramsObject.sdk_version).to.be.ok;
expect(paramsObject.t).to.be.within(0, 3);
expect(paramsObject.av).to.equal(0); // av is 0 as we parsed parsable things
if (!paramsObject.hc) { // hc is direct request
expect(paramsObject.rr).to.be.above(-1);
}
expect(paramsObject.metrics._ua).to.be.ok;
}

/**
* Turn search string into object with values parsed
* @param {String} searchString - search string
* @returns {object} - object from search string
*/
function turnSearchStringToObject(searchString) {
const searchParams = new URLSearchParams(searchString);
const paramsObject = {};
for (const [key, value] of searchParams.entries()) {
try {
paramsObject[key] = JSON.parse(decodeURIComponent(value)); // try to parse value
}
catch (e) {
paramsObject[key] = decodeURIComponent(value);
}
}
return paramsObject;
}

module.exports = {
haltAndClearStorage,
sWait,
Expand All @@ -287,5 +327,7 @@ module.exports = {
testNormalFlow,
interceptAndCheckRequests,
validateDefaultUtmTags,
userDetailObj
userDetailObj,
check_commons,
turnSearchStringToObject
};
1 change: 0 additions & 1 deletion examples/style/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ body {
a {
text-decoration: none;
color: #000;
padding: 20px;
}

#header {
Expand Down
Loading

0 comments on commit 271f385

Please sign in to comment.