Skip to content

Commit 927b353

Browse files
author
Nick Campanini
committed
Sanitize user-provided webhook fields before logging to prevent log forging
1 parent fda823d commit 927b353

File tree

1 file changed

+10
-7
lines changed

1 file changed

+10
-7
lines changed

src/SEBT.Portal.UseCases/IdProofing/ProcessWebhook/ProcessWebhookCommandHandler.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ public class ProcessWebhookCommandHandler(
2626
ILogger<ProcessWebhookCommandHandler> logger)
2727
: ICommandHandler<ProcessWebhookCommand>
2828
{
29+
private static string SanitizeForLogging(string? value) =>
30+
value?.Replace("\r", string.Empty).Replace("\n", string.Empty) ?? string.Empty;
31+
2932
public async Task<Result> Handle(
3033
ProcessWebhookCommand command,
3134
CancellationToken cancellationToken = default)
@@ -54,7 +57,7 @@ public async Task<Result> Handle(
5457
// contract with Socure has changed and all webhooks are being silently dropped.
5558
logger.LogError(
5659
"Webhook received but no challenge found for ReferenceId={ReferenceId}, EvalId={EvalId}",
57-
command.ReferenceId, command.EvalId);
60+
SanitizeForLogging(command.ReferenceId), SanitizeForLogging(command.EvalId));
5861
// Return success to prevent Socure retries — challenge may have been cleaned up
5962
return Result.Success();
6063
}
@@ -64,7 +67,7 @@ public async Task<Result> Handle(
6467
{
6568
logger.LogInformation(
6669
"Webhook event {EventId} already processed for challenge {ChallengeId}",
67-
command.EventId, challenge.PublicId);
70+
SanitizeForLogging(command.EventId), challenge.PublicId);
6871
return Result.Success();
6972
}
7073

@@ -73,7 +76,7 @@ public async Task<Result> Handle(
7376
{
7477
logger.LogWarning(
7578
"Webhook event {EventId} arrived for terminal challenge {ChallengeId} (status: {Status})",
76-
command.EventId, challenge.PublicId, challenge.Status);
79+
SanitizeForLogging(command.EventId), challenge.PublicId, challenge.Status);
7780
return Result.Success();
7881
}
7982

@@ -82,7 +85,7 @@ public async Task<Result> Handle(
8285
{
8386
logger.LogInformation(
8487
"Webhook event {EventId}: intermediate decision {Decision}, challenge {ChallengeId} stays Pending",
85-
command.EventId, command.DocumentDecision, challenge.PublicId);
88+
SanitizeForLogging(command.EventId), SanitizeForLogging(command.DocumentDecision), challenge.PublicId);
8689
return Result.Success();
8790
}
8891

@@ -91,7 +94,7 @@ public async Task<Result> Handle(
9194
{
9295
logger.LogWarning(
9396
"Webhook event {EventId} has unrecognized document decision: {Decision}",
94-
command.EventId, command.DocumentDecision);
97+
SanitizeForLogging(command.EventId), SanitizeForLogging(command.DocumentDecision));
9598
return Result.Success();
9699
}
97100

@@ -124,13 +127,13 @@ public async Task<Result> Handle(
124127
logger.LogInformation(
125128
"Webhook event {EventId}: concurrency conflict on challenge {ChallengeId}, " +
126129
"another thread already processed it",
127-
command.EventId, challenge.PublicId);
130+
SanitizeForLogging(command.EventId), challenge.PublicId);
128131
return Result.Success();
129132
}
130133

131134
logger.LogInformation(
132135
"Webhook event {EventId}: challenge {ChallengeId} transitioned to {Status}",
133-
command.EventId, challenge.PublicId, newStatus);
136+
SanitizeForLogging(command.EventId), challenge.PublicId, newStatus);
134137

135138
// If verified: update user's proofing status and IAL level
136139
if (newStatus == DocVerificationStatus.Verified)

0 commit comments

Comments
 (0)