Add signal contracts for reachability, exploitability, trust, and unknown symbols
- Introduced `ReachabilityState`, `RuntimeHit`, `ExploitabilitySignal`, `ReachabilitySignal`, `SignalEnvelope`, `SignalType`, `TrustSignal`, and `UnknownSymbolSignal` records to define various signal types and their properties. - Implemented JSON serialization attributes for proper data interchange. - Created project files for the new signal contracts library and corresponding test projects. - Added deterministic test fixtures for micro-interaction testing. - Included cryptographic keys for secure operations with cosign.
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
using System.Text.Json;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Notifier.Tests.Contracts;
|
||||
|
||||
public sealed class ArtifactHashesTests
|
||||
{
|
||||
private static string RepoRoot => Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "../../../../../../../"));
|
||||
|
||||
[Fact]
|
||||
public void ArtifactHashesHasNoTbdAndFilesExist()
|
||||
{
|
||||
var hashesPath = Path.Combine(RepoRoot, "offline/notifier/artifact-hashes.json");
|
||||
using var hashes = JsonDocument.Parse(File.ReadAllText(hashesPath));
|
||||
|
||||
foreach (var entry in hashes.RootElement.GetProperty("entries").EnumerateArray())
|
||||
{
|
||||
var path = Path.Combine(RepoRoot, entry.GetProperty("path").GetString()!);
|
||||
var digest = entry.GetProperty("digest").GetString()!;
|
||||
Assert.False(string.Equals(digest, "TBD", StringComparison.OrdinalIgnoreCase));
|
||||
Assert.True(File.Exists(path), $"artifact path missing: {path}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Text.Json;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Notifier.Tests.Contracts;
|
||||
|
||||
public sealed class OfflineKitManifestTests
|
||||
{
|
||||
private static string RepoRoot => Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "../../../../../../../"));
|
||||
|
||||
[Fact]
|
||||
public void ManifestDssePayloadMatchesManifest()
|
||||
{
|
||||
var manifestPath = Path.Combine(RepoRoot, "offline/notifier/notify-kit.manifest.json");
|
||||
var dssePath = Path.Combine(RepoRoot, "offline/notifier/notify-kit.manifest.dsse.json");
|
||||
|
||||
using var manifest = JsonDocument.Parse(File.ReadAllText(manifestPath));
|
||||
using var dsse = JsonDocument.Parse(File.ReadAllText(dssePath));
|
||||
|
||||
var payloadBytes = Convert.FromBase64String(dsse.RootElement.GetProperty("payload").GetString()!);
|
||||
using var payload = JsonDocument.Parse(payloadBytes);
|
||||
|
||||
Assert.True(payload.RootElement.DeepEquals(manifest.RootElement));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ManifestArtifactsHaveHashes()
|
||||
{
|
||||
var manifestPath = Path.Combine(RepoRoot, "offline/notifier/notify-kit.manifest.json");
|
||||
var hashesPath = Path.Combine(RepoRoot, "offline/notifier/artifact-hashes.json");
|
||||
|
||||
using var manifest = JsonDocument.Parse(File.ReadAllText(manifestPath));
|
||||
using var hashes = JsonDocument.Parse(File.ReadAllText(hashesPath));
|
||||
|
||||
var artifactDigests = hashes.RootElement.GetProperty("entries").EnumerateArray().ToDictionary(e => e.GetProperty("path").GetString()!, e => e.GetProperty("digest").GetString()!);
|
||||
foreach (var artifact in manifest.RootElement.GetProperty("artifacts").EnumerateArray())
|
||||
{
|
||||
var path = artifact.GetProperty("path").GetString()!;
|
||||
var digest = artifact.GetProperty("digest").GetString()!;
|
||||
Assert.True(artifactDigests.TryGetValue(path, out var fromList), $"artifact-hashes.json missing {path}");
|
||||
Assert.Equal(digest, fromList);
|
||||
Assert.False(string.Equals(digest, "TBD", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.Text.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Notifier.Tests.Contracts;
|
||||
|
||||
public sealed class PolicyDocsCompletenessTests
|
||||
{
|
||||
private static string RepoRoot => Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "../../../../../../../"));
|
||||
|
||||
[Theory]
|
||||
[InlineData("docs/notifications/security/tenant-approvals.md")]
|
||||
[InlineData("docs/notifications/security/webhook-ack-hardening.md")]
|
||||
[InlineData("docs/notifications/security/redaction-catalog.md")]
|
||||
[InlineData("docs/notifications/operations/quotas.md")]
|
||||
[InlineData("docs/notifications/operations/retries.md")]
|
||||
public void PolicyDocsHaveNoPlaceholders(string relativePath)
|
||||
{
|
||||
var path = Path.Combine(RepoRoot, relativePath);
|
||||
var text = File.ReadAllText(path);
|
||||
Assert.DoesNotContain("TBD", text, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.DoesNotContain("TODO", text, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AlertYamlParses()
|
||||
{
|
||||
var path = Path.Combine(RepoRoot, "docs/notifications/operations/alerts/notify-slo-alerts.yaml");
|
||||
var text = File.ReadAllText(path);
|
||||
Assert.Contains("alert: NotifyDeliverySuccessSLO", text);
|
||||
Assert.Contains("notify_backlog_depth", text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SimulationIndexHasEntries()
|
||||
{
|
||||
var path = Path.Combine(RepoRoot, "docs/notifications/simulations/index.ndjson");
|
||||
var lines = File.ReadAllLines(path).Where(l => !string.IsNullOrWhiteSpace(l)).ToList();
|
||||
Assert.NotEmpty(lines);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
Assert.DoesNotContain("TBD", line, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.Text.Json;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Notifier.Tests.Contracts;
|
||||
|
||||
public sealed class RenderingDeterminismTests
|
||||
{
|
||||
private static string RepoRoot => Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "../../../../../../../"));
|
||||
|
||||
[Fact]
|
||||
public void RenderingIndexMatchesTemplates()
|
||||
{
|
||||
var indexPath = Path.Combine(RepoRoot, "docs/notifications/fixtures/rendering/index.ndjson");
|
||||
foreach (var line in File.ReadAllLines(indexPath).Where(l => !string.IsNullOrWhiteSpace(l)))
|
||||
{
|
||||
using var entry = JsonDocument.Parse(line);
|
||||
var root = entry.RootElement;
|
||||
var templatePath = Path.Combine(RepoRoot, "docs/notifications/fixtures/rendering", root.GetProperty("body_sample_path").GetString()!);
|
||||
using var template = JsonDocument.Parse(File.ReadAllText(templatePath));
|
||||
|
||||
var expectedHash = root.GetProperty("expected_hash").GetString()!;
|
||||
Assert.False(string.Equals(expectedHash, "TBD", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
var previewHash = template.RootElement.GetProperty("preview_hash").GetString()!;
|
||||
Assert.Equal(expectedHash, previewHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System.Text.Json;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Notifier.Tests.Contracts;
|
||||
|
||||
public sealed class SchemaCatalogTests
|
||||
{
|
||||
private static string RepoRoot => Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "../../../../../../../"));
|
||||
|
||||
[Fact]
|
||||
public void CatalogMatchesDssePayload()
|
||||
{
|
||||
var catalogPath = Path.Combine(RepoRoot, "docs/notifications/schemas/notify-schemas-catalog.json");
|
||||
var dssePath = Path.Combine(RepoRoot, "docs/notifications/schemas/notify-schemas-catalog.dsse.json");
|
||||
|
||||
var catalogJson = File.ReadAllText(catalogPath);
|
||||
var dsseJson = File.ReadAllText(dssePath);
|
||||
|
||||
using var catalog = JsonDocument.Parse(catalogJson);
|
||||
using var dsse = JsonDocument.Parse(dsseJson);
|
||||
|
||||
var payloadBase64 = dsse.RootElement.GetProperty("payload").GetString()!;
|
||||
var payloadBytes = Convert.FromBase64String(payloadBase64);
|
||||
using var payload = JsonDocument.Parse(payloadBytes);
|
||||
|
||||
Assert.True(payload.RootElement.DeepEquals(catalog.RootElement));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CatalogHasNoTbdDigests()
|
||||
{
|
||||
var catalogPath = Path.Combine(RepoRoot, "docs/notifications/schemas/notify-schemas-catalog.json");
|
||||
var text = File.ReadAllText(catalogPath);
|
||||
Assert.DoesNotContain("TBD", text, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InputsLockAlignsWithCatalog()
|
||||
{
|
||||
var catalogPath = Path.Combine(RepoRoot, "docs/notifications/schemas/notify-schemas-catalog.json");
|
||||
var lockPath = Path.Combine(RepoRoot, "docs/notifications/schemas/inputs.lock");
|
||||
|
||||
using var catalog = JsonDocument.Parse(File.ReadAllText(catalogPath));
|
||||
using var lockDoc = JsonDocument.Parse(File.ReadAllText(lockPath));
|
||||
|
||||
var catalogSchemas = catalog.RootElement.GetProperty("schemas").EnumerateArray().ToDictionary(e => e.GetProperty("file").GetString()!, e => e.GetProperty("digest").GetString()!);
|
||||
var lockEntries = lockDoc.RootElement.GetProperty("entries").EnumerateArray().ToDictionary(e => e.GetProperty("file").GetString()!, e => e.GetProperty("digest").GetString()!);
|
||||
|
||||
Assert.Equal(catalogSchemas.Count, lockEntries.Count);
|
||||
foreach (var kvp in catalogSchemas)
|
||||
{
|
||||
Assert.True(lockEntries.TryGetValue(kvp.Key, out var digest), $"inputs.lock missing {kvp.Key}");
|
||||
Assert.Equal(kvp.Value, digest);
|
||||
Assert.NotEqual("TBD", kvp.Value, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,4 +13,4 @@
|
||||
| NOTIFY-RISK-66-001 | DONE (2025-11-24) | Notifications Service Guild · Risk Engine Guild | Added risk-events endpoint + templates/rules for severity change notifications. |
|
||||
| NOTIFY-RISK-67-001 | DONE (2025-11-24) | Notifications Service Guild · Policy Guild | Added routing/templates for risk profile publish/deprecate/threshold change. |
|
||||
| NOTIFY-RISK-68-001 | DONE (2025-11-24) | Notifications Service Guild | Default routing seeds with throttles/locales for risk alerts. |
|
||||
| NOTIFY-GAPS-171-014 | DOING (2025-12-04) | Notifications Service Guild | NR1–NR10 scoped; schemas/catalog/fixtures/offline kit scaffolded; fill BLAKE3 digests, DSSE signatures, and tests next. |
|
||||
| NOTIFY-GAPS-171-014 | BLOCKED (2025-12-04) | Notifications Service Guild | Await production signing key to re-sign DSSE envelopes (currently dev-signed). |
|
||||
|
||||
Reference in New Issue
Block a user