Add inline DSSE provenance documentation and Mongo schema

- Introduced a new document outlining the inline DSSE provenance for SBOM, VEX, scan, and derived events.
- Defined the Mongo schema for event patches, including key fields for provenance and trust verification.
- Documented the write path for ingesting provenance metadata and backfilling historical events.
- Created CI/CD snippets for uploading DSSE attestations and generating provenance metadata.
- Established Mongo indexes for efficient provenance queries and provided query recipes for various use cases.
- Outlined policy gates for managing VEX decisions based on provenance verification.
- Included UI nudges for displaying provenance information and implementation tasks for future enhancements.

---

Implement reachability lattice and scoring model

- Developed a comprehensive document detailing the reachability lattice and scoring model.
- Defined core types for reachability states, evidence, and mitigations with corresponding C# models.
- Established a scoring policy with base score contributions from various evidence classes.
- Mapped reachability states to VEX gates and provided a clear overview of evidence sources.
- Documented the event graph schema for persisting reachability data in MongoDB.
- Outlined the integration of runtime probes for evidence collection and defined a roadmap for future tasks.

---

Introduce uncertainty states and entropy scoring

- Created a draft document for tracking uncertainty states and their impact on risk scoring.
- Defined core uncertainty states with associated entropy values and evidence requirements.
- Established a schema for storing uncertainty states alongside findings.
- Documented the risk score calculation incorporating uncertainty and its effect on final risk assessments.
- Provided policy guidelines for handling uncertainty in decision-making processes.
- Outlined UI guidelines for displaying uncertainty information and suggested remediation actions.

---

Add Ruby package inventory management

- Implemented Ruby package inventory management with corresponding data models and storage mechanisms.
- Created C# records for Ruby package inventory, artifacts, provenance, and runtime details.
- Developed a repository for managing Ruby package inventory documents in MongoDB.
- Implemented a service for storing and retrieving Ruby package inventories.
- Added unit tests for the Ruby package inventory store to ensure functionality and data integrity.
This commit is contained in:
master
2025-11-13 00:20:33 +02:00
parent 86be324fc0
commit 7040984215
41 changed files with 1955 additions and 76 deletions

View File

@@ -515,16 +515,18 @@ public sealed class CommandHandlersTests
var originalExit = Environment.ExitCode;
var backend = new StubBackendClient(new JobTriggerResult(true, "ok", null, null))
{
RubyPackages = new[]
{
CreateRubyPackageArtifact("pkg-rack", "rack", "3.1.0", new[] { "default", "web" }, runtimeUsed: true),
CreateRubyPackageArtifact("pkg-sidekiq", "sidekiq", "7.2.1", groups: null, runtimeUsed: false, metadataOverrides: new Dictionary<string, string?>
RubyInventory = CreateRubyInventory(
"scan-ruby",
new[]
{
["groups"] = "jobs",
["runtime.entrypoints"] = "config/jobs.rb",
["runtime.files"] = "config/jobs.rb"
CreateRubyPackageArtifact("pkg-rack", "rack", "3.1.0", new[] { "default", "web" }, runtimeUsed: true),
CreateRubyPackageArtifact("pkg-sidekiq", "sidekiq", "7.2.1", groups: null, runtimeUsed: false, metadataOverrides: new Dictionary<string, string?>
{
["groups"] = "jobs",
["runtime.entrypoints"] = "config/jobs.rb",
["runtime.files"] = "config/jobs.rb"
})
})
}
};
var provider = BuildServiceProvider(backend);
@@ -557,15 +559,17 @@ public sealed class CommandHandlersTests
public async Task HandleRubyResolveAsync_WritesJson()
{
var originalExit = Environment.ExitCode;
const string identifier = "ruby-scan-json";
var backend = new StubBackendClient(new JobTriggerResult(true, "ok", null, null))
{
RubyPackages = new[]
{
CreateRubyPackageArtifact("pkg-rack-json", "rack", "3.1.0", new[] { "default" }, runtimeUsed: true)
}
RubyInventory = CreateRubyInventory(
identifier,
new[]
{
CreateRubyPackageArtifact("pkg-rack-json", "rack", "3.1.0", new[] { "default" }, runtimeUsed: true)
})
};
var provider = BuildServiceProvider(backend);
const string identifier = "ruby-scan-json";
try
{
@@ -608,6 +612,35 @@ public sealed class CommandHandlersTests
}
}
[Fact]
public async Task HandleRubyResolveAsync_NotifiesWhenInventoryMissing()
{
var originalExit = Environment.ExitCode;
var backend = new StubBackendClient(new JobTriggerResult(true, "ok", null, null));
var provider = BuildServiceProvider(backend);
try
{
var output = await CaptureTestConsoleAsync(async _ =>
{
await CommandHandlers.HandleRubyResolveAsync(
provider,
imageReference: null,
scanId: "scan-missing",
format: "table",
verbose: false,
cancellationToken: CancellationToken.None);
});
Assert.Equal(0, Environment.ExitCode);
Assert.Contains("not available", output.Combined, StringComparison.OrdinalIgnoreCase);
}
finally
{
Environment.ExitCode = originalExit;
}
}
[Fact]
public async Task HandleAdviseRunAsync_WritesOutputAndSetsExitCode()
{
@@ -3520,6 +3553,18 @@ spec:
mergedMetadata);
}
private static RubyPackageInventoryModel CreateRubyInventory(
string scanId,
IReadOnlyList<RubyPackageArtifactModel> packages,
string? imageDigest = null)
{
return new RubyPackageInventoryModel(
scanId,
imageDigest ?? "sha256:inventory",
DateTimeOffset.UtcNow,
packages);
}
private static string ComputeSha256Base64(string path)
{
@@ -3601,8 +3646,8 @@ spec:
public string? LastExcititorRoute { get; private set; }
public HttpMethod? LastExcititorMethod { get; private set; }
public object? LastExcititorPayload { get; private set; }
public IReadOnlyList<RubyPackageArtifactModel> RubyPackages { get; set; } = Array.Empty<RubyPackageArtifactModel>();
public Exception? RubyPackagesException { get; set; }
public RubyPackageInventoryModel? RubyInventory { get; set; }
public Exception? RubyInventoryException { get; set; }
public string? LastRubyPackagesScanId { get; private set; }
public List<(string ExportId, string DestinationPath, string? Algorithm, string? Digest)> ExportDownloads { get; } = new();
public ExcititorOperationResult? ExcititorResult { get; set; } = new ExcititorOperationResult(true, "ok", null, null);
@@ -3830,15 +3875,15 @@ spec:
return Task.FromResult(EntryTraceResponse);
}
public Task<IReadOnlyList<RubyPackageArtifactModel>> GetRubyPackagesAsync(string scanId, CancellationToken cancellationToken)
public Task<RubyPackageInventoryModel?> GetRubyPackagesAsync(string scanId, CancellationToken cancellationToken)
{
LastRubyPackagesScanId = scanId;
if (RubyPackagesException is not null)
if (RubyInventoryException is not null)
{
throw RubyPackagesException;
throw RubyInventoryException;
}
return Task.FromResult(RubyPackages);
return Task.FromResult(RubyInventory);
}
public Task<AdvisoryPipelinePlanResponseModel> CreateAdvisoryPipelinePlanAsync(AdvisoryAiTaskType taskType, AdvisoryPipelinePlanRequestModel request, CancellationToken cancellationToken)