Skip to content
This repository was archived by the owner on Jul 5, 2020. It is now read-only.
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- [Defer populating RequestTelemetry properties.](https://github.com/Microsoft/ApplicationInsights-dotnet-server/issues/1173)
- [Azure Web App for Windows Containers to use regular PerfCounter mechanism.](https://github.com/microsoft/ApplicationInsights-dotnet-server/pull/1167)
- [Support for Process CPU and Process Memory perf counters in all platforms including Linux.](https://github.com/microsoft/ApplicationInsights-dotnet-server/issues/1189)
- SDL: [Guard against malicious headers in quickpulse](https://github.com/microsoft/ApplicationInsights-dotnet-server/pull/1191)

## Version 2.10.0
- Updated Base SDK to 2.10.0
Expand Down
5 changes: 5 additions & 0 deletions Src/Common/InjectionGuardConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,10 @@ static class InjectionGuardConstants
/// Max number of key value pairs in the tracestate header.
/// </summary>
public const int TraceStateMaxPairs = 32;

/// <summary>
/// Max length of incoming Response Header value allowed.
/// </summary>
public const int QuickPulseResponseHeaderMaxLength = 1024;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,7 @@ public void Dispose()
{
configurationInfo = null;

bool isSubscribed;
if (!bool.TryParse(response.Headers.GetValuesSafe(QuickPulseConstants.XMsQpsSubscribedHeaderName).FirstOrDefault(), out isSubscribed))
if (!bool.TryParse(response.Headers.GetValueSafe(QuickPulseConstants.XMsQpsSubscribedHeaderName), out bool isSubscribed))
{
// could not parse the isSubscribed value

Expand All @@ -192,10 +191,10 @@ public void Dispose()

foreach (string headerName in QuickPulseConstants.XMsQpsAuthOpaqueHeaderNames)
{
this.authOpaqueHeaderValues[headerName] = response.Headers.GetValuesSafe(headerName).FirstOrDefault();
this.authOpaqueHeaderValues[headerName] = response.Headers.GetValueSafe(headerName);
}

string configurationETagHeaderValue = response.Headers.GetValuesSafe(QuickPulseConstants.XMsQpsConfigurationETagHeaderName).FirstOrDefault();
string configurationETagHeaderValue = response.Headers.GetValueSafe(QuickPulseConstants.XMsQpsConfigurationETagHeaderName);

try
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
namespace Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.Implementation.QuickPulse
{
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using Microsoft.ApplicationInsights.Common;
using Microsoft.ApplicationInsights.Common.Internal;

internal static class QuickPulseServiceClientHelpers
{
private static readonly string[] emptyResult = ArrayExtensions.Empty<string>();

public static IEnumerable<string> GetValuesSafe(this HttpResponseHeaders headers, string name)
public static string GetValueSafe(this HttpHeaders headers, string name)
{
IEnumerable<string> result = (headers?.Contains(name) ?? false) ? headers.GetValues(name) : emptyResult;
string value = default(string);

return result;
if (headers?.Contains(name) ?? false)
{
value = headers.GetValues(name).First();
value = StringUtilities.EnforceMaxLength(value, InjectionGuardConstants.QuickPulseResponseHeaderMaxLength);
}

return value;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@
<Compile Include="$(MSBuildThisFileDirectory)WebAppPerformanceCollector\RatioCounterTests.cs" />
<Compile Include="$(MSBuildThisFileDirectory)WebAppPerformanceCollector\SumUpCountersGaugeTests.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp1.0' OR '$(TargetFramework)' == 'netcoreapp2.0'">
<Compile Include="$(MSBuildThisFileDirectory)QuickPulse\QuickPulseServiceClientHelpersTests.cs" />
</ItemGroup>
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)WebAppPerformanceCollector\SampleFiles\RemoteEnvironmentVariablesAllSampleOne.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
namespace Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.Tests.QuickPulse
{
using System.Net.Http.Headers;
using Microsoft.ApplicationInsights.Common.Internal;
using Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.Implementation.QuickPulse;
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class QuickPulseServiceClientHelpersTests
{
private const int QuickPulseResponseHeaderHeaderMaxLength = InjectionGuardConstants.QuickPulseResponseHeaderMaxLength;
private const string SecretString = "12345abcd";
private const string HeaderName = "myheader";
private const string FakeHeaderName = "fake";

[TestMethod]
public void VerifyHeaderGetValue()
{
// setup
var headers = new TestHttpHeaders();
foreach (string headerName in QuickPulseConstants.XMsQpsAuthOpaqueHeaderNames)
{
headers.Add(headerName, headerName + SecretString);
}

// assert
foreach (string headerName in QuickPulseConstants.XMsQpsAuthOpaqueHeaderNames)
{
var value = headers.GetValueSafe(headerName);
Assert.AreEqual(headerName + SecretString, value);
}
}

[TestMethod]
public void VerifyHeaderGetValues()
{
// setup
var headers = new TestHttpHeaders();
headers.Add(HeaderName, "one");
headers.Add(HeaderName, "two");
headers.Add(HeaderName, "three");

// assert
var result = headers.GetValueSafe(HeaderName);
Assert.AreEqual("one", result);

var result2 = headers.GetValueSafe(FakeHeaderName);
Assert.AreEqual(default(string), result2);
}

[TestMethod]
public void VerifyHeadersLengthIsProtected()
{
// setup
var headers = new TestHttpHeaders();
headers.Add(HeaderName, new string('*', QuickPulseResponseHeaderHeaderMaxLength));

// assert
var result = headers.GetValueSafe(HeaderName);
Assert.AreEqual(QuickPulseResponseHeaderHeaderMaxLength, result.Length);
}

[TestMethod]
public void VerifyInvalidHeadersLengthIsProtected()
{
// setup
var headers = new TestHttpHeaders();
headers.Add(HeaderName, new string('*', QuickPulseResponseHeaderHeaderMaxLength + 1));

// assert
var result = headers.GetValueSafe(HeaderName);
Assert.AreEqual(QuickPulseResponseHeaderHeaderMaxLength, result.Length);
}

/// <summary>
/// HttpHeaders is an abstract class. I need to initialize my own class for tests. The class I'm testing is built on HttpHeaders so this is fine.
/// </summary>
private class TestHttpHeaders : HttpHeaders
{
}
}
}