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:
@@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"/>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }}"
|
||||
""";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user