diff --git a/src/Scanner/StellaOps.Scanner.WebService/Endpoints/SourcesEndpoints.cs b/src/Scanner/StellaOps.Scanner.WebService/Endpoints/SourcesEndpoints.cs
index 048cf2d36..65c564b95 100644
--- a/src/Scanner/StellaOps.Scanner.WebService/Endpoints/SourcesEndpoints.cs
+++ b/src/Scanner/StellaOps.Scanner.WebService/Endpoints/SourcesEndpoints.cs
@@ -3,12 +3,14 @@ using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using StellaOps.Auth.Abstractions;
+using StellaOps.Scanner.Sources.Configuration;
using StellaOps.Scanner.Sources.Contracts;
using StellaOps.Scanner.Sources.Domain;
using StellaOps.Scanner.Sources.Services;
using StellaOps.Scanner.WebService.Constants;
using StellaOps.Scanner.WebService.Infrastructure;
using StellaOps.Scanner.WebService.Security;
+using StellaOps.Scanner.WebService.Tenancy;
namespace StellaOps.Scanner.WebService.Endpoints;
diff --git a/src/Scanner/StellaOps.Scanner.WebService/Endpoints/WebhookEndpoints.cs b/src/Scanner/StellaOps.Scanner.WebService/Endpoints/WebhookEndpoints.cs
index 7c98c65fa..719ae01e4 100644
--- a/src/Scanner/StellaOps.Scanner.WebService/Endpoints/WebhookEndpoints.cs
+++ b/src/Scanner/StellaOps.Scanner.WebService/Endpoints/WebhookEndpoints.cs
@@ -15,6 +15,11 @@ using StellaOps.Scanner.WebService.Infrastructure;
namespace StellaOps.Scanner.WebService.Endpoints;
+///
+/// Marker class for webhook endpoint logging.
+///
+internal sealed class WebhookEndpointLogger;
+
///
/// Endpoints for receiving webhooks from container registries and Git providers.
///
@@ -98,7 +103,7 @@ internal static class WebhookEndpoints
IEnumerable handlers,
ISourceTriggerDispatcher dispatcher,
ICredentialResolver credentialResolver,
- ILogger logger,
+ ILogger logger,
HttpContext context,
CancellationToken ct)
{
@@ -140,7 +145,7 @@ internal static class WebhookEndpoints
logger.LogWarning("Webhook received without signature for source {SourceId}", sourceId);
return ProblemResultFactory.Create(
context,
- ProblemTypes.Unauthorized,
+ ProblemTypes.Authentication,
"Missing webhook signature",
StatusCodes.Status401Unauthorized);
}
@@ -164,7 +169,7 @@ internal static class WebhookEndpoints
logger.LogWarning("Invalid webhook signature for source {SourceId}", sourceId);
return ProblemResultFactory.Create(
context,
- ProblemTypes.Unauthorized,
+ ProblemTypes.Authentication,
"Invalid webhook signature",
StatusCodes.Status401Unauthorized);
}
@@ -252,7 +257,7 @@ internal static class WebhookEndpoints
IEnumerable handlers,
ISourceTriggerDispatcher dispatcher,
ICredentialResolver credentialResolver,
- ILogger logger,
+ ILogger logger,
HttpContext context,
CancellationToken ct)
{
@@ -290,7 +295,7 @@ internal static class WebhookEndpoints
IEnumerable handlers,
ISourceTriggerDispatcher dispatcher,
ICredentialResolver credentialResolver,
- ILogger logger,
+ ILogger logger,
HttpContext context,
CancellationToken ct)
{
@@ -337,7 +342,7 @@ internal static class WebhookEndpoints
IEnumerable handlers,
ISourceTriggerDispatcher dispatcher,
ICredentialResolver credentialResolver,
- ILogger logger,
+ ILogger logger,
HttpContext context,
CancellationToken ct)
{
@@ -377,7 +382,7 @@ internal static class WebhookEndpoints
IEnumerable handlers,
ISourceTriggerDispatcher dispatcher,
ICredentialResolver credentialResolver,
- ILogger logger,
+ ILogger logger,
HttpContext context,
CancellationToken ct)
{
@@ -420,7 +425,7 @@ internal static class WebhookEndpoints
IEnumerable handlers,
ISourceTriggerDispatcher dispatcher,
ICredentialResolver credentialResolver,
- ILogger logger,
+ ILogger logger,
HttpContext context,
string signatureHeader,
CancellationToken ct)
@@ -459,7 +464,7 @@ internal static class WebhookEndpoints
logger.LogWarning("Webhook received without signature for source {SourceId}", source.SourceId);
return ProblemResultFactory.Create(
context,
- ProblemTypes.Unauthorized,
+ ProblemTypes.Authentication,
"Missing webhook signature",
StatusCodes.Status401Unauthorized);
}
@@ -483,7 +488,7 @@ internal static class WebhookEndpoints
logger.LogWarning("Invalid webhook signature for source {SourceId}", source.SourceId);
return ProblemResultFactory.Create(
context,
- ProblemTypes.Unauthorized,
+ ProblemTypes.Authentication,
"Invalid webhook signature",
StatusCodes.Status401Unauthorized);
}
diff --git a/src/Scanner/StellaOps.Scanner.WebService/Tenancy/ITenantContext.cs b/src/Scanner/StellaOps.Scanner.WebService/Tenancy/ITenantContext.cs
new file mode 100644
index 000000000..a84e017dc
--- /dev/null
+++ b/src/Scanner/StellaOps.Scanner.WebService/Tenancy/ITenantContext.cs
@@ -0,0 +1,12 @@
+namespace StellaOps.Scanner.WebService.Tenancy;
+
+///
+/// Provides the current tenant context for multi-tenant operations.
+///
+public interface ITenantContext
+{
+ ///
+ /// Gets the current tenant ID.
+ ///
+ string TenantId { get; }
+}
diff --git a/src/Scanner/StellaOps.Scanner.WebService/Tenancy/IUserContext.cs b/src/Scanner/StellaOps.Scanner.WebService/Tenancy/IUserContext.cs
new file mode 100644
index 000000000..02f72d4c1
--- /dev/null
+++ b/src/Scanner/StellaOps.Scanner.WebService/Tenancy/IUserContext.cs
@@ -0,0 +1,17 @@
+namespace StellaOps.Scanner.WebService.Tenancy;
+
+///
+/// Provides the current user context for audit and authorization.
+///
+public interface IUserContext
+{
+ ///
+ /// Gets the current user ID.
+ ///
+ string UserId { get; }
+
+ ///
+ /// Gets the current user's display name.
+ ///
+ string? DisplayName { get; }
+}
diff --git a/src/Scanner/__Libraries/StellaOps.Scanner.Sources/Persistence/SbomSourceRepository.cs b/src/Scanner/__Libraries/StellaOps.Scanner.Sources/Persistence/SbomSourceRepository.cs
index 7c982964e..f458c2d79 100644
--- a/src/Scanner/__Libraries/StellaOps.Scanner.Sources/Persistence/SbomSourceRepository.cs
+++ b/src/Scanner/__Libraries/StellaOps.Scanner.Sources/Persistence/SbomSourceRepository.cs
@@ -122,11 +122,11 @@ public sealed class SbomSourceRepository : RepositoryBase(
+ var totalCount = await ExecuteScalarAsync(
tenantId,
countSb.ToString(),
AddFilters,
- ct)).Value;
+ ct);
string? nextCursor = null;
if (items.Count > request.Limit)
diff --git a/src/Scanner/__Libraries/StellaOps.Scanner.Sources/Persistence/SbomSourceRunRepository.cs b/src/Scanner/__Libraries/StellaOps.Scanner.Sources/Persistence/SbomSourceRunRepository.cs
index 844bec0d5..095b9a1da 100644
--- a/src/Scanner/__Libraries/StellaOps.Scanner.Sources/Persistence/SbomSourceRunRepository.cs
+++ b/src/Scanner/__Libraries/StellaOps.Scanner.Sources/Persistence/SbomSourceRunRepository.cs
@@ -98,12 +98,11 @@ public sealed class SbomSourceRunRepository : RepositoryBase(
+ var totalCount = await ExecuteScalarAsync(
"__system__",
countSb.ToString(),
AddFilters,
ct);
- var totalCount = totalCountResult.GetValueOrDefault();
string? nextCursor = null;
if (items.Count > request.Limit)