feat: Implement vulnerability token signing and verification utilities
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Added VulnTokenSigner for signing JWT tokens with specified algorithms and keys. - Introduced VulnTokenUtilities for resolving tenant and subject claims, and sanitizing context dictionaries. - Created VulnTokenVerificationUtilities for parsing tokens, verifying signatures, and deserializing payloads. - Developed VulnWorkflowAntiForgeryTokenIssuer for issuing anti-forgery tokens with configurable options. - Implemented VulnWorkflowAntiForgeryTokenVerifier for verifying anti-forgery tokens and validating payloads. - Added AuthorityVulnerabilityExplorerOptions to manage configuration for vulnerability explorer features. - Included tests for FilesystemPackRunDispatcher to ensure proper job handling under egress policy restrictions.
This commit is contained in:
@@ -1,110 +1,110 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using FluentAssertions;
|
||||
using StellaOps.Scheduler.Models;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Scheduler.Queue.Tests;
|
||||
|
||||
public sealed class PlannerAndRunnerMessageTests
|
||||
{
|
||||
[Fact]
|
||||
public void PlannerMessage_CanonicalSerialization_RoundTrips()
|
||||
{
|
||||
var schedule = new Schedule(
|
||||
id: "sch-tenant-nightly",
|
||||
tenantId: "tenant-alpha",
|
||||
name: "Nightly Deltas",
|
||||
enabled: true,
|
||||
cronExpression: "0 2 * * *",
|
||||
timezone: "UTC",
|
||||
mode: ScheduleMode.AnalysisOnly,
|
||||
selection: new Selector(SelectorScope.AllImages, tenantId: "tenant-alpha"),
|
||||
onlyIf: new ScheduleOnlyIf(lastReportOlderThanDays: 3),
|
||||
notify: new ScheduleNotify(onNewFindings: true, SeverityRank.High, includeKev: true),
|
||||
limits: new ScheduleLimits(maxJobs: 10, ratePerSecond: 5, parallelism: 3),
|
||||
createdAt: DateTimeOffset.Parse("2025-10-01T02:00:00Z"),
|
||||
createdBy: "system",
|
||||
updatedAt: DateTimeOffset.Parse("2025-10-02T02:00:00Z"),
|
||||
updatedBy: "system",
|
||||
subscribers: ImmutableArray<string>.Empty,
|
||||
schemaVersion: "1.0.0");
|
||||
|
||||
var run = new Run(
|
||||
id: "run-123",
|
||||
tenantId: "tenant-alpha",
|
||||
trigger: RunTrigger.Cron,
|
||||
state: RunState.Planning,
|
||||
stats: new RunStats(candidates: 5, deduped: 4, queued: 0, completed: 0, deltas: 0),
|
||||
createdAt: DateTimeOffset.Parse("2025-10-02T02:05:00Z"),
|
||||
reason: new RunReason(manualReason: null, feedserExportId: null, vexerExportId: null, cursor: null)
|
||||
with { ImpactWindowFrom = "2025-10-01T00:00:00Z", ImpactWindowTo = "2025-10-02T00:00:00Z" },
|
||||
scheduleId: "sch-tenant-nightly");
|
||||
|
||||
var impactSet = new ImpactSet(
|
||||
selector: new Selector(SelectorScope.AllImages, tenantId: "tenant-alpha"),
|
||||
images: new[]
|
||||
{
|
||||
new ImpactImage(
|
||||
imageDigest: "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
registry: "registry",
|
||||
repository: "repo",
|
||||
namespaces: new[] { "prod" },
|
||||
tags: new[] { "latest" },
|
||||
usedByEntrypoint: true,
|
||||
labels: new[] { KeyValuePair.Create("team", "appsec") })
|
||||
},
|
||||
usageOnly: true,
|
||||
generatedAt: DateTimeOffset.Parse("2025-10-02T02:06:00Z"),
|
||||
total: 1,
|
||||
snapshotId: "snap-001");
|
||||
|
||||
var message = new PlannerQueueMessage(run, impactSet, schedule, correlationId: "corr-1");
|
||||
|
||||
var json = CanonicalJsonSerializer.Serialize(message);
|
||||
var roundTrip = CanonicalJsonSerializer.Deserialize<PlannerQueueMessage>(json);
|
||||
|
||||
roundTrip.Should().BeEquivalentTo(message, options => options.WithStrictOrdering());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RunnerSegmentMessage_RequiresAtLeastOneDigest()
|
||||
{
|
||||
var act = () => new RunnerSegmentQueueMessage(
|
||||
segmentId: "segment-empty",
|
||||
runId: "run-123",
|
||||
tenantId: "tenant-alpha",
|
||||
imageDigests: Array.Empty<string>());
|
||||
|
||||
act.Should().Throw<ArgumentException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RunnerSegmentMessage_CanonicalSerialization_RoundTrips()
|
||||
{
|
||||
var message = new RunnerSegmentQueueMessage(
|
||||
segmentId: "segment-01",
|
||||
runId: "run-123",
|
||||
tenantId: "tenant-alpha",
|
||||
imageDigests: new[]
|
||||
{
|
||||
"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
||||
},
|
||||
scheduleId: "sch-tenant-nightly",
|
||||
ratePerSecond: 25,
|
||||
usageOnly: true,
|
||||
attributes: new Dictionary<string, string>
|
||||
{
|
||||
["plannerShard"] = "0",
|
||||
["priority"] = "kev"
|
||||
},
|
||||
correlationId: "corr-2");
|
||||
|
||||
var json = CanonicalJsonSerializer.Serialize(message);
|
||||
var roundTrip = CanonicalJsonSerializer.Deserialize<RunnerSegmentQueueMessage>(json);
|
||||
|
||||
roundTrip.Should().BeEquivalentTo(message, options => options.WithStrictOrdering());
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using FluentAssertions;
|
||||
using StellaOps.Scheduler.Models;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Scheduler.Queue.Tests;
|
||||
|
||||
public sealed class PlannerAndRunnerMessageTests
|
||||
{
|
||||
[Fact]
|
||||
public void PlannerMessage_CanonicalSerialization_RoundTrips()
|
||||
{
|
||||
var schedule = new Schedule(
|
||||
id: "sch-tenant-nightly",
|
||||
tenantId: "tenant-alpha",
|
||||
name: "Nightly Deltas",
|
||||
enabled: true,
|
||||
cronExpression: "0 2 * * *",
|
||||
timezone: "UTC",
|
||||
mode: ScheduleMode.AnalysisOnly,
|
||||
selection: new Selector(SelectorScope.AllImages, tenantId: "tenant-alpha"),
|
||||
onlyIf: new ScheduleOnlyIf(lastReportOlderThanDays: 3),
|
||||
notify: new ScheduleNotify(onNewFindings: true, SeverityRank.High, includeKev: true),
|
||||
limits: new ScheduleLimits(maxJobs: 10, ratePerSecond: 5, parallelism: 3),
|
||||
createdAt: DateTimeOffset.Parse("2025-10-01T02:00:00Z"),
|
||||
createdBy: "system",
|
||||
updatedAt: DateTimeOffset.Parse("2025-10-02T02:00:00Z"),
|
||||
updatedBy: "system",
|
||||
subscribers: ImmutableArray<string>.Empty,
|
||||
schemaVersion: "1.0.0");
|
||||
|
||||
var run = new Run(
|
||||
id: "run-123",
|
||||
tenantId: "tenant-alpha",
|
||||
trigger: RunTrigger.Cron,
|
||||
state: RunState.Planning,
|
||||
stats: new RunStats(candidates: 5, deduped: 4, queued: 0, completed: 0, deltas: 0),
|
||||
createdAt: DateTimeOffset.Parse("2025-10-02T02:05:00Z"),
|
||||
reason: new RunReason(manualReason: null, conselierExportId: null, excitorExportId: null, cursor: null)
|
||||
with { ImpactWindowFrom = "2025-10-01T00:00:00Z", ImpactWindowTo = "2025-10-02T00:00:00Z" },
|
||||
scheduleId: "sch-tenant-nightly");
|
||||
|
||||
var impactSet = new ImpactSet(
|
||||
selector: new Selector(SelectorScope.AllImages, tenantId: "tenant-alpha"),
|
||||
images: new[]
|
||||
{
|
||||
new ImpactImage(
|
||||
imageDigest: "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
registry: "registry",
|
||||
repository: "repo",
|
||||
namespaces: new[] { "prod" },
|
||||
tags: new[] { "latest" },
|
||||
usedByEntrypoint: true,
|
||||
labels: new[] { KeyValuePair.Create("team", "appsec") })
|
||||
},
|
||||
usageOnly: true,
|
||||
generatedAt: DateTimeOffset.Parse("2025-10-02T02:06:00Z"),
|
||||
total: 1,
|
||||
snapshotId: "snap-001");
|
||||
|
||||
var message = new PlannerQueueMessage(run, impactSet, schedule, correlationId: "corr-1");
|
||||
|
||||
var json = CanonicalJsonSerializer.Serialize(message);
|
||||
var roundTrip = CanonicalJsonSerializer.Deserialize<PlannerQueueMessage>(json);
|
||||
|
||||
roundTrip.Should().BeEquivalentTo(message, options => options.WithStrictOrdering());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RunnerSegmentMessage_RequiresAtLeastOneDigest()
|
||||
{
|
||||
var act = () => new RunnerSegmentQueueMessage(
|
||||
segmentId: "segment-empty",
|
||||
runId: "run-123",
|
||||
tenantId: "tenant-alpha",
|
||||
imageDigests: Array.Empty<string>());
|
||||
|
||||
act.Should().Throw<ArgumentException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RunnerSegmentMessage_CanonicalSerialization_RoundTrips()
|
||||
{
|
||||
var message = new RunnerSegmentQueueMessage(
|
||||
segmentId: "segment-01",
|
||||
runId: "run-123",
|
||||
tenantId: "tenant-alpha",
|
||||
imageDigests: new[]
|
||||
{
|
||||
"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
||||
},
|
||||
scheduleId: "sch-tenant-nightly",
|
||||
ratePerSecond: 25,
|
||||
usageOnly: true,
|
||||
attributes: new Dictionary<string, string>
|
||||
{
|
||||
["plannerShard"] = "0",
|
||||
["priority"] = "kev"
|
||||
},
|
||||
correlationId: "corr-2");
|
||||
|
||||
var json = CanonicalJsonSerializer.Serialize(message);
|
||||
var roundTrip = CanonicalJsonSerializer.Deserialize<RunnerSegmentQueueMessage>(json);
|
||||
|
||||
roundTrip.Should().BeEquivalentTo(message, options => options.WithStrictOrdering());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user