Add signal contracts for reachability, exploitability, trust, and unknown symbols
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Signals DSSE Sign & Evidence Locker / sign-signals-artifacts (push) Has been cancelled
Signals DSSE Sign & Evidence Locker / verify-signatures (push) Has been cancelled

- 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:
StellaOps Bot
2025-12-05 00:27:00 +02:00
parent b018949a8d
commit 8768c27f30
192 changed files with 27569 additions and 2552 deletions

View File

@@ -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}");
}
}
}

View File

@@ -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));
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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 | NR1NR10 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). |