feat: Implement vulnerability token signing and verification utilities
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:
master
2025-11-03 10:02:29 +02:00
parent bf2bf4b395
commit b1e78fe412
215 changed files with 19441 additions and 12185 deletions

View File

@@ -0,0 +1,65 @@
using System.Text.Json;
using StellaOps.AirGap.Policy;
using StellaOps.TaskRunner.Infrastructure.Execution;
namespace StellaOps.TaskRunner.Tests;
public sealed class FilesystemPackRunDispatcherTests
{
[Fact]
public async Task TryDequeueAsync_BlocksJob_WhenEgressPolicyDeniesDestination()
{
var root = Path.Combine(Path.GetTempPath(), "StellaOps_TaskRunnerTests", Guid.NewGuid().ToString("n"));
Directory.CreateDirectory(root);
var cancellationToken = TestContext.Current.CancellationToken;
var queuePath = Path.Combine(root, "queue");
var archivePath = Path.Combine(root, "archive");
Directory.CreateDirectory(queuePath);
Directory.CreateDirectory(archivePath);
var manifestPath = Path.Combine(queuePath, "manifest.yaml");
await File.WriteAllTextAsync(manifestPath, TestManifests.EgressBlocked, cancellationToken);
var jobEnvelope = new
{
RunId = "run-egress-blocked",
ManifestPath = Path.GetFileName(manifestPath),
InputsPath = (string?)null,
RequestedAt = (DateTimeOffset?)null
};
var jobPath = Path.Combine(queuePath, "job.json");
await File.WriteAllTextAsync(jobPath, JsonSerializer.Serialize(jobEnvelope), cancellationToken);
var policy = new EgressPolicy(new EgressPolicyOptions
{
Mode = EgressPolicyMode.Sealed,
AllowLoopback = false,
AllowPrivateNetworks = false
});
try
{
var dispatcher = new FilesystemPackRunDispatcher(queuePath, archivePath, policy);
var result = await dispatcher.TryDequeueAsync(cancellationToken);
Assert.Null(result);
Assert.False(File.Exists(jobPath));
Assert.True(File.Exists(jobPath + ".failed"));
Assert.Empty(Directory.GetFiles(archivePath));
}
finally
{
try
{
Directory.Delete(root, recursive: true);
}
catch
{
// Best-effort cleanup; ignore failures to avoid masking test results.
}
}
}
}

View File

@@ -121,7 +121,8 @@
<ProjectReference Include="..\StellaOps.TaskRunner.Infrastructure\StellaOps.TaskRunner.Infrastructure.csproj"/>
<ProjectReference Include="..\StellaOps.TaskRunner.Infrastructure\StellaOps.TaskRunner.Infrastructure.csproj"/>
<ProjectReference Include="..\..\..\AirGap\StellaOps.AirGap.Policy\StellaOps.AirGap.Policy\StellaOps.AirGap.Policy.csproj"/>

View File

@@ -1,6 +1,8 @@
using System.Linq;
using System.Text.Json.Nodes;
using StellaOps.TaskRunner.Core.Planning;
using System;
using System.Linq;
using System.Text.Json.Nodes;
using StellaOps.AirGap.Policy;
using StellaOps.TaskRunner.Core.Planning;
namespace StellaOps.TaskRunner.Tests;
@@ -165,13 +167,62 @@ public sealed class TaskPackPlannerTests
}
[Fact]
public void Plan_WhenRequiredInputMissing_ReturnsError()
{
var manifest = TestManifests.Load(TestManifests.RequiredInput);
var planner = new TaskPackPlanner();
var result = planner.Plan(manifest);
Assert.False(result.Success);
Assert.Contains(result.Errors, error => error.Path == "inputs.sbomBundle");
}
}
public void Plan_WhenRequiredInputMissing_ReturnsError()
{
var manifest = TestManifests.Load(TestManifests.RequiredInput);
var planner = new TaskPackPlanner();
var result = planner.Plan(manifest);
Assert.False(result.Success);
Assert.Contains(result.Errors, error => error.Path == "inputs.sbomBundle");
}
[Fact]
public void Plan_SealedMode_AllowsDeclaredEgress()
{
var manifest = TestManifests.Load(TestManifests.EgressAllowed);
var options = new EgressPolicyOptions
{
Mode = EgressPolicyMode.Sealed
};
options.AddAllowRule("mirror.internal", 443, EgressTransport.Https);
var planner = new TaskPackPlanner(new EgressPolicy(options));
var result = planner.Plan(manifest);
Assert.True(result.Success);
}
[Fact]
public void Plan_SealedMode_BlocksUndeclaredEgress()
{
var manifest = TestManifests.Load(TestManifests.EgressBlocked);
var options = new EgressPolicyOptions
{
Mode = EgressPolicyMode.Sealed
};
var planner = new TaskPackPlanner(new EgressPolicy(options));
var result = planner.Plan(manifest);
Assert.False(result.Success);
Assert.Contains(result.Errors, error => error.Message.Contains("example.com", StringComparison.OrdinalIgnoreCase));
}
[Fact]
public void Plan_SealedMode_RuntimeUrlWithoutDeclaration_ReturnsError()
{
var manifest = TestManifests.Load(TestManifests.EgressRuntime);
var options = new EgressPolicyOptions
{
Mode = EgressPolicyMode.Sealed
};
var planner = new TaskPackPlanner(new EgressPolicy(options));
var result = planner.Plan(manifest);
Assert.False(result.Success);
Assert.Contains(result.Errors, error => error.Path.StartsWith("spec.steps[0]", StringComparison.Ordinal));
}
}

View File

@@ -143,23 +143,74 @@ spec:
expression: "{{ steps.generate.outputs.evidence }}"
""";
public const string PolicyGate = """
apiVersion: stellaops.io/pack.v1
kind: TaskPack
metadata:
name: policy-gate-pack
version: 1.0.0
spec:
steps:
- id: prepare
run:
uses: builtin:prepare
- id: policy-check
gate:
policy:
policy: security-hold
parameters:
threshold: high
evidenceRef: "{{ steps.prepare.outputs.evidence }}"
""";
}
public const string PolicyGate = """
apiVersion: stellaops.io/pack.v1
kind: TaskPack
metadata:
name: policy-gate-pack
version: 1.0.0
spec:
steps:
- id: prepare
run:
uses: builtin:prepare
- id: policy-check
gate:
policy:
policy: security-hold
parameters:
threshold: high
evidenceRef: "{{ steps.prepare.outputs.evidence }}"
""";
public const string EgressAllowed = """
apiVersion: stellaops.io/pack.v1
kind: TaskPack
metadata:
name: egress-allowed
version: 1.0.0
spec:
steps:
- id: fetch
run:
uses: builtin:http
with:
url: https://mirror.internal/api/status
egress:
- url: https://mirror.internal/api/status
""";
public const string EgressBlocked = """
apiVersion: stellaops.io/pack.v1
kind: TaskPack
metadata:
name: egress-blocked
version: 1.0.0
spec:
steps:
- id: fetch
run:
uses: builtin:http
with:
url: https://example.com/api/status
""";
public const string EgressRuntime = """
apiVersion: stellaops.io/pack.v1
kind: TaskPack
metadata:
name: egress-runtime
version: 1.0.0
spec:
inputs:
- name: targetUrl
type: string
required: false
steps:
- id: fetch
run:
uses: builtin:http
with:
url: "{{ inputs.targetUrl }}"
""";
}