Skip to content

Commit

Permalink
add nounce support (#2409)
Browse files Browse the repository at this point in the history
  • Loading branch information
siyuniu-ms authored Sep 11, 2024
1 parent fe5c933 commit 11f7029
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<!DOCTYPE html>
<meta http-equiv="Content-Security-Policy" content="img-src https://browser.events.data.microsoft.com;frame-src https://browser.events.data.microsoft.com; script-src 'self' https://js.monitor.azure.com/scripts 'nonce-randomNonceValue';">
<html>
<head>
<style>
/* Optional styling for buttons and messages */
button {
margin: 10px;
padding: 10px 20px;
font-size: 16px;
}
#messages {
margin: 20px;
padding: 10px;
border: 1px solid #ccc;
background-color: #f9f9f9;
}
</style>
<script type="text/javascript" nonce="randomNonceValue" >
const myTrustedTypePolicy = trustedTypes.createPolicy('myTrustedTypePolicy', {
createScriptURL: (url) => {
console.log('Trusted Type Policy: myTrustedTypePolicy called with URL:', url);
return url;
}
});
!(function (cfg){function e(){cfg.onInit&&cfg.onInit(n)}var x,D,E,t,L,C,n,U=window,b=document,O=U.location,A="script",I="ingestionendpoint",j="disableExceptionTracking",q="ai.device.";"instrumentationKey"[x="toLowerCase"](),D="crossOrigin",E="POST",t="appInsightsSDK",L=cfg.name||"appInsights",C=cfg.pn||"aiPolicy",(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<i.length;a++){var r=i[a].split("=");2===r.length&&(t[r[0][x]()]=r[1])}return t[I]||(e=(n=t.endpointsuffix)?t.location:null,t[I]="https://"+(e?e+".":"")+"dc."+(n||"services.visualstudio.com")),t}()).instrumentationkey||u.instrumentationKey||"",t=(t=(t=t[I])&&"/"===t.slice(-1)?t.slice(0,-1):t)?t+"/v2/track":u.endpointUrl,t=u.userOverrideEndpointUrl||t,(n=[]).push((i="SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details)",a=e,c=t,(s=(r=f(o,"Exception")).data).baseType="ExceptionData",s.baseData.exceptions=[{typeName:"SDKLoadFailed",message:i.replace(/\./g,"-"),hasFullStack:!1,stack:i+"\nSnippet failed to load ["+a+"] -- Telemetry is disabled\nHelp Link: https://go.microsoft.com/fwlink/?linkid=2128109\nHost: "+(O&&O.pathname||"_unknown_")+"\nEndpoint: "+c,parsedStack:[]}],r)),n.push((s=e,i=t,(c=(a=f(o,"Message")).data).baseType="MessageData",(r=c.baseData).message='AI (Internal): 99 message:"'+("SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details) ("+s+")").replace(/\"/g,"")+'"',r.properties={endpoint:i},a)),e=n,o=t,JSON&&((c=U.fetch)&&!cfg.useXhr?c(o,{method:E,body:JSON.stringify(e),mode:"cors"}):XMLHttpRequest&&((s=new XMLHttpRequest).open(E,o),s.setRequestHeader("Content-type","application/json"),s.send(JSON.stringify(e)))))}function f(e,t){var n={},i="Browser";function a(e){e=""+e;return 1===e.length?"0"+e:e}return n[q+"id"]=i[x](),n[q+"type"]=i,n["ai.operation.name"]=O&&O.pathname||"_unknown_",n["ai.internal.sdkVersion"]="javascript:snippet_"+(l.sv||l.version),{time:(i=new Date).getUTCFullYear()+"-"+a(1+i.getUTCMonth())+"-"+a(i.getUTCDate())+"T"+a(i.getUTCHours())+":"+a(i.getUTCMinutes())+":"+a(i.getUTCSeconds())+"."+(i.getUTCMilliseconds()/1e3).toFixed(3).slice(2,5)+"Z",iKey:e,name:"Microsoft.ApplicationInsights."+e.replace(/-/g,"")+"."+t,sampleRate:100,tags:n,data:{baseData:{ver:2}},ver:undefined,seq:"1",aiDataContract:undefined}}var n,i,t,a,g=-1,h=0,m=["js.monitor.azure.com","js.cdn.applicationinsights.io","js.cdn.monitor.azure.com","js0.cdn.applicationinsights.io","js0.cdn.monitor.azure.com","js2.cdn.applicationinsights.io","js2.cdn.monitor.azure.com","az416426.vo.msecnd.net"],r=u.url||cfg.src,o=function(){return c(r,null)};function c(t,r){if((n=navigator)&&(~(n=(n.userAgent||"").toLowerCase()).indexOf("msie")||~n.indexOf("trident/"))&&~t.indexOf("ai.3")&&(t=t.replace(/(\/)(ai\.3\.)([^\d]*)$/,function(e,t,n){return t+"ai.2"+n})),!1!==cfg.cr)for(var e=0;e<m.length;e++)if(0<t.indexOf(m[e])){g=e;break}var n,o=function(e){var a;l.queue=[],p||(0<=g&&h+1<m.length?(a=(g+h+1)%m.length,i(t.replace(/^(.*\/\/)([\w\.]*)(\/.*)$/,function(e,t,n,i){return t+m[a]+i})),h+=1):(s=p=!0,d(t)))},c=function(e,t){p||setTimeout(function(){!t&&l.core||o()},500),s=!1},i=function(e){var n,i=b.createElement(A),e=(cfg.pl?cfg.ttp&&cfg.ttp.createScript?i.src=cfg.ttp.createScriptURL(e):i.src=(null==(n=window.trustedTypes)?void 0:n.createPolicy(C,{createScriptURL:function(e){try{var t=new URL(e);if(t.host&&"js.monitor.azure.com"===t.host)return e;a(e)}catch(n){a(e)}}})).createScriptURL(e):i.src=e,cfg.nt&&i.setAttribute("nonce",cfg.nt),r&&(i.integrity=r),i.setAttribute("data-ai-name",L),cfg[D]);function a(e){d("AI policy blocked URL: "+e)}return!e&&""!==e||"undefined"==i[D]||(i[D]=e),i.onload=c,i.onerror=o,i.onreadystatechange=function(e,t){"loaded"!==i.readyState&&"complete"!==i.readyState||c(0,t)},cfg.ld&&cfg.ld<0?b.getElementsByTagName("head")[0].appendChild(i):setTimeout(function(){b.getElementsByTagName(A)[0].parentNode.appendChild(i)},cfg.ld||0),i};i(t)}cfg.sri&&(n=r.match(/^((http[s]?:\/\/.*\/)\w+(\.\d+){1,5})\.(([\w]+\.){0,2}js)$/))&&6===n.length?(T="".concat(n[1],".integrity.json"),i="@".concat(n[4]),S=window.fetch,t=function(e){if(!e.ext||!e.ext[i]||!e.ext[i].file)throw Error("Error Loading JSON response");var t=e.ext[i].integrity||null;c(r=n[2]+e.ext[i].file,t)},S&&!cfg.useXhr?S(T,{method:"GET",mode:"cors"}).then(function(e){return e.json()["catch"](function(){return{}})}).then(t)["catch"](o):XMLHttpRequest&&((a=new XMLHttpRequest).open("GET",T),a.onreadystatechange=function(){if(a.readyState===XMLHttpRequest.DONE)if(200===a.status)try{t(JSON.parse(a.responseText))}catch(e){o()}else o()},a.send())):r&&o();try{l.cookie=b.cookie}catch(k){}function e(e){for(;e.length;)!function(t){l[t]=function(){var e=arguments;s||l.queue.push(function(){l[t].apply(l,e)})}}(e.pop())}var v,y,S="track",T="TrackPage",w="TrackEvent",S=(e([S+"Event",S+"PageView",S+"Exception",S+"Trace",S+"DependencyData",S+"Metric",S+"PageViewPerformance","start"+T,"stop"+T,"start"+w,"stop"+w,"addTelemetryInitializer","setAuthenticatedUserContext","clearAuthenticatedUserContext","flush"]),l.SeverityLevel={Verbose:0,Information:1,Warning:2,Error:3,Critical:4},(u.extensionConfig||{}).ApplicationInsightsAnalytics||{});return!0!==u[j]&&!0!==S[j]&&(e(["_"+(v="onerror")]),y=U[v],U[v]=function(e,t,n,i,a){var r=y&&y(e,t,n,i,a);return!0!==r&&l["_"+v]({message:e,url:t,lineNumber:n,columnNumber:i,error:a,evt:U.event}),r},u.autoExceptionInstrumented=!0),l}(cfg.cfg),(U[L]=n).queue&&0===n.queue.length?(n.queue.push(e),n.trackPageView({})):e();})({
src: "https://js.monitor.azure.com/scripts/b/ai.3.gbl.min.js",
// name: "appInsights", // Global SDK Instance name defaults to "appInsights" when not supplied
// ld: 0, // Defines the load delay (in ms) before attempting to load the sdk. -1 = block page load and add to head. (default) = 0ms load after timeout,
// useXhr: 1, // Use XHR instead of fetch to report failures (if available),
// dle: true, // Prevent the SDK from reporting load failure log
crossOrigin: "anonymous", // When supplied this will add the provided value as the cross origin attribute on the script tag
// onInit: null, // Once the application insights instance has loaded and initialized this callback function will be called with 1 argument -- the sdk instance (DO NOT ADD anything to the sdk.queue -- As they won't get called)
// sri: false, // Custom optional value to specify whether fetching the snippet from integrity file and do integrity check
nt: "randomNonceValue",
cfg: { // Application Insights Configuration
connectionString: "InstrumentationKey=814a172a-92fd-4950-9023-9cf13bb65696;IngestionEndpoint=https://eastus-8.in.applicationinsights.azure.com/;LiveEndpoint=https://eastus.livediagnostics.monitor.azure.com/"
}
});
</script>
</head>
<body>
<div id="messages"></div>
<button id="myButton">Send Telemetry</button>

<script nonce="randomNonceValue" >
function sendTelemetry() {
appInsights.trackEvent({name: 'test event'});
appInsights.flush();
}
</script>
<script nonce="randomNonceValue">
document.getElementById('myButton').addEventListener('click', sendTelemetry);
</script>

<script nonce="randomNonceValue">

function displayMessage(message, type) {
var messageDiv = document.createElement('div');
messageDiv.className = type;
messageDiv.textContent = message;
document.getElementById('messages').appendChild(messageDiv);
}


</script>
<script nonce="randomNonceValue">
var windowOnError = window.onerror;
window.onerror = function(message, source, lineno, colno, error) {
windowOnError && windowOnError(message, source, lineno, colno, error);
console.log('Error captured:', message, source, lineno, colno, error);

// Display the error message on the page
var errorMessage = document.createElement('div');
errorMessage.className = 'error';
errorMessage.textContent = 'Error: ' + message + ' at ' + source + ':' + lineno + ':' + colno;
document.getElementById('messages').appendChild(errorMessage);
};
</script>

</body>
</html>
4 changes: 4 additions & 0 deletions tools/applicationinsights-web-snippet/src/snippet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,10 @@ declare var cfg:ISnippetConfig;
} else {
(scriptElement as any)["src"] = src;
}

if (cfg.nt) {
(scriptElement as any).setAttribute("nonce", cfg.nt);
}

if (integrity){
// Set the integrity attribute to the script tag if integrity is provided
Expand Down
4 changes: 4 additions & 0 deletions tools/applicationinsights-web-snippet/src/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export interface ISnippetConfig {
* Custom optional value to specify the trusted type policy that would be applied on the snippet src
*/
ttp?: TrustedTypePolicy;
/**
* Custom optional value to specify the nounce tag value that would be applied on the script when we drop it on the page
*/
nt?: string;
}

export interface Fields {
Expand Down
38 changes: 28 additions & 10 deletions tools/applicationinsights-web-snippet/trustedTypeSupport.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Trust Type Support
# Trusted Type Policy Support

We offer two methods for implementing Trusted Type policy checks. Choose the one that best suits your needs.
We provide two methods for implementing Trusted Type policy checks. Choose the one that best aligns with your application's security requirements.

## Case 1: Enforcing Trusted Types with require-trusted-types-for 'script'
If your page enforces script injection policies using the require-trusted-types-for 'script' directive, configure the snippet with the following options.

## 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
/**
Expand All @@ -20,8 +21,8 @@ If your page utilizes require-trusted-types-for 'script' to enforce script injec
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:
To automatically create and apply a Trusted Type policy, set pl to true. Optionally, you can specify a custom policy name using the pn parameter.
Example:
```html
<script>
!(function (cfg) ....)({
Expand All @@ -35,8 +36,7 @@ Example usage:
</script>
```
### 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.

If you prefer to use your own Trusted Type policy, you can create and pass it using the ttp option.
Example:
```html
<script>
Expand All @@ -57,6 +57,24 @@ Example:
</script>
```
### Test
Your could also check our [test](./Tests/manual/cspUsePolicyTest.html)
You can test the Trusted Type policy implementation by using our [test example](./Tests/manual/cspUsePolicyTest.html)

## Method 2: Using Nonce Tag and script-src
## Method 2: Enforcing Script Policies with Nonce and script-src
If your page enforces script injection policies via the script-src 'self' directive, you can configure the snippet to use a nonce value.
Example:
```html
<script>
!(function (cfg) ....)({
src: "https://js.monitor.azure.com/scripts/b/ai.3.gbl.min.js",
nt: "randomNonceValue",
cfg: {
connectionString: ""
}
});
</script>
```
When the Application Insights script is added to your page, the provided nonce value will be tagged appropriately.
Notice: Make sure to include the nonce value in your Content Security Policy (CSP) directive as follows:
```html
script-src 'self' 'nonce-randomNonceValue'
```

0 comments on commit 11f7029

Please sign in to comment.