Skip to content

Commit

Permalink
Merge pull request #488 from Countly/saltsum
Browse files Browse the repository at this point in the history
Added salt
  • Loading branch information
turtledreams committed Apr 23, 2024
2 parents 01c7900 + 73402b4 commit 72f19c0
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 90 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
## 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)
- Improved Health Check feature stability
- Added support for Feedback Widget terms and conditions

Expand Down
2 changes: 1 addition & 1 deletion cypress/integration/bridge_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function initMain(name, version) {
}

const SDK_NAME = "javascript_native_web";
const SDK_VERSION = "23.12.6";
const SDK_VERSION = "24.4.0";

// tests
describe("Bridged SDK Utilities Tests", () => {
Expand Down
87 changes: 87 additions & 0 deletions cypress/integration/salt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/* eslint-disable cypress/no-unnecessary-waiting */
/* eslint-disable require-jsdoc */
var Countly = require("../../lib/countly");
// 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
Countly._internals.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");
}
44 changes: 43 additions & 1 deletion cypress/support/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,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 @@ -285,5 +325,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 72f19c0

Please sign in to comment.