| | | 1 | | using Elsa.Diagnostics.StructuredLogs.Contracts; |
| | | 2 | | using Elsa.Diagnostics.StructuredLogs.Models; |
| | | 3 | | using Elsa.Diagnostics.StructuredLogs.Options; |
| | | 4 | | using Microsoft.Extensions.Options; |
| | | 5 | | using System.Text.RegularExpressions; |
| | | 6 | | |
| | | 7 | | namespace Elsa.Diagnostics.StructuredLogs.Services; |
| | | 8 | | |
| | 8 | 9 | | public class StructuredLogRedactor(IOptions<StructuredLogsOptions> options) : IStructuredLogRedactor |
| | | 10 | | { |
| | | 11 | | private const string Redacted = "[Redacted]"; |
| | 8 | 12 | | private readonly HashSet<string> _sensitiveNames = options.Value.SensitiveNames.ToHashSet(StringComparer.OrdinalIgno |
| | 8 | 13 | | private readonly IReadOnlyCollection<Regex> _sensitiveTextPatterns = options.Value.SensitiveTextPatterns |
| | 24 | 14 | | .Select(pattern => new Regex(pattern, RegexOptions.Compiled | RegexOptions.CultureInvariant)) |
| | 8 | 15 | | .ToList(); |
| | | 16 | | |
| | | 17 | | public StructuredLogEvent Redact(StructuredLogEvent logEvent) |
| | | 18 | | { |
| | 7 | 19 | | return logEvent with |
| | 7 | 20 | | { |
| | 7 | 21 | | Message = RedactValue("message", logEvent.Message) ?? string.Empty, |
| | 7 | 22 | | MessageTemplate = RedactValue("messageTemplate", logEvent.MessageTemplate), |
| | 7 | 23 | | Exception = RedactException(logEvent.Exception), |
| | 7 | 24 | | Scopes = RedactDictionary(logEvent.Scopes), |
| | 7 | 25 | | Properties = RedactDictionary(logEvent.Properties) |
| | 7 | 26 | | }; |
| | | 27 | | } |
| | | 28 | | |
| | | 29 | | private StructuredLogException? RedactException(StructuredLogException? exception) |
| | | 30 | | { |
| | 7 | 31 | | if (exception == null) |
| | 5 | 32 | | return null; |
| | | 33 | | |
| | 2 | 34 | | return exception with |
| | 2 | 35 | | { |
| | 2 | 36 | | Message = RedactValue("exceptionMessage", exception.Message) ?? string.Empty, |
| | 2 | 37 | | StackTrace = RedactValue("stackTrace", exception.StackTrace) |
| | 2 | 38 | | }; |
| | | 39 | | } |
| | | 40 | | |
| | | 41 | | private Dictionary<string, string?> RedactDictionary(IDictionary<string, string?> values) |
| | | 42 | | { |
| | 28 | 43 | | return values.ToDictionary(x => x.Key, x => RedactValue(x.Key, x.Value), StringComparer.OrdinalIgnoreCase); |
| | | 44 | | } |
| | | 45 | | |
| | | 46 | | private string? RedactValue(string name, string? value) |
| | | 47 | | { |
| | 25 | 48 | | if (value == null) |
| | 5 | 49 | | return null; |
| | | 50 | | |
| | 20 | 51 | | if (IsSensitiveName(name)) |
| | 3 | 52 | | return Redacted; |
| | | 53 | | |
| | 68 | 54 | | return _sensitiveTextPatterns.Aggregate(value, (current, pattern) => pattern.Replace(current, Redacted)); |
| | | 55 | | } |
| | | 56 | | |
| | 180 | 57 | | private bool IsSensitiveName(string name) => _sensitiveNames.Any(sensitiveName => name.Contains(sensitiveName, Strin |
| | | 58 | | } |