New JavaScript Property to support Content Security Policy (#1706)

* New JavaScript Property to support Content Security Policy
This commit is contained in:
Timothy Mothra 2020-03-03 14:03:58 -08:00 коммит произвёл GitHub
Родитель 528bebae6b
Коммит e2d030c3e6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 78 добавлений и 45 удалений

Просмотреть файл

@ -0,0 +1 @@
Microsoft.ApplicationInsights.AspNetCore.JavaScriptSnippet.ScriptBody.get -> string

Просмотреть файл

@ -0,0 +1 @@
Microsoft.ApplicationInsights.AspNetCore.JavaScriptSnippet.ScriptBody.get -> string

Просмотреть файл

@ -0,0 +1 @@
Microsoft.ApplicationInsights.AspNetCore.JavaScriptSnippet.ScriptBody.get -> string

Просмотреть файл

@ -0,0 +1 @@
Microsoft.ApplicationInsights.AspNetCore.JavaScriptSnippet.ScriptBody.get -> string

Просмотреть файл

@ -1,11 +1,10 @@
# Changelog
## VNext
- [New: JavaScript Property to support Content Security Policy](https://github.com/microsoft/ApplicationInsights-dotnet/issues/1443)
- [Fix: All perf counters stop being collected when any of faulty counters are specified to collect on Azure WebApps](https://github.com/microsoft/ApplicationInsights-dotnet/issues/1686)
- [Fix: Some perf counters aren't collected when app is hosted on Azure WebApp](https://github.com/microsoft/ApplicationInsights-dotnet/issues/1685)
## Version 2.14.0-beta2
- [Fix: AspNetCore AddApplicationInsightsSettings() and MissingMethodException](https://github.com/microsoft/ApplicationInsights-dotnet/issues/1702)

Просмотреть файл

@ -9,7 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".Solution Items", ".Solutio
ProjectSection(SolutionItems) = preProject
build.cmd = build.cmd
build.ps1 = build.ps1
CHANGELOG.md = CHANGELOG.md
..\CHANGELOG.md = ..\CHANGELOG.md
Common.targets = Common.targets
dirs.proj = dirs.proj
NuGet.config = NuGet.config

Просмотреть файл

@ -14,6 +14,9 @@
/// </summary>
public class JavaScriptSnippet : IJavaScriptSnippet
{
private const string ScriptTagBegin = @"<script type=""text/javascript"">";
private const string ScriptTagEnd = "</script>";
/// <summary>JavaScript snippet.</summary>
private static readonly string Snippet = Resources.JavaScriptSnippet;
@ -24,12 +27,12 @@
private readonly IHttpContextAccessor httpContextAccessor;
/// <summary>Configuration instance.</summary>
private TelemetryConfiguration telemetryConfiguration;
private readonly TelemetryConfiguration telemetryConfiguration;
/// <summary> Weather to print authenticated user tracking snippet.</summary>
private bool enableAuthSnippet;
private readonly bool enableAuthSnippet;
private JavaScriptEncoder encoder;
private readonly JavaScriptEncoder encoder;
/// <summary>
/// Initializes a new instance of the <see cref="JavaScriptSnippet"/> class.
@ -52,54 +55,84 @@
this.telemetryConfiguration = telemetryConfiguration;
this.httpContextAccessor = httpContextAccessor;
this.enableAuthSnippet = serviceOptions.Value.EnableAuthenticationTrackingJavaScript;
this.encoder = (encoder == null) ? JavaScriptEncoder.Default : encoder;
this.encoder = encoder ?? JavaScriptEncoder.Default;
}
/// <summary>
/// Gets a code snippet with instrumentation key initialized from TelemetryConfiguration.
/// Gets the full JavaScript Snippet in HTML script tags with instrumentation key initialized from TelemetryConfiguration.
/// </summary>
/// <returns>JavaScript code snippet with instrumentation key or empty if instrumentation key was not set for the application.</returns>
/// <remarks>This method will evaluate if Telemetry has been disabled in the config and if the instrumentation key was provided by either setting InstrumentationKey or ConnectionString.</remarks>
/// <returns>JavaScript code snippet with instrumentation key or returns string.Empty if instrumentation key was not set for the application.</returns>
public string FullScript
{
get
{
if (!this.IsAvailable())
{
return string.Empty;
}
else
{
return string.Concat(ScriptTagBegin, this.ScriptBody, ScriptTagEnd);
}
}
}
/// <summary>
/// Gets the JavaScript Snippet body (without HTML script tags) with instrumentation key initialized from TelemetryConfiguration.
/// </summary>
/// <returns>JavaScript code snippet with instrumentation key or returns string.Empty if instrumentation key was not set for the application.</returns>
public string ScriptBody
{
get
{
if (!this.telemetryConfiguration.DisableTelemetry)
// Config JS SDK
string insertConfig;
if (!string.IsNullOrEmpty(this.telemetryConfiguration.ConnectionString))
{
// Config JS SDK
string insertConfig;
if (!string.IsNullOrEmpty(this.telemetryConfiguration.ConnectionString))
{
insertConfig = string.Format(CultureInfo.InvariantCulture, "connectionString: '{0}'", this.telemetryConfiguration.ConnectionString);
}
else if (!string.IsNullOrEmpty(this.telemetryConfiguration.InstrumentationKey))
{
insertConfig = string.Format(CultureInfo.InvariantCulture, "instrumentationKey: '{0}'", this.telemetryConfiguration.InstrumentationKey);
}
else
{
return string.Empty;
}
// Auth Snippet (setAuthenticatedUserContext)
string insertAuthUserContext = string.Empty;
if (this.enableAuthSnippet)
{
IIdentity identity = this.httpContextAccessor?.HttpContext?.User?.Identity;
if (identity != null && identity.IsAuthenticated)
{
string escapedUserName = this.encoder.Encode(identity.Name);
insertAuthUserContext = string.Format(CultureInfo.InvariantCulture, AuthSnippet, escapedUserName);
}
}
// Return full snippet
// Developer Note: If you recently updated the snippet and are now getting "FormatException: Input string was not in a correct format." you need to escape all the curly braces; '{' => '{{' and '}' => '}}'.
return string.Format(CultureInfo.InvariantCulture, Snippet, insertConfig, insertAuthUserContext);
insertConfig = string.Format(CultureInfo.InvariantCulture, "connectionString: '{0}'", this.telemetryConfiguration.ConnectionString);
}
else if (!string.IsNullOrEmpty(this.telemetryConfiguration.InstrumentationKey))
{
insertConfig = string.Format(CultureInfo.InvariantCulture, "instrumentationKey: '{0}'", this.telemetryConfiguration.InstrumentationKey);
}
else
{
return string.Empty;
}
// Auth Snippet (setAuthenticatedUserContext)
string insertAuthUserContext = string.Empty;
if (this.enableAuthSnippet)
{
IIdentity identity = this.httpContextAccessor?.HttpContext?.User?.Identity;
if (identity != null && identity.IsAuthenticated)
{
string escapedUserName = this.encoder.Encode(identity.Name);
insertAuthUserContext = string.Format(CultureInfo.InvariantCulture, AuthSnippet, escapedUserName);
}
}
// Return snippet
// Developer Note: If you recently updated the snippet and are now getting "FormatException: Input string was not in a correct format." you need to escape all the curly braces; '{' => '{{' and '}' => '}}'.
return string.Format(CultureInfo.InvariantCulture, Snippet, insertConfig, insertAuthUserContext);
}
}
/// <summary>
/// Determine if we have enough information to build a full script.
/// </summary>
/// <returns>Returns true if we can build the JavaScript snippet.</returns>
private bool IsAvailable()
{
if (this.telemetryConfiguration.DisableTelemetry)
{
return false;
}
else
{
return !(string.IsNullOrEmpty(this.telemetryConfiguration.ConnectionString)
&& string.IsNullOrEmpty(this.telemetryConfiguration.InstrumentationKey));
}
}
}

Просмотреть файл

@ -121,16 +121,13 @@
<value>appInsights.setAuthenticatedUserContext("{0}");</value>
</data>
<data name="JavaScriptSnippet" xml:space="preserve">
<value>&lt;script type="text/javascript"&gt;
var sdkInstance="appInsightsSDK";window[sdkInstance]="appInsights";var aiName=window[sdkInstance],aisdk=window[aiName]||function(e){{function n(e){{t[e]=function(){{var n=arguments;t.queue.push(function(){{t[e].apply(t,n)}})}}}}var t={{config:e}};t.initialize=!0;var i=document,a=window;setTimeout(function(){{var n=i.createElement("script");n.src=e.url||"https://az416426.vo.msecnd.net/scripts/b/ai.2.min.js",i.getElementsByTagName("script")[0].parentNode.appendChild(n)}});try{{t.cookie=i.cookie}}catch(e){{}}t.queue=[],t.version=2;for(var r=["Event","PageView","Exception","Trace","DependencyData","Metric","PageViewPerformance"];r.length;)n("track"+r.pop());n("startTrackPage"),n("stopTrackPage");var s="Track"+r[0];if(n("start"+s),n("stop"+s),n("addTelemetryInitializer"),n("setAuthenticatedUserContext"),n("clearAuthenticatedUserContext"),n("flush"),!(!0===e.disableExceptionTracking||e.extensionConfig&amp;&amp;e.extensionConfig.ApplicationInsightsAnalytics&amp;&amp;!0===e.extensionConfig.ApplicationInsightsAnalytics.disableExceptionTracking)){{n("_"+(r="onerror"));var o=a[r];a[r]=function(e,n,i,a,s){{var c=o&amp;&amp;o(e,n,i,a,s);return!0!==c&amp;&amp;t["_"+r]({{message:e,url:n,lineNumber:i,columnNumber:a,error:s}}),c}},e.autoExceptionInstrumented=!0}}return t}}(
<value>var sdkInstance="appInsightsSDK";window[sdkInstance]="appInsights";var aiName=window[sdkInstance],aisdk=window[aiName]||function(e){{function n(e){{t[e]=function(){{var n=arguments;t.queue.push(function(){{t[e].apply(t,n)}})}}}}var t={{config:e}};t.initialize=!0;var i=document,a=window;setTimeout(function(){{var n=i.createElement("script");n.src=e.url||"https://az416426.vo.msecnd.net/scripts/b/ai.2.min.js",i.getElementsByTagName("script")[0].parentNode.appendChild(n)}});try{{t.cookie=i.cookie}}catch(e){{}}t.queue=[],t.version=2;for(var r=["Event","PageView","Exception","Trace","DependencyData","Metric","PageViewPerformance"];r.length;)n("track"+r.pop());n("startTrackPage"),n("stopTrackPage");var s="Track"+r[0];if(n("start"+s),n("stop"+s),n("addTelemetryInitializer"),n("setAuthenticatedUserContext"),n("clearAuthenticatedUserContext"),n("flush"),!(!0===e.disableExceptionTracking||e.extensionConfig&amp;&amp;e.extensionConfig.ApplicationInsightsAnalytics&amp;&amp;!0===e.extensionConfig.ApplicationInsightsAnalytics.disableExceptionTracking)){{n("_"+(r="onerror"));var o=a[r];a[r]=function(e,n,i,a,s){{var c=o&amp;&amp;o(e,n,i,a,s);return!0!==c&amp;&amp;t["_"+r]({{message:e,url:n,lineNumber:i,columnNumber:a,error:s}}),c}},e.autoExceptionInstrumented=!0}}return t}}(
{{
{0}
}});
window[aiName]=aisdk,aisdk.queue&amp;&amp;0===aisdk.queue.length&amp;&amp;aisdk.trackPageView({{}});
{1}
&lt;/script&gt;</value>
</value>
</data>
</root>