feat: Add initial implementation of Vulnerability Resolver Jobs
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Created project for StellaOps.Scanner.Analyzers.Native.Tests with necessary dependencies.
- Documented roles and guidelines in AGENTS.md for Scheduler module.
- Implemented IResolverJobService interface and InMemoryResolverJobService for handling resolver jobs.
- Added ResolverBacklogNotifier and ResolverBacklogService for monitoring job metrics.
- Developed API endpoints for managing resolver jobs and retrieving metrics.
- Defined models for resolver job requests and responses.
- Integrated dependency injection for resolver job services.
- Implemented ImpactIndexSnapshot for persisting impact index data.
- Introduced SignalsScoringOptions for configurable scoring weights in reachability scoring.
- Added unit tests for ReachabilityScoringService and RuntimeFactsIngestionService.
- Created dotnet-filter.sh script to handle command-line arguments for dotnet.
- Established nuget-prime project for managing package downloads.
This commit is contained in:
master
2025-11-18 07:52:15 +02:00
parent e69b57d467
commit 8355e2ff75
299 changed files with 13293 additions and 2444 deletions

View File

@@ -0,0 +1,24 @@
namespace StellaOps.Scanner.Analyzers.Lang.Deno.Internal.Runtime;
internal static class DenoPolicySignalEmitter
{
public static IReadOnlyDictionary<string, string> FromTrace(string observationHash, DenoRuntimeTraceMetadata metadata)
{
ArgumentException.ThrowIfNullOrWhiteSpace(observationHash);
ArgumentNullException.ThrowIfNull(metadata);
var signals = new Dictionary<string, string>(StringComparer.Ordinal)
{
["surface.lang.deno.runtime.hash"] = observationHash,
["surface.lang.deno.permissions"] = string.Join(',', metadata.UniquePermissions),
["surface.lang.deno.remote_origins"] = string.Join(',', metadata.RemoteOrigins),
["surface.lang.deno.npm_modules"] = metadata.NpmResolutions.ToString(CultureInfo.InvariantCulture),
["surface.lang.deno.wasm_modules"] = metadata.WasmLoads.ToString(CultureInfo.InvariantCulture),
["surface.lang.deno.dynamic_imports"] = metadata.DynamicImports.ToString(CultureInfo.InvariantCulture),
["surface.lang.deno.module_loads"] = metadata.ModuleLoads.ToString(CultureInfo.InvariantCulture),
["surface.lang.deno.permission_uses"] = metadata.PermissionUses.ToString(CultureInfo.InvariantCulture),
};
return signals;
}
}

View File

@@ -0,0 +1,38 @@
using System.Text.Json.Serialization;
namespace StellaOps.Scanner.Analyzers.Lang.Deno.Internal.Runtime;
internal abstract record DenoRuntimeEvent(
[property: JsonPropertyName("type")] string Type,
[property: JsonPropertyName("ts")] DateTimeOffset Timestamp);
internal sealed record DenoModuleLoadEvent(
DateTimeOffset Ts,
DenoModuleIdentity Module,
string Reason,
IReadOnlyList<string> Permissions,
string? Origin) : DenoRuntimeEvent("deno.module.load", Ts);
internal sealed record DenoPermissionUseEvent(
DateTimeOffset Ts,
string Permission,
DenoModuleIdentity Module,
string Details) : DenoRuntimeEvent("deno.permission.use", Ts);
internal sealed record DenoNpmResolutionEvent(
DateTimeOffset Ts,
string Specifier,
string Package,
string Version,
string Resolved,
bool Exists) : DenoRuntimeEvent("deno.npm.resolution", Ts);
internal sealed record DenoWasmLoadEvent(
DateTimeOffset Ts,
DenoModuleIdentity Module,
string Importer,
string Reason) : DenoRuntimeEvent("deno.wasm.load", Ts);
internal sealed record DenoModuleIdentity(
[property: JsonPropertyName("normalized")] string Normalized,
[property: JsonPropertyName("path_sha256")] string PathSha256);

View File

@@ -0,0 +1,36 @@
using System.Security.Cryptography;
using System.Text;
namespace StellaOps.Scanner.Analyzers.Lang.Deno.Internal.Runtime;
internal static class DenoRuntimePathHasher
{
public static DenoModuleIdentity Create(string rootPath, string absolutePath)
{
ArgumentException.ThrowIfNullOrWhiteSpace(rootPath);
ArgumentException.ThrowIfNullOrWhiteSpace(absolutePath);
var normalized = NormalizeRelative(rootPath, absolutePath);
var sha = ComputeSha256(normalized);
return new DenoModuleIdentity(normalized, sha);
}
private static string NormalizeRelative(string rootPath, string absolutePath)
{
var relative = Path.GetRelativePath(rootPath, absolutePath);
if (string.IsNullOrWhiteSpace(relative) || relative == ".")
{
return ".";
}
return relative.Replace('\\', '/');
}
private static string ComputeSha256(string value)
{
var bytes = Encoding.UTF8.GetBytes(value ?? string.Empty);
using var sha = SHA256.Create();
var hash = sha.ComputeHash(bytes);
return Convert.ToHexString(hash).ToLowerInvariant();
}
}

View File

@@ -0,0 +1,91 @@
using System.Collections.Immutable;
namespace StellaOps.Scanner.Analyzers.Lang.Deno.Internal.Runtime;
/// <summary>
/// Collects runtime events from the Deno harness and emits deterministic NDJSON payloads.
/// </summary>
internal sealed class DenoRuntimeTraceRecorder
{
private readonly List<DenoRuntimeEvent> _events = new();
private readonly string _rootPath;
public DenoRuntimeTraceRecorder(string rootPath)
{
ArgumentException.ThrowIfNullOrWhiteSpace(rootPath);
_rootPath = Path.GetFullPath(rootPath);
}
public void AddModuleLoad(string absoluteModulePath, string reason, IEnumerable<string> permissions, string? origin = null, DateTimeOffset? timestamp = null)
{
var identity = DenoRuntimePathHasher.Create(_rootPath, absoluteModulePath);
var evt = new DenoModuleLoadEvent(
Ts: timestamp ?? DateTimeOffset.UtcNow,
Module: identity,
Reason: reason ?? string.Empty,
Permissions: NormalizePermissions(permissions),
Origin: string.IsNullOrWhiteSpace(origin) ? null : origin);
_events.Add(evt);
}
public void AddPermissionUse(string absoluteModulePath, string permission, string details, DateTimeOffset? timestamp = null)
{
var identity = DenoRuntimePathHasher.Create(_rootPath, absoluteModulePath);
var evt = new DenoPermissionUseEvent(
Ts: timestamp ?? DateTimeOffset.UtcNow,
Permission: permission ?? string.Empty,
Module: identity,
Details: details ?? string.Empty);
_events.Add(evt);
}
public void AddNpmResolution(string specifier, string package, string version, string resolved, bool exists, DateTimeOffset? timestamp = null)
{
_events.Add(new DenoNpmResolutionEvent(
Ts: timestamp ?? DateTimeOffset.UtcNow,
Specifier: specifier ?? string.Empty,
Package: package ?? string.Empty,
Version: version ?? string.Empty,
Resolved: resolved ?? string.Empty,
Exists: exists));
}
public void AddWasmLoad(string absoluteModulePath, string importerRelativePath, string reason, DateTimeOffset? timestamp = null)
{
var identity = DenoRuntimePathHasher.Create(_rootPath, absoluteModulePath);
_events.Add(new DenoWasmLoadEvent(
Ts: timestamp ?? DateTimeOffset.UtcNow,
Module: identity,
Importer: importerRelativePath ?? string.Empty,
Reason: reason ?? string.Empty));
}
public DenoRuntimeTraceSnapshot Build()
{
var (content, hash, metadata) = DenoRuntimeTraceSerializer.Serialize(_events);
return new DenoRuntimeTraceSnapshot(content, hash, metadata);
}
private static IReadOnlyList<string> NormalizePermissions(IEnumerable<string> permissions)
{
if (permissions is null)
{
return Array.Empty<string>();
}
return permissions
.Where(p => !string.IsNullOrWhiteSpace(p))
.Select(p => p.Trim().ToLowerInvariant())
.Distinct(StringComparer.Ordinal)
.OrderBy(p => p, StringComparer.Ordinal)
.ToArray();
}
}
internal sealed record DenoRuntimeTraceSnapshot(
byte[] Content,
string Sha256,
DenoRuntimeTraceMetadata Metadata)
{
public ImmutableArray<byte> ContentImmutable => Content.ToImmutableArray();
}

View File

@@ -0,0 +1,175 @@
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
namespace StellaOps.Scanner.Analyzers.Lang.Deno.Internal.Runtime;
internal static class DenoRuntimeTraceSerializer
{
private static readonly JsonWriterOptions WriterOptions = new()
{
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
Indented = false
};
public static (byte[] Content, string Sha256, DenoRuntimeTraceMetadata Metadata) Serialize(
IEnumerable<DenoRuntimeEvent> events)
{
ArgumentNullException.ThrowIfNull(events);
var ordered = events
.OrderBy(e => e.Timestamp)
.ThenBy(e => e.Type, StringComparer.Ordinal)
.ToArray();
using var stream = new MemoryStream();
using (var writer = new Utf8JsonWriter(stream, WriterOptions))
{
foreach (var evt in ordered)
{
WriteEvent(writer, evt);
writer.Flush();
stream.WriteByte((byte)'\n');
}
}
var bytes = stream.ToArray();
var hash = ComputeSha256(bytes);
var metadata = ComputeMetadata(ordered);
return (bytes, hash, metadata);
}
private static void WriteEvent(Utf8JsonWriter writer, DenoRuntimeEvent evt)
{
writer.WriteStartObject();
writer.WriteString("type", evt.Type);
writer.WriteString("ts", evt.Timestamp.ToUniversalTime());
switch (evt)
{
case DenoModuleLoadEvent e:
WriteModule(writer, e.Module);
writer.WriteString("reason", e.Reason);
writer.WriteStartArray("permissions");
foreach (var p in e.Permissions.OrderBy(p => p, StringComparer.Ordinal))
{
writer.WriteStringValue(p);
}
writer.WriteEndArray();
if (!string.IsNullOrWhiteSpace(e.Origin))
{
writer.WriteString("origin", e.Origin);
}
break;
case DenoPermissionUseEvent e:
writer.WriteString("permission", e.Permission);
WriteModule(writer, e.Module);
if (!string.IsNullOrWhiteSpace(e.Details))
{
writer.WriteString("details", e.Details);
}
break;
case DenoNpmResolutionEvent e:
writer.WriteString("specifier", e.Specifier);
writer.WriteString("package", e.Package);
writer.WriteString("version", e.Version);
writer.WriteString("resolved", e.Resolved);
writer.WriteBoolean("exists", e.Exists);
break;
case DenoWasmLoadEvent e:
WriteModule(writer, e.Module);
writer.WriteString("importer", e.Importer);
writer.WriteString("reason", e.Reason);
break;
default:
throw new InvalidOperationException($"Unsupported runtime event type '{evt.GetType().Name}'.");
}
writer.WriteEndObject();
}
private static void WriteModule(Utf8JsonWriter writer, DenoModuleIdentity module)
{
writer.WritePropertyName("module");
writer.WriteStartObject();
writer.WriteString("normalized", module.Normalized);
writer.WriteString("path_sha256", module.PathSha256);
writer.WriteEndObject();
}
private static string ComputeSha256(byte[] content)
{
using var sha = SHA256.Create();
var hash = sha.ComputeHash(content ?? Array.Empty<byte>());
return Convert.ToHexString(hash).ToLowerInvariant();
}
private static DenoRuntimeTraceMetadata ComputeMetadata(IReadOnlyCollection<DenoRuntimeEvent> events)
{
var moduleLoads = 0;
var permissionUses = 0;
var origins = new HashSet<string>(StringComparer.Ordinal);
var permissions = new HashSet<string>(StringComparer.Ordinal);
var npmResolutions = 0;
var wasmLoads = 0;
var dynamicImports = 0;
foreach (var evt in events)
{
switch (evt)
{
case DenoModuleLoadEvent e:
moduleLoads++;
if (!string.IsNullOrWhiteSpace(e.Origin))
{
origins.Add(e.Origin!);
}
if (string.Equals(e.Reason, "dynamic-import", StringComparison.Ordinal))
{
dynamicImports++;
}
foreach (var p in e.Permissions ?? Array.Empty<string>())
{
if (!string.IsNullOrWhiteSpace(p))
{
permissions.Add(p.Trim().ToLowerInvariant());
}
}
break;
case DenoPermissionUseEvent:
permissionUses++;
break;
case DenoNpmResolutionEvent:
npmResolutions++;
break;
case DenoWasmLoadEvent:
wasmLoads++;
break;
}
}
return new DenoRuntimeTraceMetadata(
EventCount: events.Count,
ModuleLoads: moduleLoads,
PermissionUses: permissionUses,
RemoteOrigins: origins.OrderBy(o => o, StringComparer.Ordinal).ToArray(),
UniquePermissions: permissions.OrderBy(p => p, StringComparer.Ordinal).ToArray(),
NpmResolutions: npmResolutions,
WasmLoads: wasmLoads,
DynamicImports: dynamicImports);
}
}
internal sealed record DenoRuntimeTraceMetadata(
int EventCount,
int ModuleLoads,
int PermissionUses,
IReadOnlyList<string> RemoteOrigins,
IReadOnlyList<string> UniquePermissions,
int NpmResolutions,
int WasmLoads,
int DynamicImports);