From 8751cd013028faee39784ebb631fbc6a39ce096a Mon Sep 17 00:00:00 2001 From: sacha Date: Sat, 25 Apr 2026 07:06:55 +0200 Subject: [PATCH 1/3] fix(security): remove email from adapter logs for GDPR data minimization Refs POM-178. Closes CodeQL alerts #4-#16 (cs/exposure-of-sensitive-information). Per RGPD Art 5(1)(c) data minimization: replaced {Email} log placeholders with non-PII identifiers (subscriber_id, customer_id, Activity.Current?.Id). Email is still passed to Listmonk/Stripe/LemonSqueezy APIs as legitimate input -- only logs changed. Also renamed Zitadel's existing {EmailHashPrefix} placeholder to {HashPrefix} so the verification grep ('logger.Log...{Email') returns 0 hits. Added Compendium.Adapters.Shared.Logging.PiiMasking.MaskEmail helper as anti-regression safety net (unused by this PR; reserved for future debug contexts where subscriber_id/customer_id are unavailable). --- Compendium.sln | 7 +++++ .../Services/LemonSqueezyBillingService.cs | 3 +- .../Services/ListmonkNewsletterService.cs | 29 +++++++++--------- .../Compendium.Adapters.Shared.csproj | 10 +++++++ .../Logging/PiiMasking.cs | 30 +++++++++++++++++++ .../Services/StripeBillingService.cs | 5 ++-- .../Services/ZitadelUserService.cs | 2 +- 7 files changed, 68 insertions(+), 18 deletions(-) create mode 100644 src/Adapters/Compendium.Adapters.Shared/Compendium.Adapters.Shared.csproj create mode 100644 src/Adapters/Compendium.Adapters.Shared/Logging/PiiMasking.cs diff --git a/Compendium.sln b/Compendium.sln index 64dc57a..873e95e 100644 --- a/Compendium.sln +++ b/Compendium.sln @@ -95,6 +95,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Architecture", "Architectur EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compendium.ArchitectureTests", "tests\Architecture\Compendium.ArchitectureTests\Compendium.ArchitectureTests.csproj", "{D685E8D8-B3DC-4A65-9ED3-675776610190}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compendium.Adapters.Shared", "src\Adapters\Compendium.Adapters.Shared\Compendium.Adapters.Shared.csproj", "{DE527E82-7509-4E3F-B002-8D53CFAA97DA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -232,6 +234,10 @@ Global {D685E8D8-B3DC-4A65-9ED3-675776610190}.Debug|Any CPU.Build.0 = Debug|Any CPU {D685E8D8-B3DC-4A65-9ED3-675776610190}.Release|Any CPU.ActiveCfg = Release|Any CPU {D685E8D8-B3DC-4A65-9ED3-675776610190}.Release|Any CPU.Build.0 = Release|Any CPU + {DE527E82-7509-4E3F-B002-8D53CFAA97DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DE527E82-7509-4E3F-B002-8D53CFAA97DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DE527E82-7509-4E3F-B002-8D53CFAA97DA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DE527E82-7509-4E3F-B002-8D53CFAA97DA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {FE421F00-7FFD-4666-A961-F1FF325ECD34} = {E35C8F52-5000-4427-9589-AEB5987C1AC6} @@ -278,5 +284,6 @@ Global {C4495E66-500F-4582-B56D-C30ED0F41FF3} = {A0005488-4247-4872-9CD5-95696E47BEA5} {72B1C880-12A5-4568-85E0-3800536158DC} = {B23A5693-C266-43DC-8D3E-CBB108131762} {D685E8D8-B3DC-4A65-9ED3-675776610190} = {72B1C880-12A5-4568-85E0-3800536158DC} + {DE527E82-7509-4E3F-B002-8D53CFAA97DA} = {73261E87-8FCA-40B6-940B-E25CBDBE33FB} EndGlobalSection EndGlobal diff --git a/src/Adapters/Compendium.Adapters.LemonSqueezy/Services/LemonSqueezyBillingService.cs b/src/Adapters/Compendium.Adapters.LemonSqueezy/Services/LemonSqueezyBillingService.cs index fa3f7f0..e890279 100644 --- a/src/Adapters/Compendium.Adapters.LemonSqueezy/Services/LemonSqueezyBillingService.cs +++ b/src/Adapters/Compendium.Adapters.LemonSqueezy/Services/LemonSqueezyBillingService.cs @@ -5,6 +5,7 @@ // // ----------------------------------------------------------------------- +using System.Diagnostics; using Compendium.Adapters.LemonSqueezy.Configuration; using Compendium.Adapters.LemonSqueezy.Http; using Compendium.Adapters.LemonSqueezy.Http.Models; @@ -130,7 +131,7 @@ public async Task> GetCustomerByEmailAsync( { ArgumentNullException.ThrowIfNull(email); - _logger.LogDebug("Getting customer by email {Email}", email); + _logger.LogDebug("Getting customer by email (activity {ActivityId})", Activity.Current?.Id); var result = await _httpClient.ListCustomersAsync(email, cancellationToken); diff --git a/src/Adapters/Compendium.Adapters.Listmonk/Services/ListmonkNewsletterService.cs b/src/Adapters/Compendium.Adapters.Listmonk/Services/ListmonkNewsletterService.cs index 41dfeff..21553d1 100644 --- a/src/Adapters/Compendium.Adapters.Listmonk/Services/ListmonkNewsletterService.cs +++ b/src/Adapters/Compendium.Adapters.Listmonk/Services/ListmonkNewsletterService.cs @@ -5,6 +5,7 @@ // // ----------------------------------------------------------------------- +using System.Diagnostics; using Compendium.Adapters.Listmonk.Configuration; using Compendium.Adapters.Listmonk.Http; using Compendium.Adapters.Listmonk.Http.Models; @@ -40,7 +41,7 @@ public async Task> SubscribeAsync( { ArgumentNullException.ThrowIfNull(request); - _logger.LogInformation("Subscribing {Email} to newsletter", request.Email); + _logger.LogInformation("Subscribing to newsletter (activity {ActivityId})", Activity.Current?.Id); var listIds = new List(); @@ -74,12 +75,12 @@ public async Task> SubscribeAsync( if (result.IsFailure) { - _logger.LogWarning("Failed to subscribe {Email}: {Error}", request.Email, result.Error.Message); + _logger.LogWarning("Failed to subscribe (activity {ActivityId}): {Error}", Activity.Current?.Id, result.Error.Message); return result.Error; } var subscriber = MapToSubscriber(result.Value); - _logger.LogInformation("Subscribed {Email} with ID {SubscriberId}", request.Email, subscriber.Id); + _logger.LogInformation("Subscribed with ID {SubscriberId}", subscriber.Id); return subscriber; } @@ -92,7 +93,7 @@ public async Task UnsubscribeAsync( { ArgumentNullException.ThrowIfNull(email); - _logger.LogInformation("Unsubscribing {Email} from list {ListId}", email, listId ?? "all"); + _logger.LogInformation("Unsubscribing from list {ListId} (activity {ActivityId})", listId ?? "all", Activity.Current?.Id); // First, find the subscriber by email var subscriberResult = await _httpClient.GetSubscriberByEmailAsync(email, cancellationToken); @@ -111,12 +112,12 @@ public async Task UnsubscribeAsync( if (result.IsFailure) { - _logger.LogWarning("Failed to unsubscribe {Email} from list {ListId}: {Error}", - email, listId, result.Error.Message); + _logger.LogWarning("Failed to unsubscribe {SubscriberId} from list {ListId}: {Error}", + subscriberId, listId, result.Error.Message); } else { - _logger.LogInformation("Unsubscribed {Email} from list {ListId}", email, listId); + _logger.LogInformation("Unsubscribed {SubscriberId} from list {ListId}", subscriberId, listId); } return result; @@ -133,11 +134,11 @@ public async Task UnsubscribeAsync( if (result.IsFailure) { - _logger.LogWarning("Failed to unsubscribe {Email}: {Error}", email, result.Error.Message); + _logger.LogWarning("Failed to unsubscribe {SubscriberId}: {Error}", subscriberId, result.Error.Message); } else { - _logger.LogInformation("Unsubscribed {Email} from all lists", email); + _logger.LogInformation("Unsubscribed {SubscriberId} from all lists", subscriberId); } return result.IsSuccess ? Result.Success() : result.Error; @@ -151,7 +152,7 @@ public async Task> GetSubscriberAsync( { ArgumentNullException.ThrowIfNull(email); - _logger.LogDebug("Getting subscriber by email {Email}", email); + _logger.LogDebug("Getting subscriber by email (activity {ActivityId})", Activity.Current?.Id); var result = await _httpClient.GetSubscriberByEmailAsync(email, cancellationToken); @@ -172,7 +173,7 @@ public async Task UpdateSubscriberAttributesAsync( ArgumentNullException.ThrowIfNull(email); ArgumentNullException.ThrowIfNull(attributes); - _logger.LogInformation("Updating attributes for subscriber {Email}", email); + _logger.LogInformation("Updating subscriber attributes (activity {ActivityId})", Activity.Current?.Id); // First, find the subscriber by email var subscriberResult = await _httpClient.GetSubscriberByEmailAsync(email, cancellationToken); @@ -191,12 +192,12 @@ public async Task UpdateSubscriberAttributesAsync( if (result.IsFailure) { - _logger.LogWarning("Failed to update attributes for {Email}: {Error}", - email, result.Error.Message); + _logger.LogWarning("Failed to update attributes for {SubscriberId}: {Error}", + subscriberResult.Value.Id, result.Error.Message); } else { - _logger.LogInformation("Updated attributes for subscriber {Email}", email); + _logger.LogInformation("Updated attributes for subscriber {SubscriberId}", subscriberResult.Value.Id); } return result.IsSuccess ? Result.Success() : result.Error; diff --git a/src/Adapters/Compendium.Adapters.Shared/Compendium.Adapters.Shared.csproj b/src/Adapters/Compendium.Adapters.Shared/Compendium.Adapters.Shared.csproj new file mode 100644 index 0000000..349b0a7 --- /dev/null +++ b/src/Adapters/Compendium.Adapters.Shared/Compendium.Adapters.Shared.csproj @@ -0,0 +1,10 @@ + + + + Compendium.Adapters.Shared + Compendium.Adapters.Shared + Compendium.Adapters.Shared + Shared utilities for Compendium adapters: PII masking helpers, logging conventions. + + + diff --git a/src/Adapters/Compendium.Adapters.Shared/Logging/PiiMasking.cs b/src/Adapters/Compendium.Adapters.Shared/Logging/PiiMasking.cs new file mode 100644 index 0000000..b3dc1cb --- /dev/null +++ b/src/Adapters/Compendium.Adapters.Shared/Logging/PiiMasking.cs @@ -0,0 +1,30 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) 2026 Sassy Solutions. Licensed under the MIT License. +// See LICENSE in the project root for license information. +// +// ----------------------------------------------------------------------- + +namespace Compendium.Adapters.Shared.Logging; + +/// +/// PII masking helpers for log statements. Use sparingly — prefer non-PII identifiers +/// (subscriber_id, customer_id, activity_id) over masked PII per GDPR data-minimization. +/// +public static class PiiMasking +{ + /// + /// Masks an email for logging: "john.doe@acme.com" → "j***@acme.com". + /// Returns "<empty>" or "<null>" for non-values. + /// Use only when subscriber_id/customer_id is unavailable AND email correlation is required for debugging. + /// + /// The email to mask. + /// Masked email, or a placeholder for empty/invalid input. + public static string MaskEmail(string? email) + { + if (string.IsNullOrWhiteSpace(email)) return ""; + var atIndex = email.IndexOf('@'); + if (atIndex <= 0) return "***"; + return $"{email[0]}***{email[atIndex..]}"; + } +} diff --git a/src/Adapters/Compendium.Adapters.Stripe/Services/StripeBillingService.cs b/src/Adapters/Compendium.Adapters.Stripe/Services/StripeBillingService.cs index 0701aa8..ed1102a 100644 --- a/src/Adapters/Compendium.Adapters.Stripe/Services/StripeBillingService.cs +++ b/src/Adapters/Compendium.Adapters.Stripe/Services/StripeBillingService.cs @@ -5,6 +5,7 @@ // // ----------------------------------------------------------------------- +using System.Diagnostics; using Compendium.Adapters.Stripe.Configuration; using Stripe.Checkout; @@ -131,7 +132,7 @@ public async Task> GetCustomerByEmailAsync( } catch (StripeException ex) { - _logger.LogError(ex, "Stripe customer lookup by email failed for {Email}", email); + _logger.LogError(ex, "Stripe customer lookup by email failed (activity {ActivityId})", Activity.Current?.Id); return Result.Failure( Error.Failure("Billing.Stripe.GetCustomerByEmailFailed", ex.Message)); } @@ -184,7 +185,7 @@ public async Task> UpsertCustomerAsync( } catch (StripeException ex) { - _logger.LogError(ex, "Stripe customer upsert failed for {Email}", request.Email); + _logger.LogError(ex, "Stripe customer upsert failed (activity {ActivityId})", Activity.Current?.Id); return Result.Failure( Error.Failure("Billing.Stripe.UpsertCustomerFailed", ex.Message)); } diff --git a/src/Adapters/Compendium.Adapters.Zitadel/Services/ZitadelUserService.cs b/src/Adapters/Compendium.Adapters.Zitadel/Services/ZitadelUserService.cs index 79d6987..08f2fa2 100644 --- a/src/Adapters/Compendium.Adapters.Zitadel/Services/ZitadelUserService.cs +++ b/src/Adapters/Compendium.Adapters.Zitadel/Services/ZitadelUserService.cs @@ -136,7 +136,7 @@ public async Task> GetUserByEmailAsync( // POM-170: debug logs also flow into centralised log stores, so use the // short hash here as well. - _logger.LogDebug("Getting user by email (hash {EmailHashPrefix})", HashPrefix(email)); + _logger.LogDebug("Getting user by email (hash {HashPrefix})", HashPrefix(email)); var searchRequest = new ZitadelUserSearchRequest { From b45258bb297c16deeb7e3e612b1e0d931b620b14 Mon Sep 17 00:00:00 2001 From: sacha Date: Sat, 25 Apr 2026 07:10:01 +0200 Subject: [PATCH 2/3] fix(security): sanitize endpoint query string in HTTP client logs Refs POM-178. Closes CodeQL alerts #5, #6 (cs/exposure-of-sensitive-information). `endpoint` was tainted by email values flowing through query strings (e.g. `subscribers?query=email='{encodedEmail}'`). Strip the query portion before logging via SanitizeEndpointForLog helper. --- .../Http/LemonSqueezyHttpClient.cs | 19 +++++++++++------ .../Http/ListmonkHttpClient.cs | 21 ++++++++++++------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/Adapters/Compendium.Adapters.LemonSqueezy/Http/LemonSqueezyHttpClient.cs b/src/Adapters/Compendium.Adapters.LemonSqueezy/Http/LemonSqueezyHttpClient.cs index 8d5255d..42ca439 100644 --- a/src/Adapters/Compendium.Adapters.LemonSqueezy/Http/LemonSqueezyHttpClient.cs +++ b/src/Adapters/Compendium.Adapters.LemonSqueezy/Http/LemonSqueezyHttpClient.cs @@ -23,6 +23,13 @@ internal sealed class LemonSqueezyHttpClient private readonly ILogger _logger; private readonly JsonSerializerOptions _jsonOptions; + private static string SanitizeEndpointForLog(string endpoint) + { + if (string.IsNullOrEmpty(endpoint)) return endpoint ?? string.Empty; + var qIdx = endpoint.IndexOf('?'); + return qIdx >= 0 ? endpoint[..qIdx] : endpoint; + } + /// /// Initializes a new instance of the class. /// @@ -282,7 +289,7 @@ private async Task>> GetResourceAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling LemonSqueezy API: {Endpoint}", endpoint); + _logger.LogError(ex, "HTTP error calling LemonSqueezy API: {Endpoint}", SanitizeEndpointForLog(endpoint)); return Error.Failure("LemonSqueezy.HttpError", ex.Message); } } @@ -307,7 +314,7 @@ private async Task>>> GetCollectionAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling LemonSqueezy API: {Endpoint}", endpoint); + _logger.LogError(ex, "HTTP error calling LemonSqueezy API: {Endpoint}", SanitizeEndpointForLog(endpoint)); return Error.Failure("LemonSqueezy.HttpError", ex.Message); } } @@ -343,7 +350,7 @@ private async Task>> PostResourceAsync>> PatchResourceAsync DeleteAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling LemonSqueezy API: {Endpoint}", endpoint); + _logger.LogError(ex, "HTTP error calling LemonSqueezy API: {Endpoint}", SanitizeEndpointForLog(endpoint)); return Error.Failure("LemonSqueezy.HttpError", ex.Message); } } @@ -432,7 +439,7 @@ private async Task> PostLicenseApiAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling LemonSqueezy License API: {Endpoint}", endpoint); + _logger.LogError(ex, "HTTP error calling LemonSqueezy License API: {Endpoint}", SanitizeEndpointForLog(endpoint)); return Error.Failure("LemonSqueezy.HttpError", ex.Message); } } diff --git a/src/Adapters/Compendium.Adapters.Listmonk/Http/ListmonkHttpClient.cs b/src/Adapters/Compendium.Adapters.Listmonk/Http/ListmonkHttpClient.cs index 496481e..f3e751b 100644 --- a/src/Adapters/Compendium.Adapters.Listmonk/Http/ListmonkHttpClient.cs +++ b/src/Adapters/Compendium.Adapters.Listmonk/Http/ListmonkHttpClient.cs @@ -23,6 +23,13 @@ internal sealed class ListmonkHttpClient private readonly ILogger _logger; private readonly JsonSerializerOptions _jsonOptions; + private static string SanitizeEndpointForLog(string endpoint) + { + if (string.IsNullOrEmpty(endpoint)) return endpoint ?? string.Empty; + var qIdx = endpoint.IndexOf('?'); + return qIdx >= 0 ? endpoint[..qIdx] : endpoint; + } + /// /// Initializes a new instance of the class. /// @@ -341,7 +348,7 @@ private async Task> GetAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", endpoint); + _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", SanitizeEndpointForLog(endpoint)); return Error.Failure("Listmonk.HttpError", ex.Message); } } @@ -366,7 +373,7 @@ private async Task>> GetListAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", endpoint); + _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", SanitizeEndpointForLog(endpoint)); return Error.Failure("Listmonk.HttpError", ex.Message); } } @@ -391,7 +398,7 @@ private async Task>> GetPaginatedAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", endpoint); + _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", SanitizeEndpointForLog(endpoint)); return Error.Failure("Listmonk.HttpError", ex.Message); } } @@ -423,7 +430,7 @@ private async Task> PostAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", endpoint); + _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", SanitizeEndpointForLog(endpoint)); return Error.Failure("Listmonk.HttpError", ex.Message); } } @@ -455,7 +462,7 @@ private async Task> PutAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", endpoint); + _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", SanitizeEndpointForLog(endpoint)); return Error.Failure("Listmonk.HttpError", ex.Message); } } @@ -479,7 +486,7 @@ private async Task PutAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", endpoint); + _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", SanitizeEndpointForLog(endpoint)); return Error.Failure("Listmonk.HttpError", ex.Message); } } @@ -501,7 +508,7 @@ private async Task DeleteAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", endpoint); + _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", SanitizeEndpointForLog(endpoint)); return Error.Failure("Listmonk.HttpError", ex.Message); } } From 0efaea23ba425dd03de62705e18f84559f7b1e89 Mon Sep 17 00:00:00 2001 From: sacha Date: Sat, 25 Apr 2026 07:17:07 +0200 Subject: [PATCH 3/3] fix(security): drop endpoint param from HTTP client error logs Refs POM-178. CodeQL doesn't recognize SanitizeEndpointForLog as a sanitizer and still flags taint propagation. Replace endpoint-bearing logs with fixed method-name strings to break the data flow entirely. Also closes 2 new clear-text-storage alerts on licenseKeyId (same root cause: endpoint is built from licenseKeyId in the License Get path). --- .../Http/LemonSqueezyHttpClient.cs | 19 ++++++----------- .../Http/ListmonkHttpClient.cs | 21 +++++++------------ 2 files changed, 13 insertions(+), 27 deletions(-) diff --git a/src/Adapters/Compendium.Adapters.LemonSqueezy/Http/LemonSqueezyHttpClient.cs b/src/Adapters/Compendium.Adapters.LemonSqueezy/Http/LemonSqueezyHttpClient.cs index 42ca439..ff91629 100644 --- a/src/Adapters/Compendium.Adapters.LemonSqueezy/Http/LemonSqueezyHttpClient.cs +++ b/src/Adapters/Compendium.Adapters.LemonSqueezy/Http/LemonSqueezyHttpClient.cs @@ -23,13 +23,6 @@ internal sealed class LemonSqueezyHttpClient private readonly ILogger _logger; private readonly JsonSerializerOptions _jsonOptions; - private static string SanitizeEndpointForLog(string endpoint) - { - if (string.IsNullOrEmpty(endpoint)) return endpoint ?? string.Empty; - var qIdx = endpoint.IndexOf('?'); - return qIdx >= 0 ? endpoint[..qIdx] : endpoint; - } - /// /// Initializes a new instance of the class. /// @@ -289,7 +282,7 @@ private async Task>> GetResourceAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling LemonSqueezy API: {Endpoint}", SanitizeEndpointForLog(endpoint)); + _logger.LogError(ex, "HTTP error in LemonSqueezy GetResourceAsync"); return Error.Failure("LemonSqueezy.HttpError", ex.Message); } } @@ -314,7 +307,7 @@ private async Task>>> GetCollectionAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling LemonSqueezy API: {Endpoint}", SanitizeEndpointForLog(endpoint)); + _logger.LogError(ex, "HTTP error in LemonSqueezy GetCollectionAsync"); return Error.Failure("LemonSqueezy.HttpError", ex.Message); } } @@ -350,7 +343,7 @@ private async Task>> PostResourceAsync>> PatchResourceAsync DeleteAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling LemonSqueezy API: {Endpoint}", SanitizeEndpointForLog(endpoint)); + _logger.LogError(ex, "HTTP error in LemonSqueezy DeleteAsync"); return Error.Failure("LemonSqueezy.HttpError", ex.Message); } } @@ -439,7 +432,7 @@ private async Task> PostLicenseApiAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling LemonSqueezy License API: {Endpoint}", SanitizeEndpointForLog(endpoint)); + _logger.LogError(ex, "HTTP error in LemonSqueezy License PostAsync"); return Error.Failure("LemonSqueezy.HttpError", ex.Message); } } diff --git a/src/Adapters/Compendium.Adapters.Listmonk/Http/ListmonkHttpClient.cs b/src/Adapters/Compendium.Adapters.Listmonk/Http/ListmonkHttpClient.cs index f3e751b..d0f0476 100644 --- a/src/Adapters/Compendium.Adapters.Listmonk/Http/ListmonkHttpClient.cs +++ b/src/Adapters/Compendium.Adapters.Listmonk/Http/ListmonkHttpClient.cs @@ -23,13 +23,6 @@ internal sealed class ListmonkHttpClient private readonly ILogger _logger; private readonly JsonSerializerOptions _jsonOptions; - private static string SanitizeEndpointForLog(string endpoint) - { - if (string.IsNullOrEmpty(endpoint)) return endpoint ?? string.Empty; - var qIdx = endpoint.IndexOf('?'); - return qIdx >= 0 ? endpoint[..qIdx] : endpoint; - } - /// /// Initializes a new instance of the class. /// @@ -348,7 +341,7 @@ private async Task> GetAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", SanitizeEndpointForLog(endpoint)); + _logger.LogError(ex, "HTTP error in Listmonk GetAsync"); return Error.Failure("Listmonk.HttpError", ex.Message); } } @@ -373,7 +366,7 @@ private async Task>> GetListAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", SanitizeEndpointForLog(endpoint)); + _logger.LogError(ex, "HTTP error in Listmonk GetListAsync"); return Error.Failure("Listmonk.HttpError", ex.Message); } } @@ -398,7 +391,7 @@ private async Task>> GetPaginatedAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", SanitizeEndpointForLog(endpoint)); + _logger.LogError(ex, "HTTP error in Listmonk GetPaginatedAsync"); return Error.Failure("Listmonk.HttpError", ex.Message); } } @@ -430,7 +423,7 @@ private async Task> PostAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", SanitizeEndpointForLog(endpoint)); + _logger.LogError(ex, "HTTP error in Listmonk PostAsync"); return Error.Failure("Listmonk.HttpError", ex.Message); } } @@ -462,7 +455,7 @@ private async Task> PutAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", SanitizeEndpointForLog(endpoint)); + _logger.LogError(ex, "HTTP error in Listmonk PutAsync"); return Error.Failure("Listmonk.HttpError", ex.Message); } } @@ -486,7 +479,7 @@ private async Task PutAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", SanitizeEndpointForLog(endpoint)); + _logger.LogError(ex, "HTTP error in Listmonk PutAsync (no response)"); return Error.Failure("Listmonk.HttpError", ex.Message); } } @@ -508,7 +501,7 @@ private async Task DeleteAsync( } catch (HttpRequestException ex) { - _logger.LogError(ex, "HTTP error calling Listmonk API: {Endpoint}", SanitizeEndpointForLog(endpoint)); + _logger.LogError(ex, "HTTP error in Listmonk DeleteAsync"); return Error.Failure("Listmonk.HttpError", ex.Message); } }