-
Notifications
You must be signed in to change notification settings - Fork 242
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[main] Custom Trusted Type Policy Support for Snippet Script Injection (
- Loading branch information
1 parent
5df231c
commit 5363b35
Showing
5 changed files
with
209 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
84 changes: 84 additions & 0 deletions
84
tools/applicationinsights-web-snippet/Tests/manual/cspUsePolicyTest.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
<!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;require-trusted-types-for 'script'"> | ||
<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"> | ||
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,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||"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[j]||(e=(n=t.endpointsuffix)?t.location:null,t[j]="https://"+(e?e+".":"")+"dc."+(n||"services.visualstudio.com")),t}()).instrumentationkey||u.instrumentationKey||"",t=(t=(t=t[j])&&"/"===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: "+(b&&b.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[A+"id"]=i[x](),n[A+"type"]=i,n["ai.operation.name"]=b&&b.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=O.createElement(I),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,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?O.getElementsByTagName("head")[0].appendChild(i):setTimeout(function(){O.getElementsByTagName(I)[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=O.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[q]&&!0!==S[q]&&(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 | ||
pl: true, // Optional value to specify whether to use Trusted Types for script URL | ||
pn: "aiPolicy", // Optional value to specify the policy name for Trusted Types | ||
ttp: myTrustedTypePolicy, | ||
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="1234"> | ||
|
||
function displayMessage(message, type) { | ||
var messageDiv = document.createElement('div'); | ||
messageDiv.className = type; | ||
messageDiv.textContent = message; | ||
document.getElementById('messages').appendChild(messageDiv); | ||
} | ||
|
||
|
||
</script> | ||
<script> | ||
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
tools/applicationinsights-web-snippet/trustedTypeSupport.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 'aiPolicy' | ||
*/ | ||
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 | ||
<script> | ||
!(function (cfg) ....)({ | ||
src: "https://js.monitor.azure.com/scripts/b/ai.3.gbl.min.js", | ||
pl: true, | ||
pn: "aiPolicy", | ||
cfg: { | ||
connectionString: "" | ||
} | ||
}); | ||
</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. | ||
|
||
Example: | ||
```html | ||
<script> | ||
const myTrustedTypePolicy = trustedTypes.createPolicy('myTrustedTypePolicy', { | ||
createScriptURL: (url) => { | ||
console.log('Trusted Type Policy: myTrustedTypePolicy called with URL:', url); | ||
return url; | ||
} | ||
}); | ||
!(function (cfg) ....)({ | ||
src: "https://js.monitor.azure.com/scripts/b/ai.3.gbl.min.js", | ||
pl: true, | ||
ttp: myTrustedTypePolicy, | ||
cfg: { | ||
connectionString: "" | ||
} | ||
}); | ||
</script> | ||
``` | ||
### Test | ||
Your could also check our [test](./Tests/manual/cspUsePolicyTest.html) | ||
|
||
## Method 2: Using Nonce Tag and script-src |