Add property-based tests for SBOM/VEX document ordering and Unicode normalization determinism

- Implement `SbomVexOrderingDeterminismProperties` for testing component list and vulnerability metadata hash consistency.
- Create `UnicodeNormalizationDeterminismProperties` to validate NFC normalization and Unicode string handling.
- Add project file for `StellaOps.Testing.Determinism.Properties` with necessary dependencies.
- Introduce CI/CD template validation tests including YAML syntax checks and documentation content verification.
- Create validation script for CI/CD templates ensuring all required files and structures are present.
This commit is contained in:
StellaOps Bot
2025-12-26 15:17:15 +02:00
parent 7792749bb4
commit 907783f625
354 changed files with 79727 additions and 1346 deletions

View File

@@ -0,0 +1,531 @@
// -----------------------------------------------------------------------------
// BudgetAlertTemplates.cs
// Sprint: SPRINT_20251226_002_BE_budget_enforcement
// Task: BUDGET-07 - Notification templates for budget alerts
// Description: Default templates for risk budget warning and exceeded alerts
// -----------------------------------------------------------------------------
using System.Collections.Immutable;
using StellaOps.Notify.Models;
namespace StellaOps.Notify.Engine.Templates;
/// <summary>
/// Provides default templates for risk budget alert notifications.
/// Templates support policy.budget.warning and policy.budget.exceeded events.
/// </summary>
public static class BudgetAlertTemplates
{
/// <summary>
/// Template key for budget warning notifications.
/// </summary>
public const string BudgetWarningKey = "notification.policy.budget.warning";
/// <summary>
/// Template key for budget exceeded notifications.
/// </summary>
public const string BudgetExceededKey = "notification.policy.budget.exceeded";
/// <summary>
/// Get all default budget alert templates for a tenant.
/// </summary>
/// <param name="tenantId">Tenant identifier.</param>
/// <param name="locale">Locale code (default: en-us).</param>
/// <returns>Collection of default templates.</returns>
public static IReadOnlyList<NotifyTemplate> GetDefaultTemplates(
string tenantId,
string locale = "en-us")
{
var templates = new List<NotifyTemplate>();
// Add warning templates
templates.Add(CreateSlackWarningTemplate(tenantId, locale));
templates.Add(CreateTeamsWarningTemplate(tenantId, locale));
templates.Add(CreateEmailWarningTemplate(tenantId, locale));
templates.Add(CreateWebhookWarningTemplate(tenantId, locale));
// Add exceeded templates
templates.Add(CreateSlackExceededTemplate(tenantId, locale));
templates.Add(CreateTeamsExceededTemplate(tenantId, locale));
templates.Add(CreateEmailExceededTemplate(tenantId, locale));
templates.Add(CreateWebhookExceededTemplate(tenantId, locale));
return templates;
}
#region Warning Templates
private static NotifyTemplate CreateSlackWarningTemplate(string tenantId, string locale) =>
NotifyTemplate.Create(
templateId: $"tmpl-budget-warning-slack-{tenantId}",
tenantId: tenantId,
channelType: NotifyChannelType.Slack,
key: BudgetWarningKey,
locale: locale,
body: SlackWarningBody,
renderMode: NotifyTemplateRenderMode.Markdown,
format: NotifyDeliveryFormat.Slack,
description: "Slack notification for risk budget warning threshold crossed",
metadata: CreateMetadata("1.0.0"),
createdBy: "system:budget-templates");
private static NotifyTemplate CreateTeamsWarningTemplate(string tenantId, string locale) =>
NotifyTemplate.Create(
templateId: $"tmpl-budget-warning-teams-{tenantId}",
tenantId: tenantId,
channelType: NotifyChannelType.Teams,
key: BudgetWarningKey,
locale: locale,
body: TeamsWarningBody,
renderMode: NotifyTemplateRenderMode.Markdown,
format: NotifyDeliveryFormat.Teams,
description: "Teams notification for risk budget warning threshold crossed",
metadata: CreateMetadata("1.0.0"),
createdBy: "system:budget-templates");
private static NotifyTemplate CreateEmailWarningTemplate(string tenantId, string locale) =>
NotifyTemplate.Create(
templateId: $"tmpl-budget-warning-email-{tenantId}",
tenantId: tenantId,
channelType: NotifyChannelType.Email,
key: BudgetWarningKey,
locale: locale,
body: EmailWarningBody,
renderMode: NotifyTemplateRenderMode.Html,
format: NotifyDeliveryFormat.Html,
description: "Email notification for risk budget warning threshold crossed",
metadata: CreateMetadata("1.0.0"),
createdBy: "system:budget-templates");
private static NotifyTemplate CreateWebhookWarningTemplate(string tenantId, string locale) =>
NotifyTemplate.Create(
templateId: $"tmpl-budget-warning-webhook-{tenantId}",
tenantId: tenantId,
channelType: NotifyChannelType.Webhook,
key: BudgetWarningKey,
locale: locale,
body: WebhookWarningBody,
renderMode: NotifyTemplateRenderMode.None,
format: NotifyDeliveryFormat.Json,
description: "Webhook notification for risk budget warning threshold crossed",
metadata: CreateMetadata("1.0.0"),
createdBy: "system:budget-templates");
#endregion
#region Exceeded Templates
private static NotifyTemplate CreateSlackExceededTemplate(string tenantId, string locale) =>
NotifyTemplate.Create(
templateId: $"tmpl-budget-exceeded-slack-{tenantId}",
tenantId: tenantId,
channelType: NotifyChannelType.Slack,
key: BudgetExceededKey,
locale: locale,
body: SlackExceededBody,
renderMode: NotifyTemplateRenderMode.Markdown,
format: NotifyDeliveryFormat.Slack,
description: "Slack notification for risk budget exceeded",
metadata: CreateMetadata("1.0.0"),
createdBy: "system:budget-templates");
private static NotifyTemplate CreateTeamsExceededTemplate(string tenantId, string locale) =>
NotifyTemplate.Create(
templateId: $"tmpl-budget-exceeded-teams-{tenantId}",
tenantId: tenantId,
channelType: NotifyChannelType.Teams,
key: BudgetExceededKey,
locale: locale,
body: TeamsExceededBody,
renderMode: NotifyTemplateRenderMode.Markdown,
format: NotifyDeliveryFormat.Teams,
description: "Teams notification for risk budget exceeded",
metadata: CreateMetadata("1.0.0"),
createdBy: "system:budget-templates");
private static NotifyTemplate CreateEmailExceededTemplate(string tenantId, string locale) =>
NotifyTemplate.Create(
templateId: $"tmpl-budget-exceeded-email-{tenantId}",
tenantId: tenantId,
channelType: NotifyChannelType.Email,
key: BudgetExceededKey,
locale: locale,
body: EmailExceededBody,
renderMode: NotifyTemplateRenderMode.Html,
format: NotifyDeliveryFormat.Html,
description: "Email notification for risk budget exceeded",
metadata: CreateMetadata("1.0.0"),
createdBy: "system:budget-templates");
private static NotifyTemplate CreateWebhookExceededTemplate(string tenantId, string locale) =>
NotifyTemplate.Create(
templateId: $"tmpl-budget-exceeded-webhook-{tenantId}",
tenantId: tenantId,
channelType: NotifyChannelType.Webhook,
key: BudgetExceededKey,
locale: locale,
body: WebhookExceededBody,
renderMode: NotifyTemplateRenderMode.None,
format: NotifyDeliveryFormat.Json,
description: "Webhook notification for risk budget exceeded",
metadata: CreateMetadata("1.0.0"),
createdBy: "system:budget-templates");
#endregion
#region Template Bodies
private const string SlackWarningBody = """
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": ":warning: Risk Budget Warning",
"emoji": true
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Service:*\n{{payload.serviceId}}"
},
{
"type": "mrkdwn",
"text": "*Status:*\n{{payload.status | uppercase}}"
}
]
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Consumed:*\n{{payload.consumed}} / {{payload.allocated}} points"
},
{
"type": "mrkdwn",
"text": "*Usage:*\n{{payload.percentageUsed}}%"
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":chart_with_upwards_trend: Budget window: *{{payload.window}}* | Tier: *{{payload.tier}}*"
}
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "Remaining: {{payload.remaining}} points | {{payload.timestamp}}"
}
]
}
]
}
""";
private const string SlackExceededBody = """
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": ":rotating_light: Risk Budget EXHAUSTED",
"emoji": true
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Service {{payload.serviceId}} has exhausted its risk budget!*\nNew high-risk releases may be blocked until budget resets."
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Consumed:*\n{{payload.consumed}} / {{payload.allocated}} points"
},
{
"type": "mrkdwn",
"text": "*Overage:*\n{{#if payload.remaining}}{{payload.remaining | abs}} points over{{else}}At limit{{/if}}"
}
]
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Window:*\n{{payload.window}}"
},
{
"type": "mrkdwn",
"text": "*Tier:*\n{{payload.tier}}"
}
]
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":bulb: *Actions:*\n• Review pending releases for risk reduction\n• Request an exception if critical\n• Wait for next budget window"
}
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "Alert generated at {{payload.timestamp}}"
}
]
}
]
}
""";
private const string TeamsWarningBody = """
{
"@type": "MessageCard",
"@context": "http://schema.org/extensions",
"themeColor": "FFA500",
"summary": "Risk Budget Warning - {{payload.serviceId}}",
"sections": [
{
"activityTitle": "⚠️ Risk Budget Warning",
"activitySubtitle": "Service: {{payload.serviceId}}",
"facts": [
{ "name": "Status", "value": "{{payload.status | uppercase}}" },
{ "name": "Consumed", "value": "{{payload.consumed}} / {{payload.allocated}} points" },
{ "name": "Usage", "value": "{{payload.percentageUsed}}%" },
{ "name": "Remaining", "value": "{{payload.remaining}} points" },
{ "name": "Window", "value": "{{payload.window}}" },
{ "name": "Tier", "value": "{{payload.tier}}" }
],
"markdown": true
}
]
}
""";
private const string TeamsExceededBody = """
{
"@type": "MessageCard",
"@context": "http://schema.org/extensions",
"themeColor": "FF0000",
"summary": "Risk Budget EXHAUSTED - {{payload.serviceId}}",
"sections": [
{
"activityTitle": "🚨 Risk Budget EXHAUSTED",
"activitySubtitle": "Service: {{payload.serviceId}}",
"activityText": "This service has exhausted its risk budget. New high-risk releases may be blocked until the budget resets.",
"facts": [
{ "name": "Consumed", "value": "{{payload.consumed}} / {{payload.allocated}} points" },
{ "name": "Window", "value": "{{payload.window}}" },
{ "name": "Tier", "value": "{{payload.tier}}" }
],
"markdown": true
},
{
"text": "**Recommended Actions:**\n- Review pending releases for risk reduction\n- Request an exception if release is critical\n- Wait for next budget window reset"
}
]
}
""";
private const string EmailWarningBody = """
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
.container { max-width: 600px; margin: 0 auto; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.header { background: #FFA500; color: white; padding: 20px; }
.header h1 { margin: 0; font-size: 24px; }
.content { padding: 20px; }
.stats { display: flex; flex-wrap: wrap; gap: 20px; margin: 20px 0; }
.stat { flex: 1; min-width: 120px; background: #f8f9fa; padding: 15px; border-radius: 4px; }
.stat-label { font-size: 12px; color: #666; text-transform: uppercase; }
.stat-value { font-size: 24px; font-weight: bold; color: #333; }
.progress { background: #e9ecef; border-radius: 4px; height: 8px; margin: 20px 0; overflow: hidden; }
.progress-bar { background: #FFA500; height: 100%; transition: width 0.3s; }
.footer { padding: 15px 20px; background: #f8f9fa; font-size: 12px; color: #666; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1> Risk Budget Warning</h1>
</div>
<div class="content">
<p>The risk budget for <strong>{{payload.serviceId}}</strong> has crossed the <strong>{{payload.status}}</strong> threshold.</p>
<div class="stats">
<div class="stat">
<div class="stat-label">Consumed</div>
<div class="stat-value">{{payload.consumed}}</div>
</div>
<div class="stat">
<div class="stat-label">Allocated</div>
<div class="stat-value">{{payload.allocated}}</div>
</div>
<div class="stat">
<div class="stat-label">Remaining</div>
<div class="stat-value">{{payload.remaining}}</div>
</div>
</div>
<div class="progress">
<div class="progress-bar" style="width: {{payload.percentageUsed}}%"></div>
</div>
<p style="text-align: center; color: #666;">{{payload.percentageUsed}}% of budget consumed</p>
<p><strong>Window:</strong> {{payload.window}} | <strong>Tier:</strong> {{payload.tier}}</p>
</div>
<div class="footer">
Alert generated at {{payload.timestamp}} by StellaOps Policy Engine
</div>
</div>
</body>
</html>
""";
private const string EmailExceededBody = """
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
.container { max-width: 600px; margin: 0 auto; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.header { background: #DC3545; color: white; padding: 20px; }
.header h1 { margin: 0; font-size: 24px; }
.content { padding: 20px; }
.alert-box { background: #fff3cd; border: 1px solid #ffc107; border-radius: 4px; padding: 15px; margin: 15px 0; }
.stats { display: flex; flex-wrap: wrap; gap: 20px; margin: 20px 0; }
.stat { flex: 1; min-width: 120px; background: #f8f9fa; padding: 15px; border-radius: 4px; }
.stat-label { font-size: 12px; color: #666; text-transform: uppercase; }
.stat-value { font-size: 24px; font-weight: bold; color: #333; }
.actions { background: #e7f3ff; border-radius: 4px; padding: 15px; margin: 15px 0; }
.actions h3 { margin: 0 0 10px 0; color: #0066cc; }
.actions ul { margin: 0; padding-left: 20px; }
.footer { padding: 15px 20px; background: #f8f9fa; font-size: 12px; color: #666; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🚨 Risk Budget EXHAUSTED</h1>
</div>
<div class="content">
<div class="alert-box">
<strong>Service {{payload.serviceId}} has exhausted its risk budget!</strong><br>
New high-risk releases (G3+) will be blocked until the budget resets.
</div>
<div class="stats">
<div class="stat">
<div class="stat-label">Consumed</div>
<div class="stat-value">{{payload.consumed}}</div>
</div>
<div class="stat">
<div class="stat-label">Allocated</div>
<div class="stat-value">{{payload.allocated}}</div>
</div>
<div class="stat">
<div class="stat-label">Status</div>
<div class="stat-value" style="color: #DC3545;">EXHAUSTED</div>
</div>
</div>
<p><strong>Window:</strong> {{payload.window}} | <strong>Tier:</strong> {{payload.tier}}</p>
<div class="actions">
<h3>Recommended Actions</h3>
<ul>
<li>Review pending releases for risk reduction opportunities</li>
<li>Request an exception if the release is business-critical</li>
<li>Wait for the next budget window to reset</li>
<li>Contact your security team for guidance</li>
</ul>
</div>
</div>
<div class="footer">
Alert generated at {{payload.timestamp}} by StellaOps Policy Engine
</div>
</div>
</body>
</html>
""";
private const string WebhookWarningBody = """
{
"event": "policy.budget.warning",
"severity": "{{payload.severity}}",
"service": {
"id": "{{payload.serviceId}}",
"tier": {{payload.tier}}
},
"budget": {
"id": "{{payload.budgetId}}",
"window": "{{payload.window}}",
"allocated": {{payload.allocated}},
"consumed": {{payload.consumed}},
"remaining": {{payload.remaining}},
"percentageUsed": {{payload.percentageUsed}},
"status": "{{payload.status}}",
"previousStatus": "{{payload.previousStatus}}"
},
"timestamp": "{{payload.timestamp}}"
}
""";
private const string WebhookExceededBody = """
{
"event": "policy.budget.exceeded",
"severity": "critical",
"service": {
"id": "{{payload.serviceId}}",
"tier": {{payload.tier}}
},
"budget": {
"id": "{{payload.budgetId}}",
"window": "{{payload.window}}",
"allocated": {{payload.allocated}},
"consumed": {{payload.consumed}},
"remaining": {{payload.remaining}},
"percentageUsed": {{payload.percentageUsed}},
"status": "exhausted"
},
"impact": {
"blockingEnabled": true,
"affectedRiskLevels": ["G3", "G4", "G5"]
},
"timestamp": "{{payload.timestamp}}"
}
""";
#endregion
private static IEnumerable<KeyValuePair<string, string>> CreateMetadata(string version) =>
new[]
{
new KeyValuePair<string, string>("version", version),
new KeyValuePair<string, string>("source", "budget-alert-templates"),
new KeyValuePair<string, string>("sprint", "SPRINT_20251226_002_BE_budget_enforcement")
};
}