feat: add PolicyPackSelectorComponent with tests and integration
- Implemented PolicyPackSelectorComponent for selecting policy packs. - Added unit tests for component behavior, including API success and error handling. - Introduced monaco-workers type declarations for editor workers. - Created acceptance tests for guardrails with stubs for AT1–AT10. - Established SCA Failure Catalogue Fixtures for regression testing. - Developed plugin determinism harness with stubs for PL1–PL10. - Added scripts for evidence upload and verification processes.
This commit is contained in:
@@ -62,7 +62,7 @@ public sealed class PackRunApprovalDecisionServiceTests
|
||||
NullLogger<PackRunApprovalDecisionService>.Instance);
|
||||
|
||||
var result = await service.ApplyAsync(
|
||||
new PackRunApprovalDecisionRequest("missing", "approval", "hash", PackRunApprovalDecisionType.Approved, "actor", null),
|
||||
new PackRunApprovalDecisionRequest("missing", "approval", "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", PackRunApprovalDecisionType.Approved, "actor", null),
|
||||
CancellationToken.None);
|
||||
|
||||
Assert.Equal("not_found", result.Status);
|
||||
@@ -107,6 +107,44 @@ public sealed class PackRunApprovalDecisionServiceTests
|
||||
Assert.False(scheduler.ScheduledContexts.Any());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ApplyAsync_ReturnsPlanHashMismatchWhenFormatInvalid()
|
||||
{
|
||||
var plan = TestPlanFactory.CreatePlan();
|
||||
var state = TestPlanFactory.CreateState("run-1", plan);
|
||||
var approval = new PackRunApprovalState(
|
||||
"security-review",
|
||||
new[] { "Packs.Approve" },
|
||||
new[] { "step-a" },
|
||||
Array.Empty<string>(),
|
||||
null,
|
||||
DateTimeOffset.UtcNow.AddMinutes(-5),
|
||||
PackRunApprovalStatus.Pending);
|
||||
|
||||
var approvalStore = new InMemoryApprovalStore(new Dictionary<string, IReadOnlyList<PackRunApprovalState>>
|
||||
{
|
||||
["run-1"] = new List<PackRunApprovalState> { approval }
|
||||
});
|
||||
var stateStore = new InMemoryStateStore(new Dictionary<string, PackRunState>
|
||||
{
|
||||
["run-1"] = state
|
||||
});
|
||||
var scheduler = new RecordingScheduler();
|
||||
|
||||
var service = new PackRunApprovalDecisionService(
|
||||
approvalStore,
|
||||
stateStore,
|
||||
scheduler,
|
||||
NullLogger<PackRunApprovalDecisionService>.Instance);
|
||||
|
||||
var result = await service.ApplyAsync(
|
||||
new PackRunApprovalDecisionRequest("run-1", "security-review", "not-a-digest", PackRunApprovalDecisionType.Approved, "actor", null),
|
||||
CancellationToken.None);
|
||||
|
||||
Assert.Equal("plan_hash_mismatch", result.Status);
|
||||
Assert.False(scheduler.ScheduledContexts.Any());
|
||||
}
|
||||
|
||||
private sealed class InMemoryApprovalStore : IPackRunApprovalStore
|
||||
{
|
||||
private readonly Dictionary<string, List<PackRunApprovalState>> _approvals;
|
||||
@@ -214,7 +252,7 @@ internal static class TestPlanFactory
|
||||
metadata,
|
||||
new Dictionary<string, JsonNode?>(StringComparer.Ordinal),
|
||||
new[] { step },
|
||||
"hash-123",
|
||||
"sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
new[]
|
||||
{
|
||||
new TaskPackPlanApproval("security-review", new[] { "Packs.Approve" }, null, null)
|
||||
|
||||
@@ -36,9 +36,24 @@ public sealed class TaskPackPlannerTests
|
||||
|
||||
var resultB = planner.Plan(manifest, inputs);
|
||||
Assert.True(resultB.Success);
|
||||
Assert.Equal(plan.Hash, resultB.Plan!.Hash);
|
||||
}
|
||||
|
||||
Assert.Equal(plan.Hash, resultB.Plan!.Hash);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PlanHash_IsPrefixedSha256Digest()
|
||||
{
|
||||
var manifest = TestManifests.Load(TestManifests.Sample);
|
||||
var planner = new TaskPackPlanner();
|
||||
|
||||
var result = planner.Plan(manifest);
|
||||
Assert.True(result.Success);
|
||||
var hash = result.Plan!.Hash;
|
||||
Assert.StartsWith("sha256:", hash, StringComparison.Ordinal);
|
||||
Assert.Equal(71, hash.Length); // "sha256:" + 64 hex characters
|
||||
var hex = hash.Substring("sha256:".Length);
|
||||
Assert.True(hex.All(c => Uri.IsHexDigit(c)), "Hash contains non-hex characters.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Plan_WhenConditionEvaluatesFalse_DisablesStep()
|
||||
{
|
||||
|
||||
@@ -19,17 +19,27 @@ metadata:
|
||||
version: 1.0.0
|
||||
description: Sample pack for planner tests
|
||||
tags: [tests]
|
||||
spec:
|
||||
inputs:
|
||||
- name: dryRun
|
||||
type: boolean
|
||||
required: false
|
||||
default: false
|
||||
approvals:
|
||||
spec:
|
||||
inputs:
|
||||
- name: dryRun
|
||||
type: boolean
|
||||
required: false
|
||||
default: false
|
||||
sandbox:
|
||||
mode: sealed
|
||||
egressAllowlist: []
|
||||
cpuLimitMillicores: 100
|
||||
memoryLimitMiB: 128
|
||||
quotaSeconds: 60
|
||||
slo:
|
||||
runP95Seconds: 300
|
||||
approvalP95Seconds: 900
|
||||
maxQueueDepth: 100
|
||||
approvals:
|
||||
- id: security-review
|
||||
grants: ["packs.approve"]
|
||||
steps:
|
||||
- id: plan-step
|
||||
steps:
|
||||
- id: plan-step
|
||||
name: Plan
|
||||
run:
|
||||
uses: builtin:plan
|
||||
@@ -57,6 +67,16 @@ spec:
|
||||
- name: sbomBundle
|
||||
type: object
|
||||
required: true
|
||||
sandbox:
|
||||
mode: sealed
|
||||
egressAllowlist: []
|
||||
cpuLimitMillicores: 100
|
||||
memoryLimitMiB: 128
|
||||
quotaSeconds: 60
|
||||
slo:
|
||||
runP95Seconds: 300
|
||||
approvalP95Seconds: 900
|
||||
maxQueueDepth: 100
|
||||
steps:
|
||||
- id: noop
|
||||
run:
|
||||
@@ -71,12 +91,22 @@ kind: TaskPack
|
||||
metadata:
|
||||
name: step-ref-pack
|
||||
version: 1.0.0
|
||||
spec:
|
||||
steps:
|
||||
- id: prepare
|
||||
run:
|
||||
uses: builtin:prepare
|
||||
- id: consume
|
||||
spec:
|
||||
sandbox:
|
||||
mode: sealed
|
||||
egressAllowlist: []
|
||||
cpuLimitMillicores: 100
|
||||
memoryLimitMiB: 128
|
||||
quotaSeconds: 60
|
||||
slo:
|
||||
runP95Seconds: 300
|
||||
approvalP95Seconds: 900
|
||||
maxQueueDepth: 100
|
||||
steps:
|
||||
- id: prepare
|
||||
run:
|
||||
uses: builtin:prepare
|
||||
- id: consume
|
||||
run:
|
||||
uses: builtin:consume
|
||||
with:
|
||||
@@ -89,16 +119,26 @@ kind: TaskPack
|
||||
metadata:
|
||||
name: map-pack
|
||||
version: 1.0.0
|
||||
spec:
|
||||
inputs:
|
||||
- name: targets
|
||||
type: array
|
||||
required: true
|
||||
steps:
|
||||
- id: maintenance-loop
|
||||
map:
|
||||
items: "{{ inputs.targets }}"
|
||||
step:
|
||||
spec:
|
||||
inputs:
|
||||
- name: targets
|
||||
type: array
|
||||
required: true
|
||||
sandbox:
|
||||
mode: sealed
|
||||
egressAllowlist: []
|
||||
cpuLimitMillicores: 100
|
||||
memoryLimitMiB: 128
|
||||
quotaSeconds: 60
|
||||
slo:
|
||||
runP95Seconds: 300
|
||||
approvalP95Seconds: 900
|
||||
maxQueueDepth: 100
|
||||
steps:
|
||||
- id: maintenance-loop
|
||||
map:
|
||||
items: "{{ inputs.targets }}"
|
||||
step:
|
||||
id: echo-step
|
||||
run:
|
||||
uses: builtin:echo
|
||||
@@ -112,16 +152,26 @@ kind: TaskPack
|
||||
metadata:
|
||||
name: secret-pack
|
||||
version: 1.0.0
|
||||
spec:
|
||||
secrets:
|
||||
spec:
|
||||
secrets:
|
||||
- name: apiKey
|
||||
scope: packs.run
|
||||
description: API authentication token
|
||||
steps:
|
||||
- id: use-secret
|
||||
run:
|
||||
uses: builtin:http
|
||||
with:
|
||||
description: API authentication token
|
||||
sandbox:
|
||||
mode: sealed
|
||||
egressAllowlist: []
|
||||
cpuLimitMillicores: 100
|
||||
memoryLimitMiB: 128
|
||||
quotaSeconds: 60
|
||||
slo:
|
||||
runP95Seconds: 300
|
||||
approvalP95Seconds: 900
|
||||
maxQueueDepth: 100
|
||||
steps:
|
||||
- id: use-secret
|
||||
run:
|
||||
uses: builtin:http
|
||||
with:
|
||||
token: "{{ secrets.apiKey }}"
|
||||
""";
|
||||
|
||||
@@ -131,12 +181,22 @@ kind: TaskPack
|
||||
metadata:
|
||||
name: output-pack
|
||||
version: 1.0.0
|
||||
spec:
|
||||
steps:
|
||||
- id: generate
|
||||
run:
|
||||
uses: builtin:generate
|
||||
outputs:
|
||||
spec:
|
||||
sandbox:
|
||||
mode: sealed
|
||||
egressAllowlist: []
|
||||
cpuLimitMillicores: 100
|
||||
memoryLimitMiB: 128
|
||||
quotaSeconds: 60
|
||||
slo:
|
||||
runP95Seconds: 300
|
||||
approvalP95Seconds: 900
|
||||
maxQueueDepth: 100
|
||||
steps:
|
||||
- id: generate
|
||||
run:
|
||||
uses: builtin:generate
|
||||
outputs:
|
||||
- name: bundlePath
|
||||
type: file
|
||||
path: artifacts/report.txt
|
||||
@@ -152,6 +212,16 @@ metadata:
|
||||
name: failure-policy-pack
|
||||
version: 1.0.0
|
||||
spec:
|
||||
sandbox:
|
||||
mode: sealed
|
||||
egressAllowlist: []
|
||||
cpuLimitMillicores: 100
|
||||
memoryLimitMiB: 128
|
||||
quotaSeconds: 60
|
||||
slo:
|
||||
runP95Seconds: 300
|
||||
approvalP95Seconds: 900
|
||||
maxQueueDepth: 100
|
||||
steps:
|
||||
- id: build
|
||||
run:
|
||||
@@ -170,6 +240,16 @@ metadata:
|
||||
name: parallel-pack
|
||||
version: 1.1.0
|
||||
spec:
|
||||
sandbox:
|
||||
mode: sealed
|
||||
egressAllowlist: []
|
||||
cpuLimitMillicores: 100
|
||||
memoryLimitMiB: 128
|
||||
quotaSeconds: 60
|
||||
slo:
|
||||
runP95Seconds: 300
|
||||
approvalP95Seconds: 900
|
||||
maxQueueDepth: 100
|
||||
steps:
|
||||
- id: fanout
|
||||
parallel:
|
||||
@@ -196,6 +276,16 @@ metadata:
|
||||
name: policy-gate-pack
|
||||
version: 1.0.0
|
||||
spec:
|
||||
sandbox:
|
||||
mode: sealed
|
||||
egressAllowlist: []
|
||||
cpuLimitMillicores: 100
|
||||
memoryLimitMiB: 128
|
||||
quotaSeconds: 60
|
||||
slo:
|
||||
runP95Seconds: 300
|
||||
approvalP95Seconds: 900
|
||||
maxQueueDepth: 100
|
||||
steps:
|
||||
- id: prepare
|
||||
run:
|
||||
@@ -216,6 +306,16 @@ metadata:
|
||||
name: egress-allowed
|
||||
version: 1.0.0
|
||||
spec:
|
||||
sandbox:
|
||||
mode: sealed
|
||||
egressAllowlist: []
|
||||
cpuLimitMillicores: 100
|
||||
memoryLimitMiB: 128
|
||||
quotaSeconds: 60
|
||||
slo:
|
||||
runP95Seconds: 300
|
||||
approvalP95Seconds: 900
|
||||
maxQueueDepth: 100
|
||||
steps:
|
||||
- id: fetch
|
||||
run:
|
||||
@@ -233,6 +333,16 @@ metadata:
|
||||
name: egress-blocked
|
||||
version: 1.0.0
|
||||
spec:
|
||||
sandbox:
|
||||
mode: sealed
|
||||
egressAllowlist: []
|
||||
cpuLimitMillicores: 100
|
||||
memoryLimitMiB: 128
|
||||
quotaSeconds: 60
|
||||
slo:
|
||||
runP95Seconds: 300
|
||||
approvalP95Seconds: 900
|
||||
maxQueueDepth: 100
|
||||
steps:
|
||||
- id: fetch
|
||||
run:
|
||||
@@ -252,6 +362,16 @@ spec:
|
||||
- name: targetUrl
|
||||
type: string
|
||||
required: false
|
||||
sandbox:
|
||||
mode: sealed
|
||||
egressAllowlist: []
|
||||
cpuLimitMillicores: 100
|
||||
memoryLimitMiB: 128
|
||||
quotaSeconds: 60
|
||||
slo:
|
||||
runP95Seconds: 300
|
||||
approvalP95Seconds: 900
|
||||
maxQueueDepth: 100
|
||||
steps:
|
||||
- id: fetch
|
||||
run:
|
||||
|
||||
Reference in New Issue
Block a user