Skip to content

Commit

Permalink
refactored message options and added unit tests and fixed bugs found
Browse files Browse the repository at this point in the history
  • Loading branch information
phillip-haydon committed Mar 10, 2024
1 parent 54e3b89 commit dc7f054
Show file tree
Hide file tree
Showing 16 changed files with 406 additions and 386 deletions.
4 changes: 4 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@
<PackageProjectUrl>https://raygun.com/platform/crash-reporting</PackageProjectUrl>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
</PropertyGroup>

<PropertyGroup>
<RaygunPublicKey>0024000004800000940000000602000000240000525341310004000001000100499b604a09b4538bcd0e626ae13f86083c9ab5950e3d7f8465d18fb93fd5e445b8fa2a46c42187b02aaeea0b8f738f238b9e1975384adf036cca1545619980c3fbfaf0fe47b9b9e88986f02cdbdeea9d69876e4fbba06b1a9dfc79eb829e258a12d1e751042384655719e3dd58552c18a978f953d110ea0209535682d64ec5bf</RaygunPublicKey>
</PropertyGroup>
</Project>
19 changes: 17 additions & 2 deletions Mindscape.Raygun4Net.AspNetCore/ApplicationBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,25 @@ public static IApplicationBuilder UseRaygun(this IApplicationBuilder app)
return app.UseMiddleware<RaygunMiddleware>();
}

public static IServiceCollection AddRaygun(this IServiceCollection services, IConfiguration? configuration = null, Action<RaygunSettings>? configure = null)
public static IServiceCollection AddRaygun(this IServiceCollection services, IConfiguration configuration, Action<RaygunSettings>? configure = null)
{
// Fetch settings from configuration or use default settings
var settings = configuration?.GetSection("RaygunSettings").Get<RaygunSettings>() ?? new RaygunSettings();
var settings = configuration.GetSection("RaygunSettings").Get<RaygunSettings>() ?? new RaygunSettings();

// Override settings with user-provided settings
configure?.Invoke(settings);

services.TryAddSingleton(settings);
services.TryAddSingleton(s => new RaygunClient(s.GetService<RaygunSettings>()));
services.AddHttpContextAccessor();

return services;
}

public static IServiceCollection AddRaygun(this IServiceCollection services, Action<RaygunSettings>? configure = null)
{
// Fetch settings from configuration or use default settings
var settings = new RaygunSettings();

// Override settings with user-provided settings
configure?.Invoke(settings);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@

namespace Mindscape.Raygun4Net.AspNetCore.Builders
{
// ReSharper disable once ClassNeverInstantiated.Global
public class RaygunAspNetCoreRequestMessageBuilder
{
private const int MAX_RAW_DATA_LENGTH = 4096; // bytes

public static async Task<RaygunRequestMessage> Build(HttpContext? context, RaygunRequestMessageOptions? options)
public static async Task<RaygunRequestMessage> Build(HttpContext? context, RaygunSettings options)
{
if (context == null)
{
Expand All @@ -27,8 +28,6 @@ public static async Task<RaygunRequestMessage> Build(HttpContext? context, Raygu

var request = context.Request;

options = options ?? new RaygunRequestMessageOptions();

var message = new RaygunRequestMessage
{
HostName = request.Host.Value,
Expand Down Expand Up @@ -65,13 +64,13 @@ private static string GetIpAddress(ConnectionInfo connection)
return ip.ToString();
}

private static IDictionary GetQueryString(HttpRequest request, RaygunRequestMessageOptions options)
private static IDictionary GetQueryString(HttpRequest request, IRaygunHttpSettings options)
{
IDictionary queryString = null;

try
{
queryString = ToDictionary(request.Query, options.IsQueryParameterIgnored, options.IsSensitiveFieldIgnored);
queryString = ToDictionary(request.Query, s => IsIgnored(s, options.IgnoreQueryParameterNames), s=> IsIgnored(s, options.IgnoreSensitiveFieldNames));
}
catch (Exception e)
{
Expand All @@ -81,12 +80,12 @@ private static IDictionary GetQueryString(HttpRequest request, RaygunRequestMess
return queryString;
}

private static IList GetCookies(HttpRequest request, RaygunRequestMessageOptions options)
private static IList GetCookies(HttpRequest request, IRaygunHttpSettings options)
{
IList cookies;
try
{
cookies = request.Cookies.Where(c => !options.IsCookieIgnored(c.Key) && !options.IsSensitiveFieldIgnored(c.Key))
cookies = request.Cookies.Where(c => !IsIgnored(c.Key, options.IgnoreCookieNames) && !IsIgnored(c.Key, options.IgnoreSensitiveFieldNames))
.Select(c => new RaygunRequestMessage.Cookie(c.Key, c.Value)).ToList();
}
// ReSharper disable once EmptyGeneralCatchClause
Expand All @@ -98,7 +97,7 @@ private static IList GetCookies(HttpRequest request, RaygunRequestMessageOptions
return cookies;
}

private static string GetRawData(HttpRequest request, RaygunRequestMessageOptions options)
private static string GetRawData(HttpRequest request, IRaygunHttpSettings options)
{
if (options.IsRawDataIgnored)
{
Expand All @@ -124,7 +123,7 @@ private static string GetRawData(HttpRequest request, RaygunRequestMessageOption
if (contentType != null && CultureInfo.InvariantCulture.CompareInfo.IndexOf(contentType, "multipart/form-data", CompareOptions.IgnoreCase) >= 0)
{
// For multipart form data, gather up all the form names and values to be stripped out later.
ignoredMultiPartFormData = GetIgnoredFormValues(request.Form, options.IsFormFieldIgnored);
ignoredMultiPartFormData = GetIgnoredFormValues(request.Form, s => IsIgnored(s, options.IgnoreFormFieldNames));
}

request.Body.Seek(0, SeekOrigin.Begin);
Expand Down Expand Up @@ -207,7 +206,7 @@ private static string StripIgnoredFormData(string rawData, Dictionary<string, st
return rawData;
}

public static string StripSensitiveValues(string rawData, RaygunRequestMessageOptions options)
public static string StripSensitiveValues(string rawData, IRaygunHttpSettings options)
{
// Early escape if theres no data.
if (string.IsNullOrEmpty(rawData))
Expand All @@ -223,7 +222,7 @@ public static string StripSensitiveValues(string rawData, RaygunRequestMessageOp
// Parse the raw data into a dictionary.
if (filter.CanParse(rawData))
{
var filteredData = filter.Filter(rawData, options.SensitiveFieldNames());
var filteredData = filter.Filter(rawData, options.IgnoreSensitiveFieldNames);

if (!string.IsNullOrEmpty(filteredData))
{
Expand All @@ -233,7 +232,7 @@ public static string StripSensitiveValues(string rawData, RaygunRequestMessageOp
}

// We have failed to parse and filter the raw data, so check if the data contains sensitive values and should be dropped.
if (options.IsRawDataIgnoredWhenFilteringFailed && DataContains(rawData, options.SensitiveFieldNames()))
if (options.IsRawDataIgnoredWhenFilteringFailed && DataContains(rawData, options.IgnoreSensitiveFieldNames))
{
return null;
}
Expand All @@ -243,13 +242,13 @@ public static string StripSensitiveValues(string rawData, RaygunRequestMessageOp
}
}

private static IList<IRaygunDataFilter> GetRawDataFilters(RaygunRequestMessageOptions options)
private static IList<IRaygunDataFilter> GetRawDataFilters(IRaygunHttpSettings options)
{
var parsers = new List<IRaygunDataFilter>();

if (options.GetRawDataFilters() != null && options.GetRawDataFilters().Count > 0)
if (options.RawDataFilters != null && options.RawDataFilters.Count > 0)
{
parsers.AddRange(options.GetRawDataFilters());
parsers.AddRange(options.RawDataFilters);
}

if (options.UseXmlRawDataFilter)
Expand All @@ -265,7 +264,7 @@ private static IList<IRaygunDataFilter> GetRawDataFilters(RaygunRequestMessageOp
return parsers;
}

public static bool DataContains(string data, List<string> values)
public static bool DataContains(string data, IReadOnlyList<string> values)
{
bool exists = false;

Expand All @@ -281,13 +280,18 @@ public static bool DataContains(string data, List<string> values)
return exists;
}

private static IDictionary GetHeaders(HttpRequest request, RaygunRequestMessageOptions options)
private static IDictionary GetHeaders(HttpRequest request, IRaygunHttpSettings options)
{
IDictionary headers = new Dictionary<string, string>();
try
{
foreach (var header in request.Headers.Where(h => !options.IsHeaderIgnored(h.Key) && !options.IsSensitiveFieldIgnored(h.Key)))
foreach (var header in request.Headers)
{
if (IsIgnored(header.Key, options.IgnoreHeaderNames) || IsIgnored(header.Key, options.IgnoreSensitiveFieldNames))
{
continue;
}

headers[header.Key] = string.Join(",", header.Value);
}
}
Expand All @@ -299,15 +303,15 @@ private static IDictionary GetHeaders(HttpRequest request, RaygunRequestMessageO
return headers;
}

private static async Task<IDictionary> GetForm(HttpRequest request, RaygunRequestMessageOptions options)
private static async Task<IDictionary> GetForm(HttpRequest request, IRaygunHttpSettings options)
{
IDictionary form = null;

try
{
if (request.HasFormContentType)
{
form = ToDictionary(await request.ReadFormAsync(), options.IsFormFieldIgnored, options.IsSensitiveFieldIgnored);
form = ToDictionary(await request.ReadFormAsync(), s => IsIgnored(s, options.IgnoreFormFieldNames), s => IsIgnored(s, options.IgnoreSensitiveFieldNames));
}
}
catch (Exception e)
Expand Down Expand Up @@ -341,5 +345,52 @@ private static IDictionary ToDictionary(IFormCollection query, Func<string, bool

return dict;
}

internal static bool IsIgnored(string? key, IReadOnlyList<string> list)
{
if (key == null || (list.Count == 1 && "*".Equals(list[0])))
{
return true;
}

// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var ignoredKey in list)
{
var ignoreResult = ignoredKey switch
{
_ when ignoredKey.StartsWith("*") && ignoredKey.EndsWith("*") && ignoredKey.Length > 2 => CheckContains(ignoredKey, key),
_ when ignoredKey.StartsWith("*") => CheckEndsWith(ignoredKey, key),
_ when ignoredKey.EndsWith("*") => CheckStartsWith(ignoredKey, key),
_ => key.Equals(ignoredKey, StringComparison.OrdinalIgnoreCase)
};

if (ignoreResult)
{
return true;
}
}

return false;
}

private static bool CheckStartsWith(string ignoredKey, string key)
{
var value = ignoredKey.AsSpan(0, ignoredKey.Length - 1);

return key.AsSpan().StartsWith(value, StringComparison.OrdinalIgnoreCase);
}

private static bool CheckEndsWith(string ignoredKey, string key)
{
var value = ignoredKey.AsSpan(1);
return key.AsSpan().EndsWith(value, StringComparison.OrdinalIgnoreCase);
}

private static bool CheckContains(string ignoredKey, string key)
{
var value = ignoredKey.AsSpan(1, ignoredKey.Length - 2);

return key.AsSpan().IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0;
}
}
}
20 changes: 20 additions & 0 deletions Mindscape.Raygun4Net.AspNetCore/IRaygunHttpSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Collections.Generic;
using Mindscape.Raygun4Net.Filters;

namespace Mindscape.Raygun4Net.AspNetCore;

public interface IRaygunHttpSettings
{
List<string> IgnoreSensitiveFieldNames { get; }
List<string> IgnoreQueryParameterNames { get; }
List<string> IgnoreFormFieldNames { get; }
List<string> IgnoreHeaderNames { get; }
List<string> IgnoreCookieNames { get; }
List<string> IgnoreServerVariableNames { get; }
List<IRaygunDataFilter> RawDataFilters { get; }

bool IsRawDataIgnored { get; }
bool IsRawDataIgnoredWhenFilteringFailed { get; }
bool UseXmlRawDataFilter { get; }
bool UseKeyValuePairRawDataFilter { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,12 @@
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<InternalsVisibleTo Include="Raygun4Net.AspNetCore.Tests"/>
</ItemGroup>

<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<InternalsVisibleTo Include="Raygun4Net.AspNetCore.Tests, PublicKey=$(RaygunPublicKey)"/>
</ItemGroup>
</Project>
Loading

0 comments on commit dc7f054

Please sign in to comment.