Round #349

08 May 2026, 17:09· csharp· claude-sonnet-4-6· r13-clean-hints
Complexity
band 0
ZP / UO / OBS
5 / 4 / 1
Journey weight
612
Lightshift ΔL
+0.17

Prompt

Implement a webhook delivery endpoint with HMAC signature verification

Zero-point index (what the engine surfaced)

ZPI-ZPI-0003 | 2026-05-08T17:09:33Z Implement a webhook delivery endpoint with HMAC signature verification ZERO POINT — 5 entries (structural invariants) ● hsl(27.69, 100%, 50%) async [feature] ● hsl(145.64, 100%, 27%) Task of T [feature] ● hsl(7.18, 100%, 38%) class [feature] ● hsl(0.00, 100%, 38%) public [feature] ● hsl(84.10, 100%, 38%) namespace [feature] UNOBSERVED — 4 entries (implied truths) ○ hsl(9.23, 100%, 50%) interface [feature] ○ hsl(77.54, 100%, 36%) Routing middleware [function] ○ hsl(83.08, 100%, 42%) ILogger of T injection [function] ○ hsl(31.38, 100%, 42%) Scoped registration [function] OBSERVED — 1 entries (explicit in request) ◉ hsl(98.46, 100%, 28%) with [feature] COMPLEXITY │ 10 entries │ spread: 89° │ band: LOW

Baseline response (no ZPI)

# Webhook Delivery Endpoint with HMAC Signature Verification ## Project Structure ``` WebhookService/ ├── Controllers/ │ └── WebhookController.cs ├── Services/ │ ├── WebhookSignatureService.cs │ ├── WebhookDeliveryService.cs │ └── WebhookRetryService.cs ├── Middleware/ │ └── WebhookSignatureMiddleware.cs ├── Models/ │ ├── WebhookEvent.cs │ ├── WebhookEndpoint.cs │ ├── WebhookDelivery.cs │ └── WebhookResult.cs ├── Filters/ │ └── ValidateWebhookSignatureFilter.cs └── Program.cs ``` ## Models ```csharp // Models/WebhookEvent.cs using System.Text.Json.Serialization; namespace WebhookService.Models; public class WebhookEvent { public string Id { get; set; } = Guid.NewGuid().ToString(); public string EventType { get; set; } = string.Empty; public DateTime Timestamp { get; set; } = DateTime.UtcNow; public int Version { get; set; } = 1; public object Payload { get; set; } = new(); public Dictionary<string, string> Metadata { get; set; } = new(); } public class WebhookPayload<T> { [JsonPropertyName("event_id")] public string EventId { get; set; } = string.Empty; [JsonPropertyName("event_type")] public string EventType { get; set; } = string.Empty; [JsonPropertyName("timestamp")] public DateTime Timestamp { get; set; } [JsonPropertyName("version")] public int Version { get; set; } [JsonPropertyName("data")] public T? Data { get; set; } } ``` ```csharp // Models/WebhookEndpoint.cs namespace WebhookService.Models; public class WebhookEndpoint { public string Id { get; set; } = Guid.NewGuid().ToString(); public string Name { get; set; } = string.Empty; public string Url { get; set; } = string.Empty; public string Secret { get; set; } = string.Empty; public List<string> EventTypes { get; set; } = new(); public bool IsActive { get; set; } = true; public int TimeoutSeconds { get; set; } = 30; public int MaxRetries { get; set; } = 3; public Dictionary<string, string> Headers { get; set; } = new(); public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public DateTime? LastDeliveryAt { get; set; } } ``` ```csharp // Models/WebhookDelivery.cs namespace WebhookService.Models; public enum DeliveryStatus { Pending, InProgress, Succeeded, Failed, Retrying, Abandoned } public class WebhookDelivery { public string Id { get; set; } = Guid.NewGuid().ToString(); public string EndpointId { get; set; } = string.Empty; public string EventId { get; set; } = string.Empty; public string EventType { get; set; } = string.Empty; public string Payload { get; set; } = string.Empty; public string Signature { get; set; } = string.Empty; public DeliveryStatus Status { get; set; } = DeliveryStatus.Pending; public int AttemptCount { get; set; } = 0; public int MaxAttempts { get; set; } = 3; public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public DateTime? LastAttemptAt { get; set; } public DateTime? NextRetryAt { get; set; } public DateTime? CompletedAt { get; set; } public List<DeliveryAttempt> Attempts { get; set; } = new(); } public class DeliveryAttempt { public int AttemptNumber { get; set; } public DateTime AttemptedAt { get; set; } = DateTime.UtcNow; public int? HttpStatusCode { get; set; } public string? ResponseBody { get; set; } public string? ErrorMessage { get; set; } public long DurationMs { get; set; } public bool Succeeded { get; set; } } ``` ```csharp // Models/WebhookResult.cs namespace WebhookService.Models; public class WebhookResult { public bool Success { get; set; } public string Message { get; set; } = string.Empty; public string? DeliveryId { get; set; } public string? EventId { get; set; } public Dictionary<string, object> Details { get; set; } = new(); public static WebhookResult Ok(string message, string? deliveryId = null, string? eventId = null) => new() { Success = true, Message = message, DeliveryId = deliveryId, EventId = eventId }; public static WebhookResult Fail(string message) => new() { Success = false, Message = message }; } public class SignatureVerificationResult { public bool IsValid { get; set; } public string? FailureReason { get; set; } public string? ReceivedSignature { get; set; } public string? ComputedSignature { get; set; } public DateTime VerifiedAt { get; set; } = DateTime.UtcNow; public static SignatureVerificationResult Valid(string received, string computed) => new() { IsValid = true, ReceivedSignature = received, ComputedSignature = computed }; public static SignatureVerificationResult Invalid(string reason, string? received = null, string? computed = null) => new() { IsValid = false, FailureReason = reason, ReceivedSignature = received, ComputedSignature = computed }; } ``` ## Signature Service ```csharp // Services/WebhookSignatureService.cs using System.Security.Cryptography; using System.Text; using Microsoft.Extensions.Options; using WebhookService.Models; namespace WebhookService.Services; public interface IWebhookSignatureService { string ComputeSignature(string payload, string secret, string? timestamp = null); SignatureVerificationResult VerifySignature(string payload, string signature, string secret, string? timestamp = null); string GenerateSecret(int length = 32); bool IsTimestampValid(string timestamp, int toleranceSeconds = 300); } public class WebhookSignatureOptions { public string SignatureHeader { get; set; } = "X-Webhook-Signature"; public string TimestampHeader { get; set; } = "X-Webhook-Timestamp"; public string SignaturePrefix { get; set; } = "sha256="; public int TimestampToleranceSeconds { get; set; } = 300; // 5 minutes public bool RequireTimestamp { get; set; } = true; } public class WebhookSignatureService : IWebhookSignatureService { private readonly WebhookSignatureOptions _options; private readonly ILogger<WebhookSignatureService> _logger; public WebhookSignatureService( IOptions<WebhookSignatureOptions> options, ILogger<WebhookSignatureService> logger) { _options = options.Value; _logger = logger; } /// <summary> /// Computes HMAC-SHA256 signature for the given payload. /// When timestamp is provided, signs "timestamp.payload" to prevent replay attacks. /// </summary> public string ComputeSignature(string payload, string secret, string? timestamp = null) { ArgumentException.ThrowIfNull

Enhanced response (with ZPI)

# Webhook Delivery Endpoint with HMAC Signature Verification ## Project Setup Install the required packages: ```bash dotnet add package Microsoft.AspNetCore.App ``` --- ## Models ```csharp // Models/WebhookPayload.cs namespace WebhookReceiver.Models; public sealed record WebhookPayload( string EventType, string EventId, DateTimeOffset OccurredAt, JsonElement Data ); public sealed record WebhookDeliveryResult( bool Success, string Message, string EventId ); ``` --- ## HMAC Signature Verification Service ```csharp // Services/IWebhookSignatureVerifier.cs namespace WebhookReceiver.Services; public interface IWebhookSignatureVerifier { ValueTask<bool> VerifyAsync(string? signatureHeader, ReadOnlyMemory<byte> body, CancellationToken ct = default); } ``` ```csharp // Services/WebhookSignatureVerifier.cs using System.Security.Cryptography; using System.Text; using Microsoft.Extensions.Options; using WebhookReceiver.Configuration; namespace WebhookReceiver.Services; public sealed class WebhookSignatureVerifier : IWebhookSignatureVerifier { private const string SignaturePrefix = "sha256="; private readonly WebhookOptions _options; private readonly ILogger<WebhookSignatureVerifier> _logger; public WebhookSignatureVerifier( IOptions<WebhookOptions> options, ILogger<WebhookSignatureVerifier> logger) { _options = options.Value; _logger = logger; } public ValueTask<bool> VerifyAsync( string? signatureHeader, ReadOnlyMemory<byte> body, CancellationToken ct = default) { if (string.IsNullOrWhiteSpace(signatureHeader)) { _logger.LogWarning("Webhook request rejected: missing signature header."); return ValueTask.FromResult(false); } if (!signatureHeader.StartsWith(SignaturePrefix, StringComparison.OrdinalIgnoreCase)) { _logger.LogWarning( "Webhook request rejected: unrecognised signature format '{Header}'.", signatureHeader); return ValueTask.FromResult(false); } var providedHex = signatureHeader[SignaturePrefix.Length..]; try { var providedBytes = Convert.FromHexString(providedHex); var secretBytes = Encoding.UTF8.GetBytes(_options.SigningSecret); var computedBytes = HMACSHA256.HashData(secretBytes, body.Span); // Constant-time comparison to prevent timing attacks var match = CryptographicOperations.FixedTimeEquals(providedBytes, computedBytes); if (!match) { _logger.LogWarning("Webhook request rejected: signature mismatch."); } return ValueTask.FromResult(match); } catch (FormatException ex) { _logger.LogWarning(ex, "Webhook request rejected: signature is not valid hex."); return ValueTask.FromResult(false); } } } ``` --- ## Replay Attack Prevention ```csharp // Services/IWebhookReplayGuard.cs namespace WebhookReceiver.Services; public interface IWebhookReplayGuard { /// <summary>Returns false if the event ID has already been processed.</summary> ValueTask<bool> TryMarkSeenAsync(string eventId, CancellationToken ct = default); } ``` ```csharp // Services/InMemoryWebhookReplayGuard.cs using System.Collections.Concurrent; using Microsoft.Extensions.Options; using WebhookReceiver.Configuration; namespace WebhookReceiver.Services; /// <summary> /// Production note: replace with a distributed cache (Redis SETNX) so that /// replay protection works across multiple instances. /// </summary> public sealed class InMemoryWebhookReplayGuard : IWebhookReplayGuard { private readonly ConcurrentDictionary<string, DateTimeOffset> _seen = new(); private readonly WebhookOptions _options; private readonly ILogger<InMemoryWebhookReplayGuard> _logger; public InMemoryWebhookReplayGuard( IOptions<WebhookOptions> options, ILogger<InMemoryWebhookReplayGuard> logger) { _options = options.Value; _logger = logger; } public ValueTask<bool> TryMarkSeenAsync(string eventId, CancellationToken ct = default) { PurgeExpired(); var added = _seen.TryAdd(eventId, DateTimeOffset.UtcNow); if (!added) { _logger.LogWarning("Duplicate webhook event '{EventId}' rejected.", eventId); } return ValueTask.FromResult(added); } private void PurgeExpired() { var cutoff = DateTimeOffset.UtcNow - _options.ReplayWindow; foreach (var (key, ts) in _seen) { if (ts < cutoff) { _seen.TryRemove(key, out _); } } } } ``` --- ## Event Processor ```csharp // Services/IWebhookEventProcessor.cs namespace WebhookReceiver.Services; public interface IWebhookEventProcessor { Task ProcessAsync(WebhookPayload payload, CancellationToken ct = default); } ``` ```csharp // Services/WebhookEventProcessor.cs using WebhookReceiver.Models; namespace WebhookReceiver.Services; public sealed class WebhookEventProcessor : IWebhookEventProcessor { private readonly ILogger<WebhookEventProcessor> _logger; public WebhookEventProcessor(ILogger<WebhookEventProcessor> logger) => _logger = logger; public Task ProcessAsync(WebhookPayload payload, CancellationToken ct = default) { // Dispatch to the appropriate handler by event type return payload.EventType switch { "order.created" => HandleOrderCreatedAsync(payload, ct), "payment.settled" => HandlePaymentSettledAsync(payload, ct), _ => HandleUnknownEventAsync(payload, ct) }; } private Task HandleOrderCreatedAsync(WebhookPayload payload, CancellationToken ct) { _logger.LogInformation( "Processing order.created event {EventId} occurred at {OccurredAt}.", payload.EventId, payload.OccurredAt); // TODO: real business logic return Task.CompletedTask; } private Task HandlePaymentSettledAsync(WebhookPayload payload, CancellationToken ct) { _logger.LogInformation( "Processing payment.settled event {EventId}.", payload.EventId); // TODO: real business logic return Task.CompletedTask; } private Task HandleUnknownEventAsync(WebhookPayload payload, CancellationToken ct) { _logger.LogWarning( "Received unknown event type '{EventType}' (id={EventId}). Ignored.", payload.EventType, payload.EventId); return Task.CompletedTask; } } ``` --- ## Configuration ```csharp // Configuration/WebhookOptions.cs using System.ComponentModel.DataAnnotations; namespace WebhookReceiver.Configuration; public sealed class WebhookOptions { public const string SectionName = "Webhook"; [Required, MinLength(32)] public string SigningSecret { get; init; } = string.Empty; /// <summary>Reject events older than this window (replay protection).</summary> public TimeSpan ReplayWindow { get; init; } =

Grader chain

Each grader reads the same baseline + enhanced pair and casts an independent verdict. Disagreement is the framework's own signal — not noise to be averaged away.

V1claude-sonnet-4-6
Better
V4qwen2.5-coder-7b-instruct
Better

Comments

Our grader said what it said. What do you say? Comment as a guest below.

No comments yet. Be the first to say what you make of this round.

← back to all rounds