up
Some checks failed
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-12-12 09:35:37 +02:00
parent ce5ec9c158
commit efaf3cb789
238 changed files with 146274 additions and 5767 deletions

View File

@@ -0,0 +1,87 @@
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using StellaOps.Signals.Models;
namespace StellaOps.Signals.Persistence;
internal sealed class InMemoryCallgraphRepository : ICallgraphRepository
{
private readonly ConcurrentDictionary<string, CallgraphDocument> _store = new(StringComparer.OrdinalIgnoreCase);
public Task<CallgraphDocument> UpsertAsync(CallgraphDocument document, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(document);
if (string.IsNullOrWhiteSpace(document.Id))
{
document.Id = Guid.NewGuid().ToString("N");
}
_store[document.Id] = Clone(document);
return Task.FromResult(Clone(document));
}
public Task<CallgraphDocument?> GetByIdAsync(string id, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(id))
{
return Task.FromResult<CallgraphDocument?>(null);
}
return Task.FromResult(_store.TryGetValue(id, out var doc) ? Clone(doc) : null);
}
private static CallgraphDocument Clone(CallgraphDocument source) => new()
{
Id = source.Id,
Language = source.Language,
Component = source.Component,
Version = source.Version,
IngestedAt = source.IngestedAt,
Artifact = CloneArtifact(source.Artifact),
Nodes = source.Nodes.Select(CloneNode).ToList(),
Edges = source.Edges.Select(CloneEdge).ToList(),
Metadata = source.Metadata is null ? null : new Dictionary<string, string?>(source.Metadata, StringComparer.OrdinalIgnoreCase),
GraphHash = source.GraphHash,
Roots = source.Roots?.Select(r => new CallgraphRoot(r.Id, r.Phase, r.Source)).ToList(),
SchemaVersion = source.SchemaVersion
};
private static CallgraphArtifactMetadata CloneArtifact(CallgraphArtifactMetadata source) => new()
{
Path = source.Path,
Hash = source.Hash,
CasUri = source.CasUri,
ManifestPath = source.ManifestPath,
ManifestCasUri = source.ManifestCasUri,
GraphHash = source.GraphHash,
ContentType = source.ContentType,
Length = source.Length
};
private static CallgraphNode CloneNode(CallgraphNode source) => new(
source.Id,
source.Name,
source.Kind,
source.Namespace,
source.File,
source.Line,
source.Purl,
source.SymbolDigest,
source.BuildId,
source.Language,
source.Evidence?.ToList(),
source.Analyzer is null ? null : new Dictionary<string, string?>(source.Analyzer, StringComparer.OrdinalIgnoreCase),
source.CodeId);
private static CallgraphEdge CloneEdge(CallgraphEdge source) => new(
source.SourceId,
source.TargetId,
source.Type,
source.Purl,
source.SymbolDigest,
source.Candidates?.ToList(),
source.Confidence,
source.Evidence?.ToList());
}

View File

@@ -0,0 +1,84 @@
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using StellaOps.Signals.Models;
namespace StellaOps.Signals.Persistence;
internal sealed class InMemoryReachabilityFactRepository : IReachabilityFactRepository
{
private readonly ConcurrentDictionary<string, ReachabilityFactDocument> _store = new(StringComparer.OrdinalIgnoreCase);
public Task<ReachabilityFactDocument> UpsertAsync(ReachabilityFactDocument document, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(document);
if (string.IsNullOrWhiteSpace(document.SubjectKey))
{
throw new ArgumentException("Subject key is required.", nameof(document));
}
_store[document.SubjectKey] = Clone(document);
return Task.FromResult(Clone(document));
}
public Task<ReachabilityFactDocument?> GetBySubjectAsync(string subjectKey, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(subjectKey))
{
throw new ArgumentException("Subject key is required.", nameof(subjectKey));
}
return Task.FromResult(_store.TryGetValue(subjectKey, out var doc) ? Clone(doc) : null);
}
private static ReachabilityFactDocument Clone(ReachabilityFactDocument source) => new()
{
Id = source.Id,
CallgraphId = source.CallgraphId,
Subject = source.Subject,
EntryPoints = source.EntryPoints.ToList(),
States = source.States.Select(CloneState).ToList(),
RuntimeFacts = source.RuntimeFacts?.Select(CloneRuntime).ToList(),
Metadata = source.Metadata is null ? null : new Dictionary<string, string?>(source.Metadata, StringComparer.OrdinalIgnoreCase),
ContextFacts = source.ContextFacts,
Score = source.Score,
UnknownsCount = source.UnknownsCount,
UnknownsPressure = source.UnknownsPressure,
ComputedAt = source.ComputedAt,
SubjectKey = source.SubjectKey
};
private static ReachabilityStateDocument CloneState(ReachabilityStateDocument source) => new()
{
Target = source.Target,
Reachable = source.Reachable,
Confidence = source.Confidence,
Bucket = source.Bucket,
Weight = source.Weight,
Score = source.Score,
Path = source.Path.ToList(),
Evidence = new ReachabilityEvidenceDocument
{
RuntimeHits = source.Evidence.RuntimeHits.ToList(),
BlockedEdges = source.Evidence.BlockedEdges?.ToList()
}
};
private static RuntimeFactDocument CloneRuntime(RuntimeFactDocument source) => new()
{
SymbolId = source.SymbolId,
CodeId = source.CodeId,
SymbolDigest = source.SymbolDigest,
Purl = source.Purl,
BuildId = source.BuildId,
LoaderBase = source.LoaderBase,
ProcessId = source.ProcessId,
ProcessName = source.ProcessName,
SocketAddress = source.SocketAddress,
ContainerId = source.ContainerId,
EvidenceUri = source.EvidenceUri,
HitCount = source.HitCount,
ObservedAt = source.ObservedAt,
Metadata = source.Metadata is null ? null : new Dictionary<string, string?>(source.Metadata, StringComparer.OrdinalIgnoreCase)
};
}

View File

@@ -0,0 +1,53 @@
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using StellaOps.Signals.Models;
namespace StellaOps.Signals.Persistence;
public sealed class InMemoryUnknownsRepository : IUnknownsRepository
{
private readonly ConcurrentDictionary<string, List<UnknownSymbolDocument>> _store = new(StringComparer.OrdinalIgnoreCase);
public Task UpsertAsync(string subjectKey, IEnumerable<UnknownSymbolDocument> items, CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrWhiteSpace(subjectKey);
ArgumentNullException.ThrowIfNull(items);
_store[subjectKey] = items.Select(Clone).ToList();
return Task.CompletedTask;
}
public Task<IReadOnlyList<UnknownSymbolDocument>> GetBySubjectAsync(string subjectKey, CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrWhiteSpace(subjectKey);
if (_store.TryGetValue(subjectKey, out var items))
{
return Task.FromResult<IReadOnlyList<UnknownSymbolDocument>>(items.Select(Clone).ToList());
}
return Task.FromResult<IReadOnlyList<UnknownSymbolDocument>>(Array.Empty<UnknownSymbolDocument>());
}
public Task<int> CountBySubjectAsync(string subjectKey, CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrWhiteSpace(subjectKey);
return Task.FromResult(_store.TryGetValue(subjectKey, out var items) ? items.Count : 0);
}
private static UnknownSymbolDocument Clone(UnknownSymbolDocument source) => new()
{
Id = source.Id,
SubjectKey = source.SubjectKey,
CallgraphId = source.CallgraphId,
SymbolId = source.SymbolId,
CodeId = source.CodeId,
Purl = source.Purl,
EdgeFrom = source.EdgeFrom,
EdgeTo = source.EdgeTo,
Reason = source.Reason,
CreatedAt = source.CreatedAt
};
}

View File

@@ -1,59 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using MongoDB.Driver;
using StellaOps.Signals.Models;
namespace StellaOps.Signals.Persistence;
internal sealed class MongoCallgraphRepository : ICallgraphRepository
{
private readonly IMongoCollection<CallgraphDocument> collection;
private readonly ILogger<MongoCallgraphRepository> logger;
public MongoCallgraphRepository(IMongoCollection<CallgraphDocument> collection, ILogger<MongoCallgraphRepository> logger)
{
this.collection = collection ?? throw new ArgumentNullException(nameof(collection));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task<CallgraphDocument> UpsertAsync(CallgraphDocument document, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(document);
var filter = Builders<CallgraphDocument>.Filter.Eq(d => d.Component, document.Component)
& Builders<CallgraphDocument>.Filter.Eq(d => d.Version, document.Version)
& Builders<CallgraphDocument>.Filter.Eq(d => d.Language, document.Language);
if (string.IsNullOrWhiteSpace(document.Id))
{
document.Id = ObjectId.GenerateNewId().ToString();
}
document.IngestedAt = DateTimeOffset.UtcNow;
var options = new ReplaceOptions { IsUpsert = true };
var result = await collection.ReplaceOneAsync(filter, document, options, cancellationToken).ConfigureAwait(false);
if (result.UpsertedId != null)
{
document.Id = result.UpsertedId.AsObjectId.ToString();
}
logger.LogInformation("Upserted callgraph {Language}:{Component}:{Version} (id={Id}).", document.Language, document.Component, document.Version, document.Id);
return document;
}
public async Task<CallgraphDocument?> GetByIdAsync(string id, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(id))
{
throw new ArgumentException("Callgraph id is required.", nameof(id));
}
var filter = Builders<CallgraphDocument>.Filter.Eq(d => d.Id, id);
return await collection.Find(filter).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
}
}

View File

@@ -1,53 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;
using StellaOps.Signals.Models;
namespace StellaOps.Signals.Persistence;
internal sealed class MongoReachabilityFactRepository : IReachabilityFactRepository
{
private readonly IMongoCollection<ReachabilityFactDocument> collection;
private readonly ILogger<MongoReachabilityFactRepository> logger;
public MongoReachabilityFactRepository(
IMongoCollection<ReachabilityFactDocument> collection,
ILogger<MongoReachabilityFactRepository> logger)
{
this.collection = collection ?? throw new ArgumentNullException(nameof(collection));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task<ReachabilityFactDocument> UpsertAsync(ReachabilityFactDocument document, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(document);
if (string.IsNullOrWhiteSpace(document.SubjectKey))
{
throw new ArgumentException("Subject key is required.", nameof(document));
}
var filter = Builders<ReachabilityFactDocument>.Filter.Eq(d => d.SubjectKey, document.SubjectKey);
var options = new ReplaceOptions { IsUpsert = true };
var result = await collection.ReplaceOneAsync(filter, document, options, cancellationToken).ConfigureAwait(false);
if (result.UpsertedId != null)
{
document.Id = result.UpsertedId.AsObjectId.ToString();
}
logger.LogInformation("Upserted reachability fact for subject {SubjectKey} (callgraph={CallgraphId}).", document.SubjectKey, document.CallgraphId);
return document;
}
public async Task<ReachabilityFactDocument?> GetBySubjectAsync(string subjectKey, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(subjectKey))
{
throw new ArgumentException("Subject key is required.", nameof(subjectKey));
}
var filter = Builders<ReachabilityFactDocument>.Filter.Eq(d => d.SubjectKey, subjectKey);
return await collection.Find(filter).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
}
}

View File

@@ -1,53 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
using StellaOps.Signals.Models;
namespace StellaOps.Signals.Persistence;
public sealed class MongoUnknownsRepository : IUnknownsRepository
{
private readonly IMongoCollection<UnknownSymbolDocument> collection;
public MongoUnknownsRepository(IMongoCollection<UnknownSymbolDocument> collection)
{
this.collection = collection ?? throw new ArgumentNullException(nameof(collection));
}
public async Task UpsertAsync(string subjectKey, IEnumerable<UnknownSymbolDocument> items, CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrWhiteSpace(subjectKey);
ArgumentNullException.ThrowIfNull(items);
// deterministic replace per subject to keep the registry stable
await collection.DeleteManyAsync(doc => doc.SubjectKey == subjectKey, cancellationToken).ConfigureAwait(false);
var batch = items.ToList();
if (batch.Count == 0)
{
return;
}
await collection.InsertManyAsync(batch, cancellationToken: cancellationToken).ConfigureAwait(false);
}
public async Task<IReadOnlyList<UnknownSymbolDocument>> GetBySubjectAsync(string subjectKey, CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrWhiteSpace(subjectKey);
var cursor = await collection.FindAsync(doc => doc.SubjectKey == subjectKey, cancellationToken: cancellationToken).ConfigureAwait(false);
return await cursor.ToListAsync(cancellationToken).ConfigureAwait(false);
}
public async Task<int> CountBySubjectAsync(string subjectKey, CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrWhiteSpace(subjectKey);
var count = await collection.CountDocumentsAsync(doc => doc.SubjectKey == subjectKey, cancellationToken: cancellationToken).ConfigureAwait(false);
return (int)count;
}
}