feat(ruby): Implement RubyManifestParser for parsing gem groups and dependencies
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

feat(ruby): Add RubyVendorArtifactCollector to collect vendor artifacts

test(deno): Add golden tests for Deno analyzer with various fixtures

test(deno): Create Deno module and package files for testing

test(deno): Implement Deno lock and import map for dependency management

test(deno): Add FFI and worker scripts for Deno testing

feat(ruby): Set up Ruby workspace with Gemfile and dependencies

feat(ruby): Add expected output for Ruby workspace tests

feat(signals): Introduce CallgraphManifest model for signal processing
This commit is contained in:
master
2025-11-10 09:27:03 +02:00
parent 69c59defdc
commit 56c687253f
87 changed files with 2462 additions and 542 deletions

View File

@@ -206,15 +206,22 @@ app.MapGet("/readyz", (SignalsStartupState state, SignalsSealedModeMonitor seale
: Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}).AllowAnonymous();
var fallbackAllowed = !bootstrap.Authority.Enabled || bootstrap.Authority.AllowAnonymousFallback;
var signalsGroup = app.MapGroup("/signals");
signalsGroup.MapGet("/ping", (HttpContext context, SignalsOptions options, SignalsSealedModeMonitor sealedModeMonitor) =>
Program.TryAuthorize(context, requiredScope: SignalsPolicies.Read, fallbackAllowed: options.Authority.AllowAnonymousFallback, out var authFailure) &&
Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure)
? Results.NoContent()
: authFailure ?? sealedFailure ?? Results.Unauthorized()).WithName("SignalsPing");
{
if (!Program.TryAuthorize(context, requiredScope: SignalsPolicies.Read, fallbackAllowed: options.Authority.AllowAnonymousFallback, out var authFailure))
{
return authFailure ?? Results.Unauthorized();
}
if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
{
return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
return Results.NoContent();
}).WithName("SignalsPing");
signalsGroup.MapGet("/status", (HttpContext context, SignalsOptions options, SignalsSealedModeMonitor sealedModeMonitor) =>
{
@@ -245,33 +252,37 @@ signalsGroup.MapPost("/callgraphs", async Task<IResult> (
SignalsSealedModeMonitor sealedModeMonitor,
CancellationToken cancellationToken) =>
{
if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure) ||
!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure))
{
return authFailure ?? sealedFailure ?? Results.Unauthorized();
return authFailure ?? Results.Unauthorized();
}
if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
{
return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
try
{
var result = await ingestionService.IngestAsync(request, cancellationToken).ConfigureAwait(false);
return Results.Accepted($"/signals/callgraphs/{result.CallgraphId}", result);
}
catch (CallgraphIngestionValidationException ex)
{
return Results.BadRequest(new { error = ex.Message });
}
catch (CallgraphParserNotFoundException ex)
{
return Results.BadRequest(new { error = ex.Message });
}
catch (CallgraphParserValidationException ex)
{
return Results.UnprocessableEntity(new { error = ex.Message });
}
catch (FormatException ex)
{
return Results.BadRequest(new { error = ex.Message });
}
try
{
var result = await ingestionService.IngestAsync(request, cancellationToken).ConfigureAwait(false);
return Results.Accepted($"/signals/callgraphs/{result.CallgraphId}", result);
}
catch (CallgraphIngestionValidationException ex)
{
return Results.BadRequest(new { error = ex.Message });
}
catch (CallgraphParserNotFoundException ex)
{
return Results.BadRequest(new { error = ex.Message });
}
catch (CallgraphParserValidationException ex)
{
return Results.UnprocessableEntity(new { error = ex.Message });
}
catch (FormatException ex)
{
return Results.BadRequest(new { error = ex.Message });
}
}).WithName("SignalsCallgraphIngest");
signalsGroup.MapGet("/callgraphs/{callgraphId}", async Task<IResult> (
@@ -282,10 +293,14 @@ signalsGroup.MapGet("/callgraphs/{callgraphId}", async Task<IResult> (
SignalsSealedModeMonitor sealedModeMonitor,
CancellationToken cancellationToken) =>
{
if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var authFailure) ||
!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var authFailure))
{
return authFailure ?? sealedFailure ?? Results.Unauthorized();
return authFailure ?? Results.Unauthorized();
}
if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
{
return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
if (string.IsNullOrWhiteSpace(callgraphId))
@@ -296,7 +311,46 @@ signalsGroup.MapGet("/callgraphs/{callgraphId}", async Task<IResult> (
var document = await callgraphRepository.GetByIdAsync(callgraphId.Trim(), cancellationToken).ConfigureAwait(false);
return document is null ? Results.NotFound() : Results.Ok(document);
}).WithName("SignalsCallgraphGet");
signalsGroup.MapGet("/callgraphs/{callgraphId}/manifest", async Task<IResult> (
HttpContext context,
SignalsOptions options,
string callgraphId,
ICallgraphRepository callgraphRepository,
SignalsSealedModeMonitor sealedModeMonitor,
CancellationToken cancellationToken) =>
{
if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var authFailure))
{
return authFailure ?? Results.Unauthorized();
}
if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
{
return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
if (string.IsNullOrWhiteSpace(callgraphId))
{
return Results.BadRequest(new { error = "callgraphId is required." });
}
var document = await callgraphRepository.GetByIdAsync(callgraphId.Trim(), cancellationToken).ConfigureAwait(false);
if (document is null || string.IsNullOrWhiteSpace(document.Artifact.ManifestPath))
{
return Results.NotFound();
}
var manifestPath = Path.Combine(options.Storage.RootPath, document.Artifact.ManifestPath);
if (!File.Exists(manifestPath))
{
return Results.NotFound(new { error = "manifest not found" });
}
var bytes = await File.ReadAllBytesAsync(manifestPath, cancellationToken).ConfigureAwait(false);
return Results.File(bytes, "application/json");
}).WithName("SignalsCallgraphManifestGet");
signalsGroup.MapPost("/runtime-facts", async Task<IResult> (
HttpContext context,
SignalsOptions options,
@@ -305,10 +359,14 @@ signalsGroup.MapPost("/runtime-facts", async Task<IResult> (
SignalsSealedModeMonitor sealedModeMonitor,
CancellationToken cancellationToken) =>
{
if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure) ||
!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure))
{
return authFailure ?? sealedFailure ?? Results.Unauthorized();
return authFailure ?? Results.Unauthorized();
}
if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
{
return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
try
@@ -325,15 +383,19 @@ signalsGroup.MapPost("/runtime-facts", async Task<IResult> (
signalsGroup.MapPost("/runtime-facts/ndjson", async Task<IResult> (
HttpContext context,
SignalsOptions options,
RuntimeFactsStreamMetadata metadata,
[AsParameters] RuntimeFactsStreamMetadata metadata,
IRuntimeFactsIngestionService ingestionService,
SignalsSealedModeMonitor sealedModeMonitor,
CancellationToken cancellationToken) =>
{
if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure) ||
!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure))
{
return authFailure ?? sealedFailure ?? Results.Unauthorized();
return authFailure ?? Results.Unauthorized();
}
if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
{
return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
if (metadata is null || string.IsNullOrWhiteSpace(metadata.CallgraphId))
@@ -341,13 +403,7 @@ signalsGroup.MapPost("/runtime-facts/ndjson", async Task<IResult> (
return Results.BadRequest(new { error = "callgraphId is required." });
}
var subject = new ReachabilitySubject
{
ScanId = metadata.ScanId,
ImageDigest = metadata.ImageDigest,
Component = metadata.Component,
Version = metadata.Version
};
var subject = metadata.ToSubject();
var isGzip = string.Equals(context.Request.Headers.ContentEncoding, "gzip", StringComparison.OrdinalIgnoreCase);
var events = await RuntimeFactsNdjsonReader.ReadAsync(context.Request.Body, isGzip, cancellationToken).ConfigureAwait(false);
@@ -382,10 +438,14 @@ signalsGroup.MapGet("/facts/{subjectKey}", async Task<IResult> (
SignalsSealedModeMonitor sealedModeMonitor,
CancellationToken cancellationToken) =>
{
if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var authFailure) ||
!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var authFailure))
{
return authFailure ?? sealedFailure ?? Results.Unauthorized();
return authFailure ?? Results.Unauthorized();
}
if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
{
return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
if (string.IsNullOrWhiteSpace(subjectKey))
@@ -405,10 +465,14 @@ signalsGroup.MapPost("/reachability/recompute", async Task<IResult> (
SignalsSealedModeMonitor sealedModeMonitor,
CancellationToken cancellationToken) =>
{
if (!Program.TryAuthorize(context, SignalsPolicies.Admin, options.Authority.AllowAnonymousFallback, out var authFailure) ||
!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
if (!Program.TryAuthorize(context, SignalsPolicies.Admin, options.Authority.AllowAnonymousFallback, out var authFailure))
{
return authFailure ?? sealedFailure ?? Results.Unauthorized();
return authFailure ?? Results.Unauthorized();
}
if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
{
return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
try
@@ -434,26 +498,6 @@ signalsGroup.MapPost("/reachability/recompute", async Task<IResult> (
}
}).WithName("SignalsReachabilityRecompute");
signalsGroup.MapGet("/facts/{subjectKey}", async Task<IResult> (
HttpContext context,
SignalsOptions options,
string subjectKey,
IReachabilityFactRepository factRepository,
CancellationToken cancellationToken) =>
{
if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var failure))
{
return failure ?? Results.Unauthorized();
}
if (string.IsNullOrWhiteSpace(subjectKey))
{
return Results.BadRequest(new { error = "subjectKey is required." });
}
var fact = await factRepository.GetBySubjectAsync(subjectKey.Trim(), cancellationToken).ConfigureAwait(false);
return fact is null ? Results.NotFound() : Results.Ok(fact);
}).WithName("SignalsFactsGet");
app.Run();