Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Add option to block the creation and usage of the cookies by name #1901 #1902

Merged
merged 2 commits into from
Sep 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,9 @@ Cookie Configuration for instance based cookie management added in version 2.6.0
| enabled | boolean | true | A boolean that indicates whether the use of cookies by the SDK is enabled by the current instance. If false, the instance of the SDK initialized by this configuration will not store or read any data from cookies |
| domain | string | null | Custom cookie domain. This is helpful if you want to share Application Insights cookies across subdomains. If not provided uses the value from root `cookieDomain` value. |
| path | string | / | Specifies the path to use for the cookie, if not provided it will use any value from the root `cookiePath` value. |
| getCookie | `(name: string) => string` | null | Function to fetch the named cookie value, if not provided it will use the internal cookie parsing / caching. |
| ignoreCookies | string[] | undefined | Specify the cookie name(s) to be ignored, this will cause any matching cookie name to never be read or written. They may still be explicitly purged or deleted. You do not need to repeat the name in the `blockedCookies` configuration.(Since v2.8.8)
| blockedCookies | string[] | undefined | Specify the cookie name(s) to never be written, this will cause any cookie name to never be created or updated, they will still be read unless also included in the ignoreCookies and may still be explicitly purged or deleted. If not provided defaults to the same list provided in ignoreCookies. (Since v2.8.8)
| getCookie | `(name: string) => string` | null | Function to fetch the named cookie value, if not provided it will use the internal cookie parsing / caching. |
| setCookie | `(name: string, value: string) => void` | null | Function to set the named cookie with the specified value, only called when adding or updating a cookie. |
| delCookie | `(name: string, value: string) => void` | null | Function to delete the named cookie with the specified value, separated from setCookie to avoid the need to parse the value to determine whether the cookie is being added or removed.if not provided it will use the internal cookie parsing / caching. |

Expand Down
99 changes: 97 additions & 2 deletions shared/AppInsightsCore/Tests/Unit/src/CookieManager.Tests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Assert, AITestClass } from "@microsoft/ai-test-framework";
import { AppInsightsCore, CoreUtils, createCookieMgr, IAppInsightsCore, IConfiguration, ICookieMgrConfig, IPlugin, ITelemetryItem, newId, _legacyCookieMgr } from "../../../src/applicationinsights-core-js"
import { AppInsightsCore, CoreUtils, createCookieMgr, IAppInsightsCore, IConfiguration, ICookieMgrConfig, IPlugin, ITelemetryItem, newId, objExtend, _legacyCookieMgr } from "../../../src/applicationinsights-core-js"
import { _InternalMessageId, LoggingSeverity } from "../../../src/JavaScriptSDK.Enums/LoggingEnums";
import { _InternalLogMessage, DiagnosticLogger } from "../../../src/JavaScriptSDK/DiagnosticLogger";

Expand Down Expand Up @@ -396,7 +396,6 @@ export class CookieManagerTests extends AITestClass {
}
});


this.testCase({
name: "CookieManager: validate setting _canUseCookies correctly enables or blocks cookie usage",
test: () => {
Expand Down Expand Up @@ -427,6 +426,102 @@ export class CookieManagerTests extends AITestClass {
Assert.equal(true, manager.isEnabled());
}
});

this.testCase({
name: "CookieManager: validate ignore Cookies empty setting",
test: () => {

let cookieCfg: ICookieMgrConfig = objExtend(true, {}, this._cookieMgrCfg);
cookieCfg.ignoreCookies = [];

let core = new AppInsightsCore();
core.initialize({
instrumentationKey: "testiKey",
cookieDomain: "MyDomain.com",
cookieCfg: cookieCfg
}, [new ChannelPlugin()]);

let manager = core.getCookieMgr();

let newKey = "test." + newId();
let newValue = newId();
manager.set(newKey, newValue);
Assert.equal(newValue, manager.get(newKey));
Assert.equal(newValue + "; domain=MyDomain.com; path=/", this._testCookies[newKey]);

manager.del(newKey);
Assert.equal("", manager.get(newKey));
Assert.equal(undefined, this._testCookies[newKey]);
}
});

this.testCase({
name: "CookieManager: validate ignore Cookies with a single cookie",
test: () => {

let cookieCfg: ICookieMgrConfig = objExtend(true, {}, this._cookieMgrCfg);
cookieCfg.ignoreCookies = [ "testCookie" ];

let core = new AppInsightsCore();
core.initialize({
instrumentationKey: "testiKey",
cookieDomain: "MyDomain.com",
cookieCfg: cookieCfg
}, [new ChannelPlugin()]);

let manager = core.getCookieMgr();

this._testCookies["testCookie"] = "test value";
Assert.equal("", manager.get("testCookie"), "Check that it can't read the cookie value");

manager.set("testCookie", "new value");
Assert.equal("test value", this._testCookies["testCookie"], "The value was not overwritten");

let newKey = "test." + newId();
let newValue = newId();
manager.set(newKey, newValue);
Assert.equal(newValue, manager.get(newKey));
Assert.equal(newValue + "; domain=MyDomain.com; path=/", this._testCookies[newKey]);

manager.del(newKey);
Assert.equal("", manager.get(newKey));
Assert.equal(undefined, this._testCookies[newKey]);
}
});

this.testCase({
name: "CookieManager: validate blocked Cookies with a single cookie",
test: () => {

let cookieCfg: ICookieMgrConfig = objExtend(true, {}, this._cookieMgrCfg);
cookieCfg.blockedCookies = [ "testCookie" ];

let core = new AppInsightsCore();
core.initialize({
instrumentationKey: "testiKey",
cookieDomain: "MyDomain.com",
cookieCfg: cookieCfg
}, [new ChannelPlugin()]);

let manager = core.getCookieMgr();

this._testCookies["testCookie"] = "test value";
Assert.equal("test value", manager.get("testCookie"), "Check that it can't read the cookie value");

manager.set("testCookie", "new value");
Assert.equal("test value", this._testCookies["testCookie"], "The value was not overwritten");

let newKey = "test." + newId();
let newValue = newId();
manager.set(newKey, newValue);
Assert.equal(newValue, manager.get(newKey));
Assert.equal(newValue + "; domain=MyDomain.com; path=/", this._testCookies[newKey]);

manager.del(newKey);
Assert.equal("", manager.get(newKey));
Assert.equal(undefined, this._testCookies[newKey]);
}
});
}
}

Expand Down
14 changes: 14 additions & 0 deletions shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/ICookieMgr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,20 @@ export interface ICookieMgrConfig {
*/
path?: string;

/**
* Specify the cookie name(s) to be ignored, this will cause any matching cookie name to never be read or written.
* They may still be explicitly purged or deleted. You do not need to repeat the name in the `blockedCookies`
* configuration.(Since v2.8.8)
*/
ignoreCookies?: string[];

/**
* Specify the cookie name(s) to never be written, this will cause any cookie name to never be created or updated,
* they will still be read unless also included in the ignoreCookies and may still be explicitly purged or deleted.
* If not provided defaults to the same list provided in ignoreCookies. (Since v2.8.8)
*/
blockedCookies?: string[];

/**
* Hook function to fetch the named cookie value.
* @param name - The name of the cookie
Expand Down
24 changes: 21 additions & 3 deletions shared/AppInsightsCore/src/JavaScriptSDK/CookieMgr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { IDiagnosticLogger } from "../JavaScriptSDK.Interfaces/IDiagnosticLogger
import { _throwInternal } from "./DiagnosticLogger";
import { dumpObj, getDocument, getLocation, getNavigator, isIE } from "./EnvUtils";
import {
arrForEach, dateNow, getExceptionName, isFunction, isNotNullOrUndefined, isNullOrUndefined, isString, isTruthy, isUndefined,
arrForEach, dateNow, getExceptionName, isArray, isFunction, isNotNullOrUndefined, isNullOrUndefined, isString, isTruthy, isUndefined,
objForEachKey, setValue, strContains, strEndsWith, strTrim
} from "./HelperFuncs";
import { STR_EMPTY } from "./InternalConstants";
Expand Down Expand Up @@ -81,6 +81,24 @@ function _createCookieMgrConfig(rootConfig: IConfiguration): ICookieMgrConfig {
return cookieMgrCfg;
}

function _isIgnoredCookie(cookieMgrCfg: ICookieMgrConfig, name: string) {
if (name && cookieMgrCfg && isArray(cookieMgrCfg.ignoreCookies)) {
return cookieMgrCfg.ignoreCookies.indexOf(name) !== -1;
}

return false;
}

function _isBlockedCookie(cookieMgrCfg: ICookieMgrConfig, name: string) {
if (name && cookieMgrCfg && isArray(cookieMgrCfg.blockedCookies)) {
if (cookieMgrCfg.blockedCookies.indexOf(name) !== -1) {
return true;
}
}

return _isIgnoredCookie(cookieMgrCfg, name);
}

/**
* Helper to return the ICookieMgr from the core (if not null/undefined) or a default implementation
* associated with the configuration or a legacy default.
Expand Down Expand Up @@ -138,7 +156,7 @@ export function createCookieMgr(rootConfig?: IConfiguration, logger?: IDiagnosti
},
set: (name: string, value: string, maxAgeSec?: number, domain?: string, path?: string) => {
let result = false;
if (_isMgrEnabled(cookieMgr)) {
if (_isMgrEnabled(cookieMgr) && !_isBlockedCookie(cookieMgrConfig, name)) {
let values: any = {};
let theValue = strTrim(value || STR_EMPTY);
let idx = theValue.indexOf(";");
Expand Down Expand Up @@ -198,7 +216,7 @@ export function createCookieMgr(rootConfig?: IConfiguration, logger?: IDiagnosti
},
get: (name: string): string => {
let value = STR_EMPTY
if (_isMgrEnabled(cookieMgr)) {
if (_isMgrEnabled(cookieMgr) && !_isIgnoredCookie(cookieMgrConfig, name)) {
value = (cookieMgrConfig.getCookie || _getCookieValue)(name);
}

Expand Down