From 89c38952a6c295f8b83022e35ca1c310f194d20e Mon Sep 17 00:00:00 2001 From: siyuniu-ms Date: Mon, 9 Sep 2024 17:09:51 -0700 Subject: [PATCH 1/4] pass policy --- .../Tests/manual/cspUsePolicyTest.html | 84 +++++++++++++++++++ .../src/snippet.ts | 38 ++++++++- .../src/type.ts | 16 ++++ 3 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 tools/applicationinsights-web-snippet/Tests/manual/cspUsePolicyTest.html diff --git a/tools/applicationinsights-web-snippet/Tests/manual/cspUsePolicyTest.html b/tools/applicationinsights-web-snippet/Tests/manual/cspUsePolicyTest.html new file mode 100644 index 000000000..5dce0ce3a --- /dev/null +++ b/tools/applicationinsights-web-snippet/Tests/manual/cspUsePolicyTest.html @@ -0,0 +1,84 @@ + + + + + + + + +
+ + + + + + + + + + diff --git a/tools/applicationinsights-web-snippet/src/snippet.ts b/tools/applicationinsights-web-snippet/src/snippet.ts index 9e3b83cdd..0a2e2f946 100644 --- a/tools/applicationinsights-web-snippet/src/snippet.ts +++ b/tools/applicationinsights-web-snippet/src/snippet.ts @@ -30,6 +30,7 @@ declare var cfg:ISnippetConfig; let strGetMethod = "GET"; let sdkInstanceName = "appInsightsSDK"; // required for Initialization to find the current instance let aiName = cfg.name || "appInsights"; // provide non default instance name through snipConfig name value + let policyName = cfg.pn || "1ds-default"; if (cfg.name || win[sdkInstanceName]) { // Only set if supplied or another name is defined to avoid polluting the global namespace win[sdkInstanceName] = aiName; @@ -304,9 +305,44 @@ declare var cfg:ISnippetConfig; loadFailed = false; } + function create_policy() { + // Function to handle URL validation + function validateURL(urlString: string): string | null { + try { + const url = new URL(urlString); + if (url.host && url.host === "js.monitor.azure.com") { + return urlString; + } else { + handleInvalidURL(urlString); + } + } catch { + handleInvalidURL(urlString); + } + } + + // Function to handle reporting failures + function handleInvalidURL(urlString: string) { + _reportFailure("AI policy blocked URL: " + urlString); + } + + return (window as any).trustedTypes?.createPolicy(policyName, { + createScriptURL: validateURL + }); + } + + const _createScript = (src: string) => { let scriptElement : HTMLElement = doc.createElement(scriptText); - (scriptElement as any)["src"] = src; + if (cfg.pl){ + if (cfg.ttp && cfg.ttp.createScript) { + (scriptElement as any)["src"] = cfg.ttp.createScriptURL(src); + } else { + (scriptElement as any)["src"] = create_policy().createScriptURL(src); + } + } else { + (scriptElement as any)["src"] = src; + } + if (integrity){ // Set the integrity attribute to the script tag if integrity is provided (scriptElement as any).integrity = integrity; diff --git a/tools/applicationinsights-web-snippet/src/type.ts b/tools/applicationinsights-web-snippet/src/type.ts index ede09f010..ed8482235 100644 --- a/tools/applicationinsights-web-snippet/src/type.ts +++ b/tools/applicationinsights-web-snippet/src/type.ts @@ -14,6 +14,13 @@ export interface SdkLoaderConfig { sri?: boolean; } +export abstract class TrustedTypePolicy { + readonly name: string; + createHTML?: ((input: string, ...args: any[]) => string) | undefined; + createScript?: ((input: string, ...args: any[]) => string) | undefined; + createScriptURL?: ((input: string, ...args: any[]) => string) | undefined; +} + export interface ISnippetConfig { src: string; name?: string; @@ -25,6 +32,15 @@ export interface ISnippetConfig { cr?: boolean; // cdn retry would be proceed if ture dle?: boolean; // Custom optional value to disable sdk load error to be sent sri?: boolean; // Custom optional value to specify whether fetching the snippet from integrity file and do integrity check + pl?: boolean; // Custom optional value to specify whether to enable the trusted type policy check on snippet + /** + * Custom optional value to specify the name of the trusted type policy that would be implemented on the snippet, default is '1ds-default' + */ + pn?: string; + /* + * Custom optional value to specify the trusted type policy that would be applied on the snippet src + */ + ttp?: TrustedTypePolicy; } export interface Fields { From ba0e79c398a4b6fb17dfa96524642e57b1c95197 Mon Sep 17 00:00:00 2001 From: siyuniu-ms Date: Mon, 9 Sep 2024 17:13:33 -0700 Subject: [PATCH 2/4] Update snippet.ts --- tools/applicationinsights-web-snippet/src/snippet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/applicationinsights-web-snippet/src/snippet.ts b/tools/applicationinsights-web-snippet/src/snippet.ts index 0a2e2f946..748af9f93 100644 --- a/tools/applicationinsights-web-snippet/src/snippet.ts +++ b/tools/applicationinsights-web-snippet/src/snippet.ts @@ -30,7 +30,7 @@ declare var cfg:ISnippetConfig; let strGetMethod = "GET"; let sdkInstanceName = "appInsightsSDK"; // required for Initialization to find the current instance let aiName = cfg.name || "appInsights"; // provide non default instance name through snipConfig name value - let policyName = cfg.pn || "1ds-default"; + let policyName = cfg.pn || "aiPolicy"; if (cfg.name || win[sdkInstanceName]) { // Only set if supplied or another name is defined to avoid polluting the global namespace win[sdkInstanceName] = aiName; From a0b698c14403a2b8ee9dd764978ba1a1c019c44c Mon Sep 17 00:00:00 2001 From: siyuniu-ms Date: Mon, 9 Sep 2024 17:31:53 -0700 Subject: [PATCH 3/4] add doc --- .../applicationinsights-web-snippet/README.md | 4 ++ .../src/type.ts | 8 ++- .../trustedTypeSupport.md | 62 +++++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 tools/applicationinsights-web-snippet/trustedTypeSupport.md diff --git a/tools/applicationinsights-web-snippet/README.md b/tools/applicationinsights-web-snippet/README.md index 0a75c9a70..440cfae03 100644 --- a/tools/applicationinsights-web-snippet/README.md +++ b/tools/applicationinsights-web-snippet/README.md @@ -45,6 +45,10 @@ let config = {connectionString: "InstrumentationKey=xxx", sri: true}; More details on web snippet, see [Web Snippet](https://github.com/microsoft/ApplicationInsights-JS#snippet-setup-ignore-if-using-npm-setup). +### Trusted Types Support (available since v1.2.2) +For restrictions like require-trusted-types-for 'script', check [url policy check](./trustedTypeSupport.md) +For restrictions like script-src 'self' ..., check [add nounce when inject script]() + ## Build ``` npm install -g grunt-cli diff --git a/tools/applicationinsights-web-snippet/src/type.ts b/tools/applicationinsights-web-snippet/src/type.ts index ed8482235..0836838aa 100644 --- a/tools/applicationinsights-web-snippet/src/type.ts +++ b/tools/applicationinsights-web-snippet/src/type.ts @@ -12,6 +12,9 @@ export interface SdkLoaderConfig { cr?: boolean; dle?: boolean; sri?: boolean; + pl?: boolean; + pn?: string; + ttp?: TrustedTypePolicy; } export abstract class TrustedTypePolicy { @@ -32,7 +35,10 @@ export interface ISnippetConfig { cr?: boolean; // cdn retry would be proceed if ture dle?: boolean; // Custom optional value to disable sdk load error to be sent sri?: boolean; // Custom optional value to specify whether fetching the snippet from integrity file and do integrity check - pl?: boolean; // Custom optional value to specify whether to enable the trusted type policy check on snippet + /** + * Custom optional value to specify whether to enable the trusted type policy check on snippet + */ + pl?: boolean; /** * Custom optional value to specify the name of the trusted type policy that would be implemented on the snippet, default is '1ds-default' */ diff --git a/tools/applicationinsights-web-snippet/trustedTypeSupport.md b/tools/applicationinsights-web-snippet/trustedTypeSupport.md new file mode 100644 index 000000000..9d3aaf2bb --- /dev/null +++ b/tools/applicationinsights-web-snippet/trustedTypeSupport.md @@ -0,0 +1,62 @@ +# Trust Type Support + +We offer two methods for implementing Trusted Type policy checks. Choose the one that best suits your needs. + +## Method 1: Using require-trusted-types-for 'script' +If your page utilizes require-trusted-types-for 'script' to enforce script injection policies, configure your snippet as follows: +### Configuration Options +```js + /** + * Custom optional value to specify whether to enable the trusted type policy check on snippet + */ + pl?: boolean; + /** + * Custom optional value to specify the name of the trusted type policy that would be implemented on the snippet, default is '1ds-default' + */ + pn?: string; + /* + * Custom optional value to specify the trusted type policy that would be applied on the snippet src + */ + ttp?: TrustedTypePolicy; +``` +### Automatic Policy Creation +To have the policy automatically created, set pl to true. You can optionally specify a policy name with pn. +Example usage: +```html + +``` +### Using a Custom Trusted Type Policy +If you prefer to pass your own Trusted Type Policy, create it and then apply it using the ttp option. + +Example: +```html + +``` +### Test +Your could also check our [test](./Tests/manual/cspUsePolicyTest.html) + +## Method 2: Using Nonce Tag and script-src From 673c1eb5e3d1f16d686fc1446b9c9656443072ab Mon Sep 17 00:00:00 2001 From: siyuniu-ms Date: Mon, 9 Sep 2024 17:33:55 -0700 Subject: [PATCH 4/4] name change --- .../Tests/manual/cspUsePolicyTest.html | 4 ++-- tools/applicationinsights-web-snippet/src/type.ts | 2 +- tools/applicationinsights-web-snippet/trustedTypeSupport.md | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/applicationinsights-web-snippet/Tests/manual/cspUsePolicyTest.html b/tools/applicationinsights-web-snippet/Tests/manual/cspUsePolicyTest.html index 5dce0ce3a..26740e988 100644 --- a/tools/applicationinsights-web-snippet/Tests/manual/cspUsePolicyTest.html +++ b/tools/applicationinsights-web-snippet/Tests/manual/cspUsePolicyTest.html @@ -23,7 +23,7 @@ return url; } }); -!(function (cfg){function e(){cfg.onInit&&cfg.onInit(n)}var x,D,E,t,L,C,n,U=window,O=document,b=U.location,I="script",j="ingestionendpoint",q="disableExceptionTracking",A="ai.device.";"instrumentationKey"[x="toLowerCase"](),D="crossOrigin",E="POST",t="appInsightsSDK",L=cfg.name||"appInsights",C=cfg.pn||"1ds-default",(cfg.name||U[t])&&(U[t]=L),n=U[L]||function(u){var s=!1,p=!1,l={initialize:!0,queue:[],sv:"8",version:2,config:u};function d(e){var t,n,i,a,r,o,c,s;!0!==cfg.dle&&(o=(t=function(){var e,t={},n=u.connectionString;if("string"==typeof n&&n)for(var i=n.split(";"),a=0;a