Add signal contracts for reachability, exploitability, trust, and unknown symbols
- Introduced `ReachabilityState`, `RuntimeHit`, `ExploitabilitySignal`, `ReachabilitySignal`, `SignalEnvelope`, `SignalType`, `TrustSignal`, and `UnknownSymbolSignal` records to define various signal types and their properties. - Implemented JSON serialization attributes for proper data interchange. - Created project files for the new signal contracts library and corresponding test projects. - Added deterministic test fixtures for micro-interaction testing. - Included cryptographic keys for secure operations with cosign.
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.0-rc.2.25502.107" />
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace StellaOps.Microservice.SourceGen;
|
||||
|
||||
/// <summary>
|
||||
/// Placeholder type for the source generator project.
|
||||
/// This will be replaced with actual source generator implementation in a later sprint.
|
||||
/// </summary>
|
||||
public static class Placeholder
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the source generator is not yet implemented.
|
||||
/// </summary>
|
||||
public const string Status = "NotImplemented";
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,24 @@
|
||||
using StellaOps.Router.Common;
|
||||
|
||||
namespace StellaOps.Microservice;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for a router endpoint.
|
||||
/// </summary>
|
||||
public sealed class RouterEndpointConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the router host.
|
||||
/// </summary>
|
||||
public required string Host { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the router port.
|
||||
/// </summary>
|
||||
public required int Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transport type to use.
|
||||
/// </summary>
|
||||
public TransportType TransportType { get; set; } = TransportType.Tcp;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace StellaOps.Microservice;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for registering Stella microservice services.
|
||||
/// </summary>
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds Stella microservice services to the service collection.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection.</param>
|
||||
/// <param name="configure">Action to configure the microservice options.</param>
|
||||
/// <returns>The service collection for chaining.</returns>
|
||||
public static IServiceCollection AddStellaMicroservice(
|
||||
this IServiceCollection services,
|
||||
Action<StellaMicroserviceOptions> configure)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
ArgumentNullException.ThrowIfNull(configure);
|
||||
|
||||
// Stub implementation - will be filled in later sprints
|
||||
services.Configure(configure);
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using StellaOps.Router.Common;
|
||||
|
||||
namespace StellaOps.Microservice;
|
||||
|
||||
/// <summary>
|
||||
/// Options for configuring a Stella microservice.
|
||||
/// </summary>
|
||||
public sealed class StellaMicroserviceOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the service name.
|
||||
/// </summary>
|
||||
public required string ServiceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the semantic version.
|
||||
/// </summary>
|
||||
public required string Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the region where this instance is deployed.
|
||||
/// </summary>
|
||||
public required string Region { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the unique instance identifier.
|
||||
/// </summary>
|
||||
public string InstanceId { get; set; } = Guid.NewGuid().ToString("N");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the router endpoints to connect to.
|
||||
/// At least one router endpoint is required.
|
||||
/// </summary>
|
||||
public List<RouterEndpointConfig> Routers { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the optional path to a YAML config file for endpoint overrides.
|
||||
/// </summary>
|
||||
public string? EndpointConfigPath { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.0-rc.2.25502.107" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Router.Common\StellaOps.Router.Common.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
17
src/__Libraries/StellaOps.Router.Common/ClaimRequirement.cs
Normal file
17
src/__Libraries/StellaOps.Router.Common/ClaimRequirement.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace StellaOps.Router.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a claim requirement for endpoint authorization.
|
||||
/// </summary>
|
||||
public sealed record ClaimRequirement
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the claim type that must be present.
|
||||
/// </summary>
|
||||
public required string Type { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the optional claim value that must match.
|
||||
/// </summary>
|
||||
public string? Value { get; init; }
|
||||
}
|
||||
42
src/__Libraries/StellaOps.Router.Common/ConnectionState.cs
Normal file
42
src/__Libraries/StellaOps.Router.Common/ConnectionState.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
namespace StellaOps.Router.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the state of a connection between a microservice and the router.
|
||||
/// </summary>
|
||||
public sealed class ConnectionState
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique identifier for this connection.
|
||||
/// </summary>
|
||||
public required string ConnectionId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance descriptor for the connected microservice.
|
||||
/// </summary>
|
||||
public required InstanceDescriptor Instance { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the health status of this connection.
|
||||
/// </summary>
|
||||
public InstanceHealthStatus Status { get; set; } = InstanceHealthStatus.Unknown;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the UTC timestamp of the last heartbeat.
|
||||
/// </summary>
|
||||
public DateTime LastHeartbeatUtc { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the average ping time in milliseconds.
|
||||
/// </summary>
|
||||
public double AveragePingMs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the endpoints served by this connection.
|
||||
/// </summary>
|
||||
public Dictionary<(string Method, string Path), EndpointDescriptor> Endpoints { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transport type used for this connection.
|
||||
/// </summary>
|
||||
public required TransportType TransportType { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
namespace StellaOps.Router.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Describes an endpoint's identity and metadata.
|
||||
/// </summary>
|
||||
public sealed record EndpointDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the service that owns this endpoint.
|
||||
/// </summary>
|
||||
public required string ServiceName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the semantic version of the service.
|
||||
/// </summary>
|
||||
public required string Version { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the HTTP method (GET, POST, PUT, PATCH, DELETE).
|
||||
/// </summary>
|
||||
public required string Method { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path template (e.g., "/billing/invoices/{id}").
|
||||
/// </summary>
|
||||
public required string Path { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default timeout for this endpoint.
|
||||
/// </summary>
|
||||
public TimeSpan DefaultTimeout { get; init; } = TimeSpan.FromSeconds(30);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the claim requirements for authorization.
|
||||
/// </summary>
|
||||
public IReadOnlyList<ClaimRequirement> RequiringClaims { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this endpoint supports streaming.
|
||||
/// </summary>
|
||||
public bool SupportsStreaming { get; init; }
|
||||
}
|
||||
22
src/__Libraries/StellaOps.Router.Common/Frame.cs
Normal file
22
src/__Libraries/StellaOps.Router.Common/Frame.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace StellaOps.Router.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a protocol frame in the router transport layer.
|
||||
/// </summary>
|
||||
public sealed record Frame
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the type of this frame.
|
||||
/// </summary>
|
||||
public required FrameType Type { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the correlation ID for request/response matching.
|
||||
/// </summary>
|
||||
public string? CorrelationId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the raw payload bytes.
|
||||
/// </summary>
|
||||
public ReadOnlyMemory<byte> Payload { get; init; }
|
||||
}
|
||||
47
src/__Libraries/StellaOps.Router.Common/FrameType.cs
Normal file
47
src/__Libraries/StellaOps.Router.Common/FrameType.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
namespace StellaOps.Router.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the frame types used in the router protocol.
|
||||
/// </summary>
|
||||
public enum FrameType
|
||||
{
|
||||
/// <summary>
|
||||
/// Initial registration frame sent by microservice.
|
||||
/// </summary>
|
||||
Hello,
|
||||
|
||||
/// <summary>
|
||||
/// Periodic health check frame.
|
||||
/// </summary>
|
||||
Heartbeat,
|
||||
|
||||
/// <summary>
|
||||
/// Request frame containing method, path, headers, and body.
|
||||
/// </summary>
|
||||
Request,
|
||||
|
||||
/// <summary>
|
||||
/// Response frame containing status, headers, and body.
|
||||
/// </summary>
|
||||
Response,
|
||||
|
||||
/// <summary>
|
||||
/// Streaming request data frame.
|
||||
/// </summary>
|
||||
RequestStreamData,
|
||||
|
||||
/// <summary>
|
||||
/// Streaming response data frame.
|
||||
/// </summary>
|
||||
ResponseStreamData,
|
||||
|
||||
/// <summary>
|
||||
/// Cancellation frame for aborting in-flight requests.
|
||||
/// </summary>
|
||||
Cancel,
|
||||
|
||||
/// <summary>
|
||||
/// Optional frame for updating endpoint metadata at runtime.
|
||||
/// </summary>
|
||||
EndpointsUpdate
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
namespace StellaOps.Router.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Provides global routing state derived from all live connections.
|
||||
/// </summary>
|
||||
public interface IGlobalRoutingState
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolves an HTTP request to an endpoint descriptor.
|
||||
/// </summary>
|
||||
/// <param name="method">The HTTP method.</param>
|
||||
/// <param name="path">The request path.</param>
|
||||
/// <returns>The endpoint descriptor, or null if not found.</returns>
|
||||
EndpointDescriptor? ResolveEndpoint(string method, string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all connections that can handle the specified endpoint.
|
||||
/// </summary>
|
||||
/// <param name="serviceName">The service name.</param>
|
||||
/// <param name="version">The service version.</param>
|
||||
/// <param name="method">The HTTP method.</param>
|
||||
/// <param name="path">The request path.</param>
|
||||
/// <returns>The available connection states.</returns>
|
||||
IEnumerable<ConnectionState> GetConnectionsForEndpoint(
|
||||
string serviceName,
|
||||
string version,
|
||||
string method,
|
||||
string path);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a connection and its endpoints.
|
||||
/// </summary>
|
||||
/// <param name="connection">The connection state to register.</param>
|
||||
void RegisterConnection(ConnectionState connection);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a connection from the routing state.
|
||||
/// </summary>
|
||||
/// <param name="connectionId">The connection ID to remove.</param>
|
||||
void UnregisterConnection(string connectionId);
|
||||
}
|
||||
24
src/__Libraries/StellaOps.Router.Common/IRoutingPlugin.cs
Normal file
24
src/__Libraries/StellaOps.Router.Common/IRoutingPlugin.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace StellaOps.Router.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extensibility for routing decisions.
|
||||
/// </summary>
|
||||
public interface IRoutingPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the priority of this plugin. Lower values run first.
|
||||
/// </summary>
|
||||
int Priority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Filters or reorders candidate connections for routing.
|
||||
/// </summary>
|
||||
/// <param name="candidates">The candidate connections.</param>
|
||||
/// <param name="endpoint">The target endpoint.</param>
|
||||
/// <param name="gatewayRegion">The gateway's region.</param>
|
||||
/// <returns>The filtered/reordered connections.</returns>
|
||||
IEnumerable<ConnectionState> Filter(
|
||||
IEnumerable<ConnectionState> candidates,
|
||||
EndpointDescriptor endpoint,
|
||||
string gatewayRegion);
|
||||
}
|
||||
24
src/__Libraries/StellaOps.Router.Common/ITransportClient.cs
Normal file
24
src/__Libraries/StellaOps.Router.Common/ITransportClient.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace StellaOps.Router.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a transport client that connects to routers.
|
||||
/// </summary>
|
||||
public interface ITransportClient : IAsyncDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the transport type for this client.
|
||||
/// </summary>
|
||||
TransportType TransportType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Connects to a router endpoint.
|
||||
/// </summary>
|
||||
/// <param name="host">The router host.</param>
|
||||
/// <param name="port">The router port.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The established connection.</returns>
|
||||
Task<ITransportConnection> ConnectAsync(
|
||||
string host,
|
||||
int port,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace StellaOps.Router.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a bidirectional transport connection.
|
||||
/// </summary>
|
||||
public interface ITransportConnection : IAsyncDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique identifier for this connection.
|
||||
/// </summary>
|
||||
string ConnectionId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the connection is open.
|
||||
/// </summary>
|
||||
bool IsConnected { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sends a frame over the connection.
|
||||
/// </summary>
|
||||
/// <param name="frame">The frame to send.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
ValueTask SendAsync(Frame frame, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Receives the next frame from the connection.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The received frame, or null if connection closed.</returns>
|
||||
ValueTask<Frame?> ReceiveAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Closes the connection gracefully.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
Task CloseAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
45
src/__Libraries/StellaOps.Router.Common/ITransportServer.cs
Normal file
45
src/__Libraries/StellaOps.Router.Common/ITransportServer.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
namespace StellaOps.Router.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a transport server that accepts connections from microservices.
|
||||
/// </summary>
|
||||
public interface ITransportServer : IAsyncDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the transport type for this server.
|
||||
/// </summary>
|
||||
TransportType TransportType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Starts listening for incoming connections.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
Task StartAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Stops accepting new connections.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
Task StopAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a new connection is established.
|
||||
/// </summary>
|
||||
event EventHandler<TransportConnectionEventArgs>? ConnectionEstablished;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a connection is closed.
|
||||
/// </summary>
|
||||
event EventHandler<TransportConnectionEventArgs>? ConnectionClosed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event arguments for transport connection events.
|
||||
/// </summary>
|
||||
public sealed class TransportConnectionEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the connection that triggered the event.
|
||||
/// </summary>
|
||||
public required ITransportConnection Connection { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace StellaOps.Router.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Describes a microservice instance's identity.
|
||||
/// </summary>
|
||||
public sealed record InstanceDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique identifier for this instance.
|
||||
/// </summary>
|
||||
public required string InstanceId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the service.
|
||||
/// </summary>
|
||||
public required string ServiceName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the semantic version of the service.
|
||||
/// </summary>
|
||||
public required string Version { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the region where this instance is deployed.
|
||||
/// </summary>
|
||||
public required string Region { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace StellaOps.Router.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the health status of a microservice instance.
|
||||
/// </summary>
|
||||
public enum InstanceHealthStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Health status is not yet determined.
|
||||
/// </summary>
|
||||
Unknown,
|
||||
|
||||
/// <summary>
|
||||
/// Instance is healthy and can accept requests.
|
||||
/// </summary>
|
||||
Healthy,
|
||||
|
||||
/// <summary>
|
||||
/// Instance is degraded but can still accept requests.
|
||||
/// </summary>
|
||||
Degraded,
|
||||
|
||||
/// <summary>
|
||||
/// Instance is draining and will not accept new requests.
|
||||
/// </summary>
|
||||
Draining,
|
||||
|
||||
/// <summary>
|
||||
/// Instance is unhealthy and should not receive requests.
|
||||
/// </summary>
|
||||
Unhealthy
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
32
src/__Libraries/StellaOps.Router.Common/TransportType.cs
Normal file
32
src/__Libraries/StellaOps.Router.Common/TransportType.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
namespace StellaOps.Router.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the transport types supported for microservice-to-router communication.
|
||||
/// </summary>
|
||||
public enum TransportType
|
||||
{
|
||||
/// <summary>
|
||||
/// In-memory transport for testing and local development.
|
||||
/// </summary>
|
||||
InMemory,
|
||||
|
||||
/// <summary>
|
||||
/// UDP transport for small/bounded payloads.
|
||||
/// </summary>
|
||||
Udp,
|
||||
|
||||
/// <summary>
|
||||
/// TCP transport with length-prefixed framing.
|
||||
/// </summary>
|
||||
Tcp,
|
||||
|
||||
/// <summary>
|
||||
/// TLS/mTLS transport with certificate-based authentication.
|
||||
/// </summary>
|
||||
Tls,
|
||||
|
||||
/// <summary>
|
||||
/// RabbitMQ transport for queue-based communication.
|
||||
/// </summary>
|
||||
RabbitMq
|
||||
}
|
||||
25
src/__Libraries/StellaOps.Router.Config/PayloadLimits.cs
Normal file
25
src/__Libraries/StellaOps.Router.Config/PayloadLimits.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
namespace StellaOps.Router.Config;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for payload and memory limits.
|
||||
/// </summary>
|
||||
public sealed class PayloadLimits
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum request bytes per call.
|
||||
/// Default: 10 MB.
|
||||
/// </summary>
|
||||
public long MaxRequestBytesPerCall { get; set; } = 10 * 1024 * 1024;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum request bytes per connection.
|
||||
/// Default: 100 MB.
|
||||
/// </summary>
|
||||
public long MaxRequestBytesPerConnection { get; set; } = 100 * 1024 * 1024;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum aggregate in-flight bytes across all requests.
|
||||
/// Default: 1 GB.
|
||||
/// </summary>
|
||||
public long MaxAggregateInflightBytes { get; set; } = 1024 * 1024 * 1024;
|
||||
}
|
||||
17
src/__Libraries/StellaOps.Router.Config/RouterConfig.cs
Normal file
17
src/__Libraries/StellaOps.Router.Config/RouterConfig.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace StellaOps.Router.Config;
|
||||
|
||||
/// <summary>
|
||||
/// Root configuration for the router.
|
||||
/// </summary>
|
||||
public sealed class RouterConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the payload limits configuration.
|
||||
/// </summary>
|
||||
public PayloadLimits PayloadLimits { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the service configurations.
|
||||
/// </summary>
|
||||
public List<ServiceConfig> Services { get; set; } = [];
|
||||
}
|
||||
60
src/__Libraries/StellaOps.Router.Config/ServiceConfig.cs
Normal file
60
src/__Libraries/StellaOps.Router.Config/ServiceConfig.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using StellaOps.Router.Common;
|
||||
|
||||
namespace StellaOps.Router.Config;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for a service in the router.
|
||||
/// </summary>
|
||||
public sealed class ServiceConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the service name.
|
||||
/// </summary>
|
||||
public required string ServiceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default version for requests without explicit version.
|
||||
/// </summary>
|
||||
public string? DefaultVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default transport type for this service.
|
||||
/// </summary>
|
||||
public TransportType DefaultTransport { get; set; } = TransportType.Tcp;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the endpoint configurations.
|
||||
/// </summary>
|
||||
public List<EndpointConfig> Endpoints { get; set; } = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for an endpoint in a service.
|
||||
/// </summary>
|
||||
public sealed class EndpointConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the HTTP method.
|
||||
/// </summary>
|
||||
public required string Method { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path template.
|
||||
/// </summary>
|
||||
public required string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default timeout.
|
||||
/// </summary>
|
||||
public TimeSpan? DefaultTimeout { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether streaming is supported.
|
||||
/// </summary>
|
||||
public bool SupportsStreaming { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the claim requirements.
|
||||
/// </summary>
|
||||
public List<ClaimRequirement> RequiringClaims { get; set; } = [];
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Router.Common\StellaOps.Router.Common.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
82
src/__Libraries/StellaOps.Signals.Contracts/AGENTS.md
Normal file
82
src/__Libraries/StellaOps.Signals.Contracts/AGENTS.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# StellaOps.Signals.Contracts
|
||||
|
||||
Shared signal contracts for cross-module signal communication in StellaOps.
|
||||
|
||||
## Purpose
|
||||
|
||||
This library provides the common contracts (interfaces and DTOs) for signal-based communication between StellaOps modules. It enables:
|
||||
|
||||
- **Concelier** to emit reachability and trust signals
|
||||
- **Scanner** to emit entropy and unknown symbol signals
|
||||
- **Policy Engine** to consume all signal types for risk scoring
|
||||
- **Signals service** to aggregate and cache signals
|
||||
- **Authority** to emit trust/provenance signals
|
||||
|
||||
## Signal Types
|
||||
|
||||
| Type | Producer | Description |
|
||||
|------|----------|-------------|
|
||||
| `Reachability` | Concelier, Scanner | Whether vulnerable code paths are reachable |
|
||||
| `Entropy` | Scanner | Code complexity and risk metrics |
|
||||
| `Exploitability` | Concelier | KEV status, EPSS scores, exploit availability |
|
||||
| `Trust` | Authority, Scanner | Publisher reputation, provenance, signatures |
|
||||
| `UnknownSymbol` | Scanner | Unresolved dependencies during analysis |
|
||||
| `Custom` | Any | Extension point for module-specific signals |
|
||||
|
||||
## Usage
|
||||
|
||||
### Emitting Signals
|
||||
|
||||
```csharp
|
||||
public class MySignalProducer
|
||||
{
|
||||
private readonly ISignalEmitter _emitter;
|
||||
private readonly ISignalContext _context;
|
||||
|
||||
public async Task EmitReachabilityAsync(string purl, bool isReachable)
|
||||
{
|
||||
var signal = new ReachabilitySignal
|
||||
{
|
||||
Purl = purl,
|
||||
IsReachable = isReachable,
|
||||
Confidence = 0.95
|
||||
};
|
||||
|
||||
var envelope = _context.CreateReachabilityEnvelope(purl, signal);
|
||||
await _emitter.EmitAsync(envelope);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Consuming Signals
|
||||
|
||||
```csharp
|
||||
public class MySignalConsumer
|
||||
{
|
||||
private readonly ISignalConsumer _consumer;
|
||||
|
||||
public async Task ProcessSignalsAsync(CancellationToken ct)
|
||||
{
|
||||
await foreach (var signal in _consumer.ConsumeAsync(SignalType.Reachability, ct))
|
||||
{
|
||||
// Process signal
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `Microsoft.Extensions.DependencyInjection.Abstractions` — DI registration helpers
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
This library contains only contracts. Actual transport implementations are provided by:
|
||||
|
||||
- `StellaOps.Signals.Nats` — NATS JetStream transport
|
||||
- `StellaOps.Signals.InMemory` — In-memory transport for testing
|
||||
|
||||
## Related
|
||||
|
||||
- [Signal Flow Architecture](../../docs/modules/signals/architecture.md)
|
||||
- [Policy Engine Signals Integration](../../docs/modules/policy/signals.md)
|
||||
@@ -0,0 +1,38 @@
|
||||
namespace StellaOps.Signals.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for consuming signals from the signal bus.
|
||||
/// Implemented by services that process signals.
|
||||
/// </summary>
|
||||
public interface ISignalConsumer
|
||||
{
|
||||
/// <summary>
|
||||
/// Consumes signals from the signal bus as an async enumerable.
|
||||
/// </summary>
|
||||
/// <param name="filterType">Optional signal type to filter by.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Async enumerable of signal envelopes.</returns>
|
||||
IAsyncEnumerable<SignalEnvelope> ConsumeAsync(
|
||||
SignalType? filterType = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the latest signal for a given key.
|
||||
/// </summary>
|
||||
/// <param name="signalKey">The signal key to look up.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The signal envelope if found, null otherwise.</returns>
|
||||
ValueTask<SignalEnvelope?> GetLatestAsync(
|
||||
string signalKey,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all signals for a given PURL.
|
||||
/// </summary>
|
||||
/// <param name="purl">The package URL to look up.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Collection of signal envelopes for the PURL.</returns>
|
||||
ValueTask<IReadOnlyList<SignalEnvelope>> GetByPurlAsync(
|
||||
string purl,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
namespace StellaOps.Signals.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for signal context providing metadata and correlation.
|
||||
/// Used by signal producers to add context to emitted signals.
|
||||
/// </summary>
|
||||
public interface ISignalContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current tenant ID.
|
||||
/// </summary>
|
||||
string? TenantId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current correlation ID for distributed tracing.
|
||||
/// </summary>
|
||||
string? CorrelationId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the service producing signals.
|
||||
/// </summary>
|
||||
string ServiceName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a signal envelope with context metadata.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the signal value.</typeparam>
|
||||
/// <param name="signalKey">Unique key for the signal.</param>
|
||||
/// <param name="signalType">Type of the signal.</param>
|
||||
/// <param name="value">The signal value.</param>
|
||||
/// <returns>A fully populated signal envelope.</returns>
|
||||
SignalEnvelope CreateEnvelope<T>(string signalKey, SignalType signalType, T value) where T : notnull;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a reachability signal envelope.
|
||||
/// </summary>
|
||||
SignalEnvelope CreateReachabilityEnvelope(string purl, ReachabilitySignal signal);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an entropy signal envelope.
|
||||
/// </summary>
|
||||
SignalEnvelope CreateEntropyEnvelope(string purl, EntropySignal signal);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an exploitability signal envelope.
|
||||
/// </summary>
|
||||
SignalEnvelope CreateExploitabilityEnvelope(string cveId, ExploitabilitySignal signal);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a trust signal envelope.
|
||||
/// </summary>
|
||||
SignalEnvelope CreateTrustEnvelope(string purl, TrustSignal signal);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an unknown symbol signal envelope.
|
||||
/// </summary>
|
||||
SignalEnvelope CreateUnknownSymbolEnvelope(string symbolId, UnknownSymbolSignal signal);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace StellaOps.Signals.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for emitting signals to the signal bus.
|
||||
/// Implemented by services that produce signals.
|
||||
/// </summary>
|
||||
public interface ISignalEmitter
|
||||
{
|
||||
/// <summary>
|
||||
/// Emits a single signal to the signal bus.
|
||||
/// </summary>
|
||||
/// <param name="signal">The signal envelope to emit.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
ValueTask EmitAsync(SignalEnvelope signal, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Emits a batch of signals to the signal bus.
|
||||
/// </summary>
|
||||
/// <param name="signals">The signal envelopes to emit.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
ValueTask EmitBatchAsync(IEnumerable<SignalEnvelope> signals, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace StellaOps.Signals.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for registering signal services.
|
||||
/// </summary>
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds signal contracts services to the service collection.
|
||||
/// This is a marker method for modules that want to declare dependency on Signals.Contracts.
|
||||
/// Actual implementations are registered by specific transport libraries.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection.</param>
|
||||
/// <returns>The service collection for chaining.</returns>
|
||||
public static IServiceCollection AddSignalContracts(this IServiceCollection services)
|
||||
{
|
||||
// This method serves as a marker for dependency on Signals.Contracts.
|
||||
// Actual implementations of ISignalEmitter, ISignalConsumer, and ISignalContext
|
||||
// are provided by transport-specific libraries (e.g., NATS, in-memory, etc.)
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a signal emitter implementation to the service collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEmitter">The emitter implementation type.</typeparam>
|
||||
/// <param name="services">The service collection.</param>
|
||||
/// <returns>The service collection for chaining.</returns>
|
||||
public static IServiceCollection AddSignalEmitter<TEmitter>(this IServiceCollection services)
|
||||
where TEmitter : class, ISignalEmitter
|
||||
{
|
||||
services.AddSingleton<ISignalEmitter, TEmitter>();
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a signal consumer implementation to the service collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="TConsumer">The consumer implementation type.</typeparam>
|
||||
/// <param name="services">The service collection.</param>
|
||||
/// <returns>The service collection for chaining.</returns>
|
||||
public static IServiceCollection AddSignalConsumer<TConsumer>(this IServiceCollection services)
|
||||
where TConsumer : class, ISignalConsumer
|
||||
{
|
||||
services.AddSingleton<ISignalConsumer, TConsumer>();
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a signal context implementation to the service collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="TContext">The context implementation type.</typeparam>
|
||||
/// <param name="services">The service collection.</param>
|
||||
/// <returns>The service collection for chaining.</returns>
|
||||
public static IServiceCollection AddSignalContext<TContext>(this IServiceCollection services)
|
||||
where TContext : class, ISignalContext
|
||||
{
|
||||
services.AddScoped<ISignalContext, TContext>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Signals.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Entropy signal from code complexity and risk analysis.
|
||||
/// Measures complexity metrics that may indicate higher risk.
|
||||
/// </summary>
|
||||
public sealed record EntropySignal
|
||||
{
|
||||
/// <summary>
|
||||
/// Package URL (PURL) of the analyzed package.
|
||||
/// </summary>
|
||||
[JsonPropertyName("purl")]
|
||||
public required string Purl { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Overall entropy score (0.0-1.0, higher = more complex/risky).
|
||||
/// </summary>
|
||||
[JsonPropertyName("score")]
|
||||
public required double Score { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Cyclomatic complexity metric.
|
||||
/// </summary>
|
||||
[JsonPropertyName("cyclomaticComplexity")]
|
||||
public double? CyclomaticComplexity { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Ratio of opaque/binary content in the package.
|
||||
/// </summary>
|
||||
[JsonPropertyName("opaqueRatio")]
|
||||
public double OpaqueRatio { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of suspicious patterns detected.
|
||||
/// </summary>
|
||||
[JsonPropertyName("suspiciousPatternCount")]
|
||||
public int SuspiciousPatternCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Total lines of code analyzed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("linesOfCode")]
|
||||
public int? LinesOfCode { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Dependency depth in the tree.
|
||||
/// </summary>
|
||||
[JsonPropertyName("dependencyDepth")]
|
||||
public int? DependencyDepth { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Penalty applied from entropy analysis.
|
||||
/// </summary>
|
||||
[JsonPropertyName("penalty")]
|
||||
public double Penalty { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Top file contributing to entropy (if applicable).
|
||||
/// </summary>
|
||||
[JsonPropertyName("topOffendingFile")]
|
||||
public string? TopOffendingFile { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Signals.Contracts.Evidence;
|
||||
|
||||
/// <summary>
|
||||
/// Single call path from entry point to vulnerable symbol.
|
||||
/// </summary>
|
||||
public sealed record CallPath
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique path identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("pathId")]
|
||||
public required string PathId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Call depth (number of hops).
|
||||
/// </summary>
|
||||
[JsonPropertyName("depth")]
|
||||
public required int Depth { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Path confidence (0.0-1.0).
|
||||
/// </summary>
|
||||
[JsonPropertyName("confidence")]
|
||||
public double Confidence { get; init; } = 1.0;
|
||||
|
||||
/// <summary>
|
||||
/// How the path was discovered.
|
||||
/// </summary>
|
||||
[JsonPropertyName("pathType")]
|
||||
public PathDiscoveryType PathType { get; init; } = PathDiscoveryType.Static;
|
||||
|
||||
/// <summary>
|
||||
/// Entry point for this path.
|
||||
/// </summary>
|
||||
[JsonPropertyName("entryPoint")]
|
||||
public EntryPoint? EntryPoint { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Ordered list of nodes in the path.
|
||||
/// </summary>
|
||||
[JsonPropertyName("nodes")]
|
||||
public required IReadOnlyList<CallPathNode> Nodes { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Edges connecting the nodes.
|
||||
/// </summary>
|
||||
[JsonPropertyName("edges")]
|
||||
public IReadOnlyList<CallEdge> Edges { get; init; } = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// How a call path was discovered.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum PathDiscoveryType
|
||||
{
|
||||
/// <summary>Discovered through static analysis.</summary>
|
||||
Static,
|
||||
|
||||
/// <summary>Observed at runtime.</summary>
|
||||
Dynamic,
|
||||
|
||||
/// <summary>Inferred from other evidence.</summary>
|
||||
Inferred
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Node in a call path.
|
||||
/// </summary>
|
||||
public sealed record CallPathNode
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique node identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("nodeId")]
|
||||
public required string NodeId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Function/method name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("functionName")]
|
||||
public string? FunctionName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Package URL of containing package.
|
||||
/// </summary>
|
||||
[JsonPropertyName("purl")]
|
||||
public string? Purl { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Immutable code identity anchor.
|
||||
/// </summary>
|
||||
[JsonPropertyName("codeAnchor")]
|
||||
public CodeAnchor? CodeAnchor { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this is the vulnerable function.
|
||||
/// </summary>
|
||||
[JsonPropertyName("isVulnerable")]
|
||||
public bool IsVulnerable { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this is an entry point.
|
||||
/// </summary>
|
||||
[JsonPropertyName("isEntryPoint")]
|
||||
public bool IsEntryPoint { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of runtime observations at this node.
|
||||
/// </summary>
|
||||
[JsonPropertyName("runtimeHitCount")]
|
||||
public int RuntimeHitCount { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Edge between call path nodes.
|
||||
/// </summary>
|
||||
public sealed record CallEdge
|
||||
{
|
||||
/// <summary>
|
||||
/// Source node ID.
|
||||
/// </summary>
|
||||
[JsonPropertyName("from")]
|
||||
public required string From { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Target node ID.
|
||||
/// </summary>
|
||||
[JsonPropertyName("to")]
|
||||
public required string To { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Call edge type.
|
||||
/// </summary>
|
||||
[JsonPropertyName("kind")]
|
||||
public required CallEdgeKind Kind { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Edge confidence (0.0-1.0).
|
||||
/// </summary>
|
||||
[JsonPropertyName("confidence")]
|
||||
public double Confidence { get; init; } = 1.0;
|
||||
|
||||
/// <summary>
|
||||
/// Evidence sources (e.g., reloc:.plt.got, bb-target:0x40f0ff).
|
||||
/// </summary>
|
||||
[JsonPropertyName("evidence")]
|
||||
public IReadOnlyList<string> Evidence { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Reason for edge.
|
||||
/// </summary>
|
||||
[JsonPropertyName("reason")]
|
||||
public EdgeReason Reason { get; init; } = EdgeReason.Direct;
|
||||
|
||||
/// <summary>
|
||||
/// Whether edge was revoked/disproven.
|
||||
/// </summary>
|
||||
[JsonPropertyName("revoked")]
|
||||
public bool Revoked { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type of call edge.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum CallEdgeKind
|
||||
{
|
||||
/// <summary>Direct static call.</summary>
|
||||
Static,
|
||||
|
||||
/// <summary>Virtual/polymorphic call.</summary>
|
||||
Virtual,
|
||||
|
||||
/// <summary>Dynamic dispatch.</summary>
|
||||
Dynamic,
|
||||
|
||||
/// <summary>Import/external call.</summary>
|
||||
Import,
|
||||
|
||||
/// <summary>Callback/function pointer.</summary>
|
||||
Callback,
|
||||
|
||||
/// <summary>Reflection-based call.</summary>
|
||||
Reflection
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reason for edge existence.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum EdgeReason
|
||||
{
|
||||
/// <summary>Direct call observed.</summary>
|
||||
Direct,
|
||||
|
||||
/// <summary>Indirect call inferred.</summary>
|
||||
Indirect,
|
||||
|
||||
/// <summary>Observed at runtime.</summary>
|
||||
RuntimeObserved,
|
||||
|
||||
/// <summary>Inferred from other signals.</summary>
|
||||
Inferred,
|
||||
|
||||
/// <summary>Contested/uncertain.</summary>
|
||||
Contested
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Application entry point.
|
||||
/// </summary>
|
||||
public sealed record EntryPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// Entry point identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("entryPointId")]
|
||||
public required string EntryPointId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Entry point type.
|
||||
/// </summary>
|
||||
[JsonPropertyName("type")]
|
||||
public required EntryPointType Type { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Entry point name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("name")]
|
||||
public string? Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// HTTP route pattern if applicable.
|
||||
/// </summary>
|
||||
[JsonPropertyName("route")]
|
||||
public string? Route { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Code identity anchor.
|
||||
/// </summary>
|
||||
[JsonPropertyName("codeAnchor")]
|
||||
public CodeAnchor? CodeAnchor { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Execution phase (load, init, runtime).
|
||||
/// </summary>
|
||||
[JsonPropertyName("phase")]
|
||||
public ExecutionPhase Phase { get; init; } = ExecutionPhase.Runtime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type of application entry point.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum EntryPointType
|
||||
{
|
||||
/// <summary>Main function.</summary>
|
||||
Main,
|
||||
|
||||
/// <summary>HTTP request handler.</summary>
|
||||
HttpHandler,
|
||||
|
||||
/// <summary>Event handler.</summary>
|
||||
EventHandler,
|
||||
|
||||
/// <summary>Scheduled task.</summary>
|
||||
ScheduledTask,
|
||||
|
||||
/// <summary>Init array constructor.</summary>
|
||||
InitArray,
|
||||
|
||||
/// <summary>Class constructor.</summary>
|
||||
Constructor,
|
||||
|
||||
/// <summary>CLI command handler.</summary>
|
||||
CliCommand,
|
||||
|
||||
/// <summary>gRPC method.</summary>
|
||||
GrpcMethod,
|
||||
|
||||
/// <summary>Message queue handler.</summary>
|
||||
MessageHandler,
|
||||
|
||||
/// <summary>Other entry point.</summary>
|
||||
Other
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execution phase.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum ExecutionPhase
|
||||
{
|
||||
/// <summary>Load-time execution.</summary>
|
||||
Load,
|
||||
|
||||
/// <summary>Initialization phase.</summary>
|
||||
Init,
|
||||
|
||||
/// <summary>Runtime execution.</summary>
|
||||
Runtime
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Signals.Contracts.Evidence;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable code identity anchor (code_id) for function-level evidence.
|
||||
/// Anchors every vulnerability finding to an immutable {artifact_digest, code_id} tuple.
|
||||
/// </summary>
|
||||
public sealed record CodeAnchor
|
||||
{
|
||||
/// <summary>
|
||||
/// Binary/source format.
|
||||
/// </summary>
|
||||
[JsonPropertyName("format")]
|
||||
public required CodeFormat Format { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Artifact content digest (sha256:...).
|
||||
/// </summary>
|
||||
[JsonPropertyName("artifactDigest")]
|
||||
public required string ArtifactDigest { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Build ID (.note.gnu.build-id or equivalent).
|
||||
/// </summary>
|
||||
[JsonPropertyName("buildId")]
|
||||
public string? BuildId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Section name (e.g., .text, .init).
|
||||
/// </summary>
|
||||
[JsonPropertyName("section")]
|
||||
public string? Section { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Start address (hex, e.g., 0x4012a0).
|
||||
/// </summary>
|
||||
[JsonPropertyName("startAddress")]
|
||||
public string? StartAddress { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Code block length in bytes.
|
||||
/// </summary>
|
||||
[JsonPropertyName("length")]
|
||||
public int? Length { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional hash of code bytes (blake3:...).
|
||||
/// </summary>
|
||||
[JsonPropertyName("codeBlockHash")]
|
||||
public string? CodeBlockHash { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Symbol information.
|
||||
/// </summary>
|
||||
[JsonPropertyName("symbol")]
|
||||
public SymbolInfo? Symbol { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binary/source format for code anchors.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum CodeFormat
|
||||
{
|
||||
/// <summary>ELF binary format.</summary>
|
||||
ELF,
|
||||
|
||||
/// <summary>Windows PE format.</summary>
|
||||
PE,
|
||||
|
||||
/// <summary>macOS Mach-O format.</summary>
|
||||
MachO,
|
||||
|
||||
/// <summary>Java bytecode.</summary>
|
||||
JVM,
|
||||
|
||||
/// <summary>.NET Common Language Runtime.</summary>
|
||||
CLR,
|
||||
|
||||
/// <summary>WebAssembly.</summary>
|
||||
WASM,
|
||||
|
||||
/// <summary>Source code.</summary>
|
||||
SOURCE
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Symbol information with demangling metadata.
|
||||
/// </summary>
|
||||
public sealed record SymbolInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Mangled symbol name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("mangled")]
|
||||
public string? Mangled { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Demangled/human-readable name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("demangled")]
|
||||
public string? Demangled { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Symbol source (DWARF, PDB, SYM, STABS, EXPORTS, none).
|
||||
/// </summary>
|
||||
[JsonPropertyName("source")]
|
||||
public SymbolSource Source { get; init; } = SymbolSource.None;
|
||||
|
||||
/// <summary>
|
||||
/// Symbol resolution confidence (0.0-1.0).
|
||||
/// </summary>
|
||||
[JsonPropertyName("confidence")]
|
||||
public double Confidence { get; init; } = 1.0;
|
||||
|
||||
/// <summary>
|
||||
/// Inferred source language.
|
||||
/// </summary>
|
||||
[JsonPropertyName("language")]
|
||||
public SourceLanguage? Language { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Symbol debug info source.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum SymbolSource
|
||||
{
|
||||
/// <summary>No symbol info available.</summary>
|
||||
None,
|
||||
|
||||
/// <summary>DWARF debug info.</summary>
|
||||
DWARF,
|
||||
|
||||
/// <summary>Windows PDB file.</summary>
|
||||
PDB,
|
||||
|
||||
/// <summary>Symbol table.</summary>
|
||||
SYM,
|
||||
|
||||
/// <summary>STABS format.</summary>
|
||||
STABS,
|
||||
|
||||
/// <summary>Export table.</summary>
|
||||
EXPORTS
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inferred source language.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum SourceLanguage
|
||||
{
|
||||
Unknown,
|
||||
C,
|
||||
Cpp,
|
||||
Rust,
|
||||
Go,
|
||||
Java,
|
||||
CSharp,
|
||||
Python,
|
||||
JavaScript,
|
||||
TypeScript,
|
||||
Ruby,
|
||||
PHP
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Signals.Contracts.Evidence;
|
||||
|
||||
/// <summary>
|
||||
/// Complete reachability evidence chain with call paths, runtime hits, and attestations.
|
||||
/// Used by CLI 'stella graph explain' and UI explain drawer.
|
||||
/// </summary>
|
||||
public sealed record ReachabilityEvidenceChain
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique evidence chain identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("evidenceId")]
|
||||
public required Guid EvidenceId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of evidence.
|
||||
/// </summary>
|
||||
[JsonPropertyName("evidenceType")]
|
||||
public required EvidenceType EvidenceType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Static call graph analysis evidence.
|
||||
/// </summary>
|
||||
[JsonPropertyName("graphEvidence")]
|
||||
public GraphEvidence? GraphEvidence { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Runtime observation evidence.
|
||||
/// </summary>
|
||||
[JsonPropertyName("runtimeEvidence")]
|
||||
public RuntimeEvidence? RuntimeEvidence { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Immutable code identity anchors.
|
||||
/// </summary>
|
||||
[JsonPropertyName("codeAnchors")]
|
||||
public IReadOnlyList<CodeAnchor> CodeAnchors { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Application entry points analyzed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("entryPoints")]
|
||||
public IReadOnlyList<EntryPoint> EntryPoints { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Unresolved symbols/edges for uncertainty tracking.
|
||||
/// </summary>
|
||||
[JsonPropertyName("unknowns")]
|
||||
public IReadOnlyList<UnknownRecord> Unknowns { get; init; } = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type of evidence.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum EvidenceType
|
||||
{
|
||||
/// <summary>Static call graph only.</summary>
|
||||
CallGraph,
|
||||
|
||||
/// <summary>Runtime trace only.</summary>
|
||||
RuntimeTrace,
|
||||
|
||||
/// <summary>Combination of static and runtime.</summary>
|
||||
Hybrid,
|
||||
|
||||
/// <summary>Manual assessment.</summary>
|
||||
Manual
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static call graph analysis evidence.
|
||||
/// </summary>
|
||||
public sealed record GraphEvidence
|
||||
{
|
||||
/// <summary>
|
||||
/// BLAKE3 hash of canonical graph.
|
||||
/// </summary>
|
||||
[JsonPropertyName("graphHash")]
|
||||
public required string GraphHash { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// CAS URI to graph (cas://reachability/graphs/{hash}).
|
||||
/// </summary>
|
||||
[JsonPropertyName("graphCasUri")]
|
||||
public string? GraphCasUri { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Graph schema version.
|
||||
/// </summary>
|
||||
[JsonPropertyName("graphKind")]
|
||||
public GraphKind GraphKind { get; init; } = GraphKind.RichGraphV1;
|
||||
|
||||
/// <summary>
|
||||
/// Number of nodes in the graph.
|
||||
/// </summary>
|
||||
[JsonPropertyName("nodeCount")]
|
||||
public int NodeCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of edges in the graph.
|
||||
/// </summary>
|
||||
[JsonPropertyName("edgeCount")]
|
||||
public int EdgeCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Percentage of code covered by analysis.
|
||||
/// </summary>
|
||||
[JsonPropertyName("coveragePercent")]
|
||||
public double CoveragePercent { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Analyzer information.
|
||||
/// </summary>
|
||||
[JsonPropertyName("analyzer")]
|
||||
public AnalyzerInfo? Analyzer { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Graph schema version.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum GraphKind
|
||||
{
|
||||
/// <summary>Rich graph v1 schema.</summary>
|
||||
[JsonPropertyName("richgraph-v1")]
|
||||
RichGraphV1,
|
||||
|
||||
/// <summary>Simple call graph v1.</summary>
|
||||
[JsonPropertyName("simplecg-v1")]
|
||||
SimpleCgV1,
|
||||
|
||||
/// <summary>Entry trace v1.</summary>
|
||||
[JsonPropertyName("entry-trace-v1")]
|
||||
EntryTraceV1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runtime observation evidence.
|
||||
/// </summary>
|
||||
public sealed record RuntimeEvidence
|
||||
{
|
||||
/// <summary>
|
||||
/// BLAKE3 hash of trace data.
|
||||
/// </summary>
|
||||
[JsonPropertyName("traceHash")]
|
||||
public string? TraceHash { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// CAS URI to trace data.
|
||||
/// </summary>
|
||||
[JsonPropertyName("traceCasUri")]
|
||||
public string? TraceCasUri { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Observation window start.
|
||||
/// </summary>
|
||||
[JsonPropertyName("observationStart")]
|
||||
public DateTimeOffset? ObservationStart { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Observation window end.
|
||||
/// </summary>
|
||||
[JsonPropertyName("observationEnd")]
|
||||
public DateTimeOffset? ObservationEnd { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Total hit count.
|
||||
/// </summary>
|
||||
[JsonPropertyName("hitCount")]
|
||||
public int HitCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of unique functions hit.
|
||||
/// </summary>
|
||||
[JsonPropertyName("uniqueFunctionsHit")]
|
||||
public int UniqueFunctionsHit { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Runtime probe type.
|
||||
/// </summary>
|
||||
[JsonPropertyName("probeType")]
|
||||
public ProbeType ProbeType { get; init; } = ProbeType.Instrumentation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runtime probe type.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum ProbeType
|
||||
{
|
||||
/// <summary>.NET EventPipe.</summary>
|
||||
EventPipe,
|
||||
|
||||
/// <summary>Java Flight Recorder.</summary>
|
||||
JFR,
|
||||
|
||||
/// <summary>Linux eBPF.</summary>
|
||||
eBPF,
|
||||
|
||||
/// <summary>Code instrumentation.</summary>
|
||||
Instrumentation
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Information about the analyzer that produced evidence.
|
||||
/// </summary>
|
||||
public sealed record AnalyzerInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Analyzer name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("name")]
|
||||
public required string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Analyzer version.
|
||||
/// </summary>
|
||||
[JsonPropertyName("version")]
|
||||
public required string Version { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Analyzer binary/image digest.
|
||||
/// </summary>
|
||||
[JsonPropertyName("digest")]
|
||||
public string? Digest { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Hash of analyzer configuration.
|
||||
/// </summary>
|
||||
[JsonPropertyName("configHash")]
|
||||
public string? ConfigHash { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unresolved symbol or edge for uncertainty tracking.
|
||||
/// </summary>
|
||||
public sealed record UnknownRecord
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of unknown.
|
||||
/// </summary>
|
||||
[JsonPropertyName("unknownType")]
|
||||
public required UnknownType UnknownType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Identifier for the unknown.
|
||||
/// </summary>
|
||||
[JsonPropertyName("identifier")]
|
||||
public required string Identifier { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Context where unknown was encountered.
|
||||
/// </summary>
|
||||
[JsonPropertyName("context")]
|
||||
public string? Context { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Uncertainty tier (U1, U2, U3).
|
||||
/// </summary>
|
||||
[JsonPropertyName("uncertaintyLevel")]
|
||||
public UncertaintyLevel UncertaintyLevel { get; init; } = UncertaintyLevel.U1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type of unknown element.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum UnknownType
|
||||
{
|
||||
/// <summary>Unresolved symbol.</summary>
|
||||
Symbol,
|
||||
|
||||
/// <summary>Unresolved edge.</summary>
|
||||
Edge,
|
||||
|
||||
/// <summary>Unresolved import.</summary>
|
||||
Import,
|
||||
|
||||
/// <summary>Unresolved reference.</summary>
|
||||
Reference
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uncertainty tier.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum UncertaintyLevel
|
||||
{
|
||||
/// <summary>Low uncertainty.</summary>
|
||||
U1,
|
||||
|
||||
/// <summary>Medium uncertainty.</summary>
|
||||
U2,
|
||||
|
||||
/// <summary>High uncertainty.</summary>
|
||||
U3
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Signals.Contracts.Evidence;
|
||||
|
||||
/// <summary>
|
||||
/// Request to explain reachability for a vulnerability/package.
|
||||
/// Used by CLI 'stella graph explain' command.
|
||||
/// </summary>
|
||||
public sealed record ReachabilityExplainRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique request identifier for idempotency.
|
||||
/// </summary>
|
||||
[JsonPropertyName("requestId")]
|
||||
public required Guid RequestId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Correlation ID for tracing.
|
||||
/// </summary>
|
||||
[JsonPropertyName("correlationId")]
|
||||
public string? CorrelationId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Tenant scope.
|
||||
/// </summary>
|
||||
[JsonPropertyName("tenantId")]
|
||||
public required string TenantId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Subject being analyzed for reachability.
|
||||
/// </summary>
|
||||
[JsonPropertyName("subject")]
|
||||
public required ReachabilitySubject Subject { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Options for the explain request.
|
||||
/// </summary>
|
||||
[JsonPropertyName("options")]
|
||||
public ExplainOptions? Options { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subject being analyzed for reachability.
|
||||
/// </summary>
|
||||
public sealed record ReachabilitySubject
|
||||
{
|
||||
/// <summary>
|
||||
/// Package URL of the component.
|
||||
/// </summary>
|
||||
[JsonPropertyName("purl")]
|
||||
public required string Purl { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// CVE identifier if analyzing vulnerability.
|
||||
/// </summary>
|
||||
[JsonPropertyName("cve")]
|
||||
public string? Cve { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Vulnerable function/method name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("vulnerableSymbol")]
|
||||
public string? VulnerableSymbol { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Container image digest.
|
||||
/// </summary>
|
||||
[JsonPropertyName("imageDigest")]
|
||||
public string? ImageDigest { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Scan job identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("scanId")]
|
||||
public string? ScanId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Artifact content digest.
|
||||
/// </summary>
|
||||
[JsonPropertyName("artifactDigest")]
|
||||
public string? ArtifactDigest { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Options for explain request.
|
||||
/// </summary>
|
||||
public sealed record ExplainOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Maximum number of paths to return (1-100, default 10).
|
||||
/// </summary>
|
||||
[JsonPropertyName("maxPaths")]
|
||||
public int MaxPaths { get; init; } = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum call depth to analyze (1-50, default 20).
|
||||
/// </summary>
|
||||
[JsonPropertyName("maxDepth")]
|
||||
public int MaxDepth { get; init; } = 20;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to include runtime hits.
|
||||
/// </summary>
|
||||
[JsonPropertyName("includeRuntimeHits")]
|
||||
public bool IncludeRuntimeHits { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to include unresolved symbols.
|
||||
/// </summary>
|
||||
[JsonPropertyName("includeUnknowns")]
|
||||
public bool IncludeUnknowns { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Only return attested evidence.
|
||||
/// </summary>
|
||||
[JsonPropertyName("requireAttestation")]
|
||||
public bool RequireAttestation { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Output format.
|
||||
/// </summary>
|
||||
[JsonPropertyName("format")]
|
||||
public ExplainOutputFormat Format { get; init; } = ExplainOutputFormat.Json;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Output format for explain results.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum ExplainOutputFormat
|
||||
{
|
||||
/// <summary>JSON format.</summary>
|
||||
Json,
|
||||
|
||||
/// <summary>SARIF format.</summary>
|
||||
Sarif,
|
||||
|
||||
/// <summary>Graphviz DOT format.</summary>
|
||||
Graphviz
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Response containing reachability explanation with call paths and evidence.
|
||||
/// </summary>
|
||||
public sealed record ReachabilityExplainResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Original request identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("requestId")]
|
||||
public required Guid RequestId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Response status.
|
||||
/// </summary>
|
||||
[JsonPropertyName("status")]
|
||||
public required ExplainStatus Status { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Reachability state determination.
|
||||
/// </summary>
|
||||
[JsonPropertyName("reachabilityState")]
|
||||
public ReachabilityState? ReachabilityState { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Evidence chain supporting the determination.
|
||||
/// </summary>
|
||||
[JsonPropertyName("evidenceChain")]
|
||||
public ReachabilityEvidenceChain? EvidenceChain { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Ordered call paths from entry points to vulnerable symbol.
|
||||
/// </summary>
|
||||
[JsonPropertyName("callPaths")]
|
||||
public IReadOnlyList<CallPath> CallPaths { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Runtime observations confirming reachability.
|
||||
/// </summary>
|
||||
[JsonPropertyName("runtimeHits")]
|
||||
public IReadOnlyList<RuntimeHit> RuntimeHits { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// DSSE attestations backing the evidence.
|
||||
/// </summary>
|
||||
[JsonPropertyName("attestations")]
|
||||
public IReadOnlyList<AttestationReference> Attestations { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Analysis metadata.
|
||||
/// </summary>
|
||||
[JsonPropertyName("analysisMetadata")]
|
||||
public AnalysisMetadata? AnalysisMetadata { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Error details if status is ERROR.
|
||||
/// </summary>
|
||||
[JsonPropertyName("error")]
|
||||
public ReachabilityError? Error { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explain response status.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum ExplainStatus
|
||||
{
|
||||
/// <summary>Full explanation available.</summary>
|
||||
Success,
|
||||
|
||||
/// <summary>Partial explanation (some data missing).</summary>
|
||||
Partial,
|
||||
|
||||
/// <summary>Subject not found.</summary>
|
||||
NotFound,
|
||||
|
||||
/// <summary>Error during explanation.</summary>
|
||||
Error
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Analysis metadata.
|
||||
/// </summary>
|
||||
public sealed record AnalysisMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Analysis identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("analysisId")]
|
||||
public Guid? AnalysisId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When analysis started.
|
||||
/// </summary>
|
||||
[JsonPropertyName("startedAt")]
|
||||
public DateTimeOffset? StartedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When analysis completed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("completedAt")]
|
||||
public DateTimeOffset? CompletedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Duration in milliseconds.
|
||||
/// </summary>
|
||||
[JsonPropertyName("durationMs")]
|
||||
public long? DurationMs { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Analyzer information.
|
||||
/// </summary>
|
||||
[JsonPropertyName("analyzer")]
|
||||
public AnalyzerInfo? Analyzer { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether analysis can be replayed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("replayable")]
|
||||
public bool Replayable { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// URI to replay manifest.
|
||||
/// </summary>
|
||||
[JsonPropertyName("replayManifestUri")]
|
||||
public string? ReplayManifestUri { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reachability error details.
|
||||
/// </summary>
|
||||
public sealed record ReachabilityError
|
||||
{
|
||||
/// <summary>
|
||||
/// Error code.
|
||||
/// </summary>
|
||||
[JsonPropertyName("code")]
|
||||
public required ReachabilityErrorCode Code { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable error message.
|
||||
/// </summary>
|
||||
[JsonPropertyName("message")]
|
||||
public required string Message { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Additional error details.
|
||||
/// </summary>
|
||||
[JsonPropertyName("details")]
|
||||
public IDictionary<string, object>? Details { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reachability error codes.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum ReachabilityErrorCode
|
||||
{
|
||||
/// <summary>Subject not found.</summary>
|
||||
SubjectNotFound,
|
||||
|
||||
/// <summary>Call graph not available.</summary>
|
||||
GraphNotAvailable,
|
||||
|
||||
/// <summary>Analysis failed.</summary>
|
||||
AnalysisFailed,
|
||||
|
||||
/// <summary>Attestation invalid.</summary>
|
||||
AttestationInvalid,
|
||||
|
||||
/// <summary>Transparency log unavailable.</summary>
|
||||
TransparencyUnavailable,
|
||||
|
||||
/// <summary>Operation timed out.</summary>
|
||||
Timeout,
|
||||
|
||||
/// <summary>Internal error.</summary>
|
||||
InternalError
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Signals.Contracts.Evidence;
|
||||
|
||||
/// <summary>
|
||||
/// Lattice-based reachability state determination.
|
||||
/// </summary>
|
||||
public sealed record ReachabilityState
|
||||
{
|
||||
/// <summary>
|
||||
/// Reachability lattice state.
|
||||
/// </summary>
|
||||
[JsonPropertyName("state")]
|
||||
public required ReachabilityLatticeState State { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Confidence score (0.0-1.0).
|
||||
/// </summary>
|
||||
[JsonPropertyName("confidence")]
|
||||
public double Confidence { get; init; } = 1.0;
|
||||
|
||||
/// <summary>
|
||||
/// Analysis method used.
|
||||
/// </summary>
|
||||
[JsonPropertyName("analysisMethod")]
|
||||
public AnalysisMethod AnalysisMethod { get; init; } = AnalysisMethod.Static;
|
||||
|
||||
/// <summary>
|
||||
/// Number of paths reaching vulnerable symbol.
|
||||
/// </summary>
|
||||
[JsonPropertyName("callPathCount")]
|
||||
public int CallPathCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum call depth from entry point.
|
||||
/// </summary>
|
||||
[JsonPropertyName("minCallDepth")]
|
||||
public int? MinCallDepth { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum call depth from entry point.
|
||||
/// </summary>
|
||||
[JsonPropertyName("maxCallDepth")]
|
||||
public int? MaxCallDepth { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of runtime observations.
|
||||
/// </summary>
|
||||
[JsonPropertyName("runtimeHitCount")]
|
||||
public int RuntimeHitCount { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reachability lattice states.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum ReachabilityLatticeState
|
||||
{
|
||||
/// <summary>Vulnerable symbol is reachable from entry points.</summary>
|
||||
Reachable,
|
||||
|
||||
/// <summary>Vulnerable symbol is not reachable from any entry point.</summary>
|
||||
Unreachable,
|
||||
|
||||
/// <summary>Vulnerable symbol may be reachable but analysis is incomplete.</summary>
|
||||
PotentiallyReachable,
|
||||
|
||||
/// <summary>Reachability cannot be determined.</summary>
|
||||
Unknown,
|
||||
|
||||
/// <summary>Awaiting manual review.</summary>
|
||||
UnderReview
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Analysis method used to determine reachability.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum AnalysisMethod
|
||||
{
|
||||
/// <summary>Static analysis only.</summary>
|
||||
Static,
|
||||
|
||||
/// <summary>Dynamic/runtime analysis.</summary>
|
||||
Dynamic,
|
||||
|
||||
/// <summary>Combination of static and dynamic.</summary>
|
||||
Hybrid,
|
||||
|
||||
/// <summary>Runtime observation only.</summary>
|
||||
Runtime,
|
||||
|
||||
/// <summary>Inferred from other signals.</summary>
|
||||
Inferred
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Signals.Contracts.Evidence;
|
||||
|
||||
/// <summary>
|
||||
/// Runtime observation of function execution.
|
||||
/// </summary>
|
||||
public sealed record RuntimeHit
|
||||
{
|
||||
/// <summary>
|
||||
/// Symbol identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("symbolId")]
|
||||
public required string SymbolId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Code identity anchor.
|
||||
/// </summary>
|
||||
[JsonPropertyName("codeAnchor")]
|
||||
public CodeAnchor? CodeAnchor { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of times function was hit.
|
||||
/// </summary>
|
||||
[JsonPropertyName("hitCount")]
|
||||
public required int HitCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When hit was observed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("observedAt")]
|
||||
public required DateTimeOffset ObservedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Runtime loader base address (hex).
|
||||
/// </summary>
|
||||
[JsonPropertyName("loaderBase")]
|
||||
public string? LoaderBase { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Process identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("processId")]
|
||||
public string? ProcessId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Container identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("containerId")]
|
||||
public string? ContainerId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// CAS URI to raw trace data.
|
||||
/// </summary>
|
||||
[JsonPropertyName("casUri")]
|
||||
public string? CasUri { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reference to DSSE attestation.
|
||||
/// </summary>
|
||||
public sealed record AttestationReference
|
||||
{
|
||||
/// <summary>
|
||||
/// in-toto predicate type URI.
|
||||
/// </summary>
|
||||
[JsonPropertyName("predicateType")]
|
||||
public required string PredicateType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Attestation envelope digest.
|
||||
/// </summary>
|
||||
[JsonPropertyName("digest")]
|
||||
public required string Digest { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// CAS URI to attestation.
|
||||
/// </summary>
|
||||
[JsonPropertyName("casUri")]
|
||||
public string? CasUri { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Signer key identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("signerId")]
|
||||
public string? SignerId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When attestation was signed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("signedAt")]
|
||||
public DateTimeOffset? SignedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Rekor transparency log index.
|
||||
/// </summary>
|
||||
[JsonPropertyName("rekorLogIndex")]
|
||||
public long? RekorLogIndex { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reference to call graph artifact.
|
||||
/// </summary>
|
||||
public sealed record CallGraphReference
|
||||
{
|
||||
/// <summary>
|
||||
/// BLAKE3 hash of the graph.
|
||||
/// </summary>
|
||||
[JsonPropertyName("graphHash")]
|
||||
public required string GraphHash { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// CAS URI to graph data.
|
||||
/// </summary>
|
||||
[JsonPropertyName("casUri")]
|
||||
public string? CasUri { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// URI to DSSE envelope.
|
||||
/// </summary>
|
||||
[JsonPropertyName("dsseUri")]
|
||||
public string? DsseUri { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Graph schema version.
|
||||
/// </summary>
|
||||
[JsonPropertyName("kind")]
|
||||
public GraphKind Kind { get; init; } = GraphKind.RichGraphV1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reference to transparency log entry.
|
||||
/// </summary>
|
||||
public sealed record TransparencyLogReference
|
||||
{
|
||||
/// <summary>
|
||||
/// Log identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("logId")]
|
||||
public string? LogId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Entry index in the log.
|
||||
/// </summary>
|
||||
[JsonPropertyName("logIndex")]
|
||||
public long? LogIndex { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When entry was integrated.
|
||||
/// </summary>
|
||||
[JsonPropertyName("integratedTime")]
|
||||
public DateTimeOffset? IntegratedTime { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// URI to the log entry.
|
||||
/// </summary>
|
||||
[JsonPropertyName("entryUri")]
|
||||
public string? EntryUri { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Base64-encoded inclusion proof.
|
||||
/// </summary>
|
||||
[JsonPropertyName("inclusionProof")]
|
||||
public string? InclusionProof { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Signals.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Exploitability signal from KEV and exploit databases.
|
||||
/// Indicates known exploitation status and availability.
|
||||
/// </summary>
|
||||
public sealed record ExploitabilitySignal
|
||||
{
|
||||
/// <summary>
|
||||
/// CVE identifier for the vulnerability.
|
||||
/// </summary>
|
||||
[JsonPropertyName("cveId")]
|
||||
public required string CveId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this vulnerability is in CISA KEV (Known Exploited Vulnerabilities).
|
||||
/// </summary>
|
||||
[JsonPropertyName("inKev")]
|
||||
public bool InKev { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Date added to KEV catalog (if applicable).
|
||||
/// </summary>
|
||||
[JsonPropertyName("kevAddedDate")]
|
||||
public DateTimeOffset? KevAddedDate { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether a public exploit is available.
|
||||
/// </summary>
|
||||
[JsonPropertyName("hasPublicExploit")]
|
||||
public bool HasPublicExploit { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Exploit maturity level (e.g., "poc", "functional", "weaponized").
|
||||
/// </summary>
|
||||
[JsonPropertyName("exploitMaturity")]
|
||||
public string? ExploitMaturity { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// EPSS (Exploit Prediction Scoring System) score (0.0-1.0).
|
||||
/// </summary>
|
||||
[JsonPropertyName("epssScore")]
|
||||
public double? EpssScore { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// EPSS percentile ranking.
|
||||
/// </summary>
|
||||
[JsonPropertyName("epssPercentile")]
|
||||
public double? EpssPercentile { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// References to exploit sources.
|
||||
/// </summary>
|
||||
[JsonPropertyName("exploitReferences")]
|
||||
public IReadOnlyList<string> ExploitReferences { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Last date exploit information was updated.
|
||||
/// </summary>
|
||||
[JsonPropertyName("lastUpdated")]
|
||||
public DateTimeOffset? LastUpdated { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Signals.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Reachability signal from callgraph analysis.
|
||||
/// Indicates whether a vulnerable code path is reachable from application entry points.
|
||||
/// </summary>
|
||||
public sealed record ReachabilitySignal
|
||||
{
|
||||
/// <summary>
|
||||
/// Package URL (PURL) of the analyzed package.
|
||||
/// </summary>
|
||||
[JsonPropertyName("purl")]
|
||||
public required string Purl { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the vulnerable symbol is reachable.
|
||||
/// </summary>
|
||||
[JsonPropertyName("isReachable")]
|
||||
public required bool IsReachable { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Confidence score (0.0-1.0) of the reachability determination.
|
||||
/// </summary>
|
||||
[JsonPropertyName("confidence")]
|
||||
public double Confidence { get; init; } = 1.0;
|
||||
|
||||
/// <summary>
|
||||
/// Number of call paths that reach the vulnerable symbol.
|
||||
/// </summary>
|
||||
[JsonPropertyName("callPathCount")]
|
||||
public int CallPathCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum call depth to reach the vulnerable symbol.
|
||||
/// </summary>
|
||||
[JsonPropertyName("minCallDepth")]
|
||||
public int? MinCallDepth { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// List of entry points that can reach the vulnerable symbol.
|
||||
/// </summary>
|
||||
[JsonPropertyName("reachingEntryPoints")]
|
||||
public IReadOnlyList<string> ReachingEntryPoints { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Analysis method used (e.g., "static", "dynamic", "hybrid").
|
||||
/// </summary>
|
||||
[JsonPropertyName("analysisMethod")]
|
||||
public string AnalysisMethod { get; init; } = "static";
|
||||
|
||||
/// <summary>
|
||||
/// The vulnerable symbol identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("vulnerableSymbol")]
|
||||
public string? VulnerableSymbol { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Signals.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Universal envelope for carrying signal data across modules.
|
||||
/// Provides a common structure for all signal types with metadata.
|
||||
/// </summary>
|
||||
public sealed record SignalEnvelope
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique key identifying this signal (e.g., "pkg:npm/lodash@4.17.21:reachability").
|
||||
/// </summary>
|
||||
[JsonPropertyName("signalKey")]
|
||||
public required string SignalKey { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of signal contained in this envelope.
|
||||
/// </summary>
|
||||
[JsonPropertyName("signalType")]
|
||||
public required SignalType SignalType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The signal payload (type depends on SignalType).
|
||||
/// </summary>
|
||||
[JsonPropertyName("value")]
|
||||
public required object Value { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp when this signal was computed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("computedAt")]
|
||||
public required DateTimeOffset ComputedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the service that produced this signal.
|
||||
/// </summary>
|
||||
[JsonPropertyName("sourceService")]
|
||||
public required string SourceService { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional tenant ID for multi-tenant isolation.
|
||||
/// </summary>
|
||||
[JsonPropertyName("tenantId")]
|
||||
public string? TenantId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional correlation ID for distributed tracing.
|
||||
/// </summary>
|
||||
[JsonPropertyName("correlationId")]
|
||||
public string? CorrelationId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional content digest for provenance verification.
|
||||
/// </summary>
|
||||
[JsonPropertyName("provenanceDigest")]
|
||||
public string? ProvenanceDigest { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional schema version for backward compatibility.
|
||||
/// </summary>
|
||||
[JsonPropertyName("schemaVersion")]
|
||||
public string SchemaVersion { get; init; } = "1.0";
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
namespace StellaOps.Signals.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration of supported signal types in the StellaOps platform.
|
||||
/// </summary>
|
||||
public enum SignalType
|
||||
{
|
||||
/// <summary>
|
||||
/// Reachability signal from callgraph analysis.
|
||||
/// Indicates whether a vulnerable code path is reachable.
|
||||
/// </summary>
|
||||
Reachability,
|
||||
|
||||
/// <summary>
|
||||
/// Entropy signal from code complexity analysis.
|
||||
/// Measures code complexity and associated risk metrics.
|
||||
/// </summary>
|
||||
Entropy,
|
||||
|
||||
/// <summary>
|
||||
/// Exploitability signal from KEV/exploit databases.
|
||||
/// Indicates known exploitation status and availability.
|
||||
/// </summary>
|
||||
Exploitability,
|
||||
|
||||
/// <summary>
|
||||
/// Trust signal from reputation and chain-of-custody scoring.
|
||||
/// Measures publisher/maintainer trustworthiness.
|
||||
/// </summary>
|
||||
Trust,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown symbol signal for unresolved dependencies.
|
||||
/// Flags symbols that could not be resolved during analysis.
|
||||
/// </summary>
|
||||
UnknownSymbol,
|
||||
|
||||
/// <summary>
|
||||
/// Custom signal type for extensibility.
|
||||
/// </summary>
|
||||
Custom
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Signals.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Trust signal from reputation and chain-of-custody scoring.
|
||||
/// Measures publisher/maintainer trustworthiness.
|
||||
/// </summary>
|
||||
public sealed record TrustSignal
|
||||
{
|
||||
/// <summary>
|
||||
/// Package URL (PURL) of the analyzed package.
|
||||
/// </summary>
|
||||
[JsonPropertyName("purl")]
|
||||
public required string Purl { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Overall trust score (0.0-1.0, higher = more trustworthy).
|
||||
/// </summary>
|
||||
[JsonPropertyName("score")]
|
||||
public required double Score { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Publisher/maintainer reputation score.
|
||||
/// </summary>
|
||||
[JsonPropertyName("publisherReputation")]
|
||||
public double? PublisherReputation { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the package has verified provenance.
|
||||
/// </summary>
|
||||
[JsonPropertyName("hasVerifiedProvenance")]
|
||||
public bool HasVerifiedProvenance { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the package is signed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("isSigned")]
|
||||
public bool IsSigned { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Signature type if signed (e.g., "sigstore", "gpg", "npm-provenance").
|
||||
/// </summary>
|
||||
[JsonPropertyName("signatureType")]
|
||||
public string? SignatureType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Age of the package in days since first publish.
|
||||
/// </summary>
|
||||
[JsonPropertyName("packageAgeDays")]
|
||||
public int? PackageAgeDays { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of maintainers.
|
||||
/// </summary>
|
||||
[JsonPropertyName("maintainerCount")]
|
||||
public int? MaintainerCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Community adoption score based on downloads/stars.
|
||||
/// </summary>
|
||||
[JsonPropertyName("adoptionScore")]
|
||||
public double? AdoptionScore { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Known security incidents for this publisher.
|
||||
/// </summary>
|
||||
[JsonPropertyName("publisherIncidentCount")]
|
||||
public int PublisherIncidentCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Trust factors that contributed to the score.
|
||||
/// </summary>
|
||||
[JsonPropertyName("trustFactors")]
|
||||
public IReadOnlyDictionary<string, double> TrustFactors { get; init; } = new Dictionary<string, double>();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Signals.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Unknown symbol signal for unresolved dependencies.
|
||||
/// Flags symbols that could not be resolved during analysis.
|
||||
/// </summary>
|
||||
public sealed record UnknownSymbolSignal
|
||||
{
|
||||
/// <summary>
|
||||
/// The unresolved symbol identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("symbolId")]
|
||||
public required string SymbolId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Programming language of the symbol.
|
||||
/// </summary>
|
||||
[JsonPropertyName("language")]
|
||||
public string? Language { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Expected package containing the symbol.
|
||||
/// </summary>
|
||||
[JsonPropertyName("expectedPackage")]
|
||||
public string? ExpectedPackage { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Reason the symbol could not be resolved.
|
||||
/// </summary>
|
||||
[JsonPropertyName("reason")]
|
||||
public required string Reason { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Severity of the unresolved symbol (e.g., "warning", "error").
|
||||
/// </summary>
|
||||
[JsonPropertyName("severity")]
|
||||
public string Severity { get; init; } = "warning";
|
||||
|
||||
/// <summary>
|
||||
/// File path where the symbol was referenced.
|
||||
/// </summary>
|
||||
[JsonPropertyName("referencingFile")]
|
||||
public string? ReferencingFile { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Line number where the symbol was referenced.
|
||||
/// </summary>
|
||||
[JsonPropertyName("lineNumber")]
|
||||
public int? LineNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Suggested resolutions for this symbol.
|
||||
/// </summary>
|
||||
[JsonPropertyName("suggestedResolutions")]
|
||||
public IReadOnlyList<string> SuggestedResolutions { get; init; } = [];
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Description>Shared signal contracts for cross-module signal communication in StellaOps</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0-rc.2.25502.107" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user