audit, advisories and doctors/setup work

This commit is contained in:
master
2026-01-13 18:53:39 +02:00
parent 9ca7cb183e
commit d7be6ba34b
811 changed files with 54242 additions and 4056 deletions

View File

@@ -0,0 +1,163 @@
// <copyright file="ScmWebhookServiceTests.cs" company="Stella Operations">
// Copyright (c) Stella Operations. Licensed under AGPL-3.0-or-later.
// </copyright>
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using StellaOps.Signals.Options;
using StellaOps.Signals.Scm.Models;
using StellaOps.Signals.Scm.Services;
using StellaOps.Signals.Scm.Webhooks;
using Xunit;
namespace StellaOps.Signals.Tests.Scm;
/// <summary>
/// Unit tests for SCM webhook processing.
/// </summary>
public sealed class ScmWebhookServiceTests
{
private static readonly DateTimeOffset FixedTimestamp =
DateTimeOffset.Parse("2025-01-01T00:00:00Z", CultureInfo.InvariantCulture);
[Fact]
public async Task ProcessAsync_RejectsWhenSecretMissingAndUnsignedNotAllowed()
{
var options = new SignalsOptions();
options.Scm.AllowUnsignedWebhooks = false;
var triggerService = new TestScmTriggerService();
var service = CreateService(options, triggerService);
var payload = Encoding.UTF8.GetBytes("{}");
var result = await service.ProcessAsync(
ScmProvider.GitHub,
eventType: "push",
deliveryId: "delivery-1",
signature: null,
payload: payload,
integrationId: null,
tenantId: null);
Assert.False(result.Success);
Assert.Equal(401, result.StatusCode);
Assert.Equal(0, triggerService.Calls);
}
[Fact]
public async Task ProcessAsync_AllowsUnsignedWhenConfigured()
{
var options = new SignalsOptions();
options.Scm.AllowUnsignedWebhooks = true;
var triggerService = new TestScmTriggerService();
var service = CreateService(options, triggerService);
var payload = Encoding.UTF8.GetBytes("{}");
var result = await service.ProcessAsync(
ScmProvider.GitHub,
eventType: "push",
deliveryId: "delivery-2",
signature: null,
payload: payload,
integrationId: "integration-1",
tenantId: "tenant-1");
Assert.True(result.Success);
Assert.Equal(202, result.StatusCode);
Assert.Equal(1, triggerService.Calls);
Assert.Equal("integration-1", result.Event?.IntegrationId);
Assert.Equal("tenant-1", result.Event?.TenantId);
}
[Fact]
public async Task ProcessAsync_ValidSignature_AcceptsAndDispatches()
{
var options = new SignalsOptions();
options.Scm.DefaultSecret = "test-secret";
var triggerService = new TestScmTriggerService();
var service = CreateService(options, triggerService);
var payload = Encoding.UTF8.GetBytes("{}");
var signature = ComputeGitHubSignature(payload, options.Scm.DefaultSecret);
var result = await service.ProcessAsync(
ScmProvider.GitHub,
eventType: "push",
deliveryId: "delivery-3",
signature: signature,
payload: payload,
integrationId: null,
tenantId: null);
Assert.True(result.Success);
Assert.Equal(202, result.StatusCode);
Assert.Equal(1, triggerService.Calls);
Assert.Equal("delivery-3", result.Event?.EventId);
}
private static ScmWebhookService CreateService(SignalsOptions options, TestScmTriggerService triggerService)
{
return new ScmWebhookService(
NullLogger<ScmWebhookService>.Instance,
Options.Create(options),
triggerService,
new IWebhookSignatureValidator[] { new GitHubWebhookValidator() },
new IScmEventMapper[] { new TestScmEventMapper(ScmProvider.GitHub) });
}
private static string ComputeGitHubSignature(byte[] payload, string secret)
{
var secretBytes = Encoding.UTF8.GetBytes(secret);
var hash = HMACSHA256.HashData(secretBytes, payload);
return $"sha256={Convert.ToHexStringLower(hash)}";
}
private sealed class TestScmEventMapper : IScmEventMapper
{
public TestScmEventMapper(ScmProvider provider)
{
Provider = provider;
}
public ScmProvider Provider { get; }
public NormalizedScmEvent? Map(string eventType, string deliveryId, JsonElement payload)
{
return new NormalizedScmEvent
{
EventId = deliveryId,
Provider = Provider,
EventType = ScmEventType.Push,
Timestamp = FixedTimestamp,
Repository = new ScmRepository
{
FullName = "stellaops/signals"
}
};
}
}
private sealed class TestScmTriggerService : IScmTriggerService
{
public int Calls { get; private set; }
public Task<ScmTriggerResult> ProcessEventAsync(NormalizedScmEvent scmEvent, CancellationToken cancellationToken = default)
{
Calls++;
return Task.FromResult(new ScmTriggerResult
{
TriggersDispatched = true,
ScanTriggersCount = 1,
SbomTriggersCount = 0
});
}
}
}