feat: Initialize Zastava Webhook service with TLS and Authority authentication
- Added Program.cs to set up the web application with Serilog for logging, health check endpoints, and a placeholder admission endpoint. - Configured Kestrel server to use TLS 1.3 and handle client certificates appropriately. - Created StellaOps.Zastava.Webhook.csproj with necessary dependencies including Serilog and Polly. - Documented tasks in TASKS.md for the Zastava Webhook project, outlining current work and exit criteria for each task.
This commit is contained in:
32
docs/notify/samples/notify-channel@1.sample.json
Normal file
32
docs/notify/samples/notify-channel@1.sample.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"schemaVersion": "notify.channel@1",
|
||||
"channelId": "channel-slack-sec-ops",
|
||||
"tenantId": "tenant-01",
|
||||
"name": "slack:sec-ops",
|
||||
"type": "slack",
|
||||
"displayName": "SecOps Slack",
|
||||
"description": "Primary incident response channel.",
|
||||
"config": {
|
||||
"secretRef": "ref://notify/channels/slack/sec-ops",
|
||||
"target": "#sec-ops",
|
||||
"properties": {
|
||||
"workspace": "stellaops-sec"
|
||||
},
|
||||
"limits": {
|
||||
"concurrency": 2,
|
||||
"requestsPerMinute": 60,
|
||||
"timeout": "PT10S"
|
||||
}
|
||||
},
|
||||
"enabled": true,
|
||||
"labels": {
|
||||
"team": "secops"
|
||||
},
|
||||
"metadata": {
|
||||
"createdByTask": "NOTIFY-MODELS-15-102"
|
||||
},
|
||||
"createdBy": "ops:amir",
|
||||
"createdAt": "2025-10-18T17:02:11+00:00",
|
||||
"updatedBy": "ops:amir",
|
||||
"updatedAt": "2025-10-18T17:45:00+00:00"
|
||||
}
|
||||
34
docs/notify/samples/notify-event@1.sample.json
Normal file
34
docs/notify/samples/notify-event@1.sample.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"eventId": "8a8d6a2f-9315-49fe-9d52-8fec79ec7aeb",
|
||||
"kind": "scanner.report.ready",
|
||||
"version": "1",
|
||||
"tenant": "tenant-01",
|
||||
"ts": "2025-10-19T03:58:42+00:00",
|
||||
"actor": "scanner-webservice",
|
||||
"scope": {
|
||||
"namespace": "prod-payment",
|
||||
"repo": "ghcr.io/acme/api",
|
||||
"digest": "sha256:79c1f9e5...",
|
||||
"labels": {
|
||||
"environment": "production"
|
||||
},
|
||||
"attributes": {}
|
||||
},
|
||||
"payload": {
|
||||
"delta": {
|
||||
"kev": [
|
||||
"CVE-2025-40123"
|
||||
],
|
||||
"newCritical": 1,
|
||||
"newHigh": 2
|
||||
},
|
||||
"links": {
|
||||
"rekor": "https://rekor.stella.local/api/v1/log/entries/1",
|
||||
"ui": "https://ui.stella.local/reports/sha256-79c1f9e5"
|
||||
},
|
||||
"verdict": "fail"
|
||||
},
|
||||
"attributes": {
|
||||
"correlationId": "scan-23a6"
|
||||
}
|
||||
}
|
||||
63
docs/notify/samples/notify-rule@1.sample.json
Normal file
63
docs/notify/samples/notify-rule@1.sample.json
Normal file
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"schemaVersion": "notify.rule@1",
|
||||
"ruleId": "rule-secops-critical",
|
||||
"tenantId": "tenant-01",
|
||||
"name": "Critical digests to SecOps",
|
||||
"description": "Escalate KEV-tagged findings to on-call feeds.",
|
||||
"enabled": true,
|
||||
"match": {
|
||||
"eventKinds": [
|
||||
"scanner.report.ready",
|
||||
"scheduler.rescan.delta"
|
||||
],
|
||||
"namespaces": [
|
||||
"prod-*"
|
||||
],
|
||||
"repositories": [],
|
||||
"digests": [],
|
||||
"labels": [],
|
||||
"componentPurls": [],
|
||||
"minSeverity": "high",
|
||||
"verdicts": [],
|
||||
"kevOnly": true,
|
||||
"vex": {
|
||||
"includeAcceptedJustifications": false,
|
||||
"includeRejectedJustifications": false,
|
||||
"includeUnknownJustifications": false,
|
||||
"justificationKinds": [
|
||||
"component-remediated",
|
||||
"not-affected"
|
||||
]
|
||||
}
|
||||
},
|
||||
"actions": [
|
||||
{
|
||||
"actionId": "email-digest",
|
||||
"channel": "email:soc",
|
||||
"digest": "hourly",
|
||||
"template": "digest",
|
||||
"enabled": true,
|
||||
"metadata": {
|
||||
"locale": "en-us"
|
||||
}
|
||||
},
|
||||
{
|
||||
"actionId": "slack-oncall",
|
||||
"channel": "slack:sec-ops",
|
||||
"template": "concise",
|
||||
"throttle": "PT5M",
|
||||
"metadata": {},
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"labels": {
|
||||
"team": "secops"
|
||||
},
|
||||
"metadata": {
|
||||
"source": "sprint-15"
|
||||
},
|
||||
"createdBy": "ops:zoya",
|
||||
"createdAt": "2025-10-19T04:12:27+00:00",
|
||||
"updatedBy": "ops:zoya",
|
||||
"updatedAt": "2025-10-19T04:45:03+00:00"
|
||||
}
|
||||
19
docs/notify/samples/notify-template@1.sample.json
Normal file
19
docs/notify/samples/notify-template@1.sample.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"schemaVersion": "notify.template@1",
|
||||
"templateId": "tmpl-slack-concise",
|
||||
"tenantId": "tenant-01",
|
||||
"channelType": "slack",
|
||||
"key": "concise",
|
||||
"locale": "en-us",
|
||||
"body": "{{severity_icon payload.delta.newCritical}} {{summary}}",
|
||||
"description": "Slack concise message for high severity findings.",
|
||||
"renderMode": "markdown",
|
||||
"format": "slack",
|
||||
"metadata": {
|
||||
"version": "2025-10-19"
|
||||
},
|
||||
"createdBy": "ops:zoya",
|
||||
"createdAt": "2025-10-19T05:00:00+00:00",
|
||||
"updatedBy": "ops:zoya",
|
||||
"updatedAt": "2025-10-19T05:45:00+00:00"
|
||||
}
|
||||
73
docs/notify/schemas/notify-channel@1.json
Normal file
73
docs/notify/schemas/notify-channel@1.json
Normal file
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"$id": "https://stella-ops.org/schemas/notify/notify-channel@1.json",
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Notify Channel",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"schemaVersion",
|
||||
"channelId",
|
||||
"tenantId",
|
||||
"name",
|
||||
"type",
|
||||
"config",
|
||||
"enabled",
|
||||
"createdAt",
|
||||
"updatedAt"
|
||||
],
|
||||
"properties": {
|
||||
"schemaVersion": {"type": "string", "const": "notify.channel@1"},
|
||||
"channelId": {"type": "string"},
|
||||
"tenantId": {"type": "string"},
|
||||
"name": {"type": "string"},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["slack", "teams", "email", "webhook", "custom"]
|
||||
},
|
||||
"displayName": {"type": "string"},
|
||||
"description": {"type": "string"},
|
||||
"config": {"$ref": "#/$defs/channelConfig"},
|
||||
"enabled": {"type": "boolean"},
|
||||
"labels": {"$ref": "#/$defs/stringMap"},
|
||||
"metadata": {"$ref": "#/$defs/stringMap"},
|
||||
"createdBy": {"type": "string"},
|
||||
"createdAt": {"type": "string", "format": "date-time"},
|
||||
"updatedBy": {"type": "string"},
|
||||
"updatedAt": {"type": "string", "format": "date-time"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"$defs": {
|
||||
"channelConfig": {
|
||||
"type": "object",
|
||||
"required": ["secretRef"],
|
||||
"properties": {
|
||||
"secretRef": {"type": "string"},
|
||||
"target": {"type": "string"},
|
||||
"endpoint": {"type": "string", "format": "uri"},
|
||||
"properties": {"$ref": "#/$defs/stringMap"},
|
||||
"limits": {"$ref": "#/$defs/channelLimits"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"channelLimits": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"concurrency": {"type": "integer", "minimum": 1},
|
||||
"requestsPerMinute": {"type": "integer", "minimum": 1},
|
||||
"timeout": {
|
||||
"type": "string",
|
||||
"pattern": "^P(T.*)?$",
|
||||
"description": "ISO 8601 duration"
|
||||
},
|
||||
"maxBatchSize": {"type": "integer", "minimum": 1}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"stringMap": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
".*": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
56
docs/notify/schemas/notify-event@1.json
Normal file
56
docs/notify/schemas/notify-event@1.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"$id": "https://stella-ops.org/schemas/notify/notify-event@1.json",
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Notify Event Envelope",
|
||||
"type": "object",
|
||||
"required": ["eventId", "kind", "tenant", "ts", "payload"],
|
||||
"properties": {
|
||||
"eventId": {"type": "string", "format": "uuid"},
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"description": "Event kind identifier (e.g. scanner.report.ready).",
|
||||
"enum": [
|
||||
"scanner.report.ready",
|
||||
"scanner.scan.completed",
|
||||
"scheduler.rescan.delta",
|
||||
"attestor.logged",
|
||||
"zastava.admission",
|
||||
"feedser.export.completed",
|
||||
"vexer.export.completed"
|
||||
]
|
||||
},
|
||||
"version": {"type": "string"},
|
||||
"tenant": {"type": "string"},
|
||||
"ts": {"type": "string", "format": "date-time"},
|
||||
"actor": {"type": "string"},
|
||||
"scope": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"namespace": {"type": "string"},
|
||||
"repo": {"type": "string"},
|
||||
"digest": {"type": "string"},
|
||||
"component": {"type": "string"},
|
||||
"image": {"type": "string"},
|
||||
"labels": {"$ref": "#/$defs/stringMap"},
|
||||
"attributes": {"$ref": "#/$defs/stringMap"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"payload": {
|
||||
"type": "object",
|
||||
"description": "Event specific body; see individual schemas for shapes.",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"attributes": {"$ref": "#/$defs/stringMap"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"$defs": {
|
||||
"stringMap": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
".*": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
96
docs/notify/schemas/notify-rule@1.json
Normal file
96
docs/notify/schemas/notify-rule@1.json
Normal file
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"$id": "https://stella-ops.org/schemas/notify/notify-rule@1.json",
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Notify Rule",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"schemaVersion",
|
||||
"ruleId",
|
||||
"tenantId",
|
||||
"name",
|
||||
"enabled",
|
||||
"match",
|
||||
"actions",
|
||||
"createdAt",
|
||||
"updatedAt"
|
||||
],
|
||||
"properties": {
|
||||
"schemaVersion": {"type": "string", "const": "notify.rule@1"},
|
||||
"ruleId": {"type": "string"},
|
||||
"tenantId": {"type": "string"},
|
||||
"name": {"type": "string"},
|
||||
"description": {"type": "string"},
|
||||
"enabled": {"type": "boolean"},
|
||||
"match": {"$ref": "#/$defs/ruleMatch"},
|
||||
"actions": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {"$ref": "#/$defs/ruleAction"}
|
||||
},
|
||||
"labels": {"$ref": "#/$defs/stringMap"},
|
||||
"metadata": {"$ref": "#/$defs/stringMap"},
|
||||
"createdBy": {"type": "string"},
|
||||
"createdAt": {"type": "string", "format": "date-time"},
|
||||
"updatedBy": {"type": "string"},
|
||||
"updatedAt": {"type": "string", "format": "date-time"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"$defs": {
|
||||
"ruleMatch": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"eventKinds": {"$ref": "#/$defs/stringArray"},
|
||||
"namespaces": {"$ref": "#/$defs/stringArray"},
|
||||
"repositories": {"$ref": "#/$defs/stringArray"},
|
||||
"digests": {"$ref": "#/$defs/stringArray"},
|
||||
"labels": {"$ref": "#/$defs/stringArray"},
|
||||
"componentPurls": {"$ref": "#/$defs/stringArray"},
|
||||
"minSeverity": {"type": "string"},
|
||||
"verdicts": {"$ref": "#/$defs/stringArray"},
|
||||
"kevOnly": {"type": "boolean"},
|
||||
"vex": {"$ref": "#/$defs/ruleMatchVex"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ruleMatchVex": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"includeAcceptedJustifications": {"type": "boolean"},
|
||||
"includeRejectedJustifications": {"type": "boolean"},
|
||||
"includeUnknownJustifications": {"type": "boolean"},
|
||||
"justificationKinds": {"$ref": "#/$defs/stringArray"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ruleAction": {
|
||||
"type": "object",
|
||||
"required": ["actionId", "channel", "enabled"],
|
||||
"properties": {
|
||||
"actionId": {"type": "string"},
|
||||
"channel": {"type": "string"},
|
||||
"template": {"type": "string"},
|
||||
"digest": {"type": "string"},
|
||||
"throttle": {
|
||||
"type": "string",
|
||||
"pattern": "^P(T.*)?$",
|
||||
"description": "ISO 8601 duration"
|
||||
},
|
||||
"locale": {"type": "string"},
|
||||
"enabled": {"type": "boolean"},
|
||||
"metadata": {"$ref": "#/$defs/stringMap"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"stringArray": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"}
|
||||
},
|
||||
"stringMap": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
".*": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
55
docs/notify/schemas/notify-template@1.json
Normal file
55
docs/notify/schemas/notify-template@1.json
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"$id": "https://stella-ops.org/schemas/notify/notify-template@1.json",
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Notify Template",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"schemaVersion",
|
||||
"templateId",
|
||||
"tenantId",
|
||||
"channelType",
|
||||
"key",
|
||||
"locale",
|
||||
"body",
|
||||
"renderMode",
|
||||
"format",
|
||||
"createdAt",
|
||||
"updatedAt"
|
||||
],
|
||||
"properties": {
|
||||
"schemaVersion": {"type": "string", "const": "notify.template@1"},
|
||||
"templateId": {"type": "string"},
|
||||
"tenantId": {"type": "string"},
|
||||
"channelType": {
|
||||
"type": "string",
|
||||
"enum": ["slack", "teams", "email", "webhook", "custom"]
|
||||
},
|
||||
"key": {"type": "string"},
|
||||
"locale": {"type": "string"},
|
||||
"body": {"type": "string"},
|
||||
"description": {"type": "string"},
|
||||
"renderMode": {
|
||||
"type": "string",
|
||||
"enum": ["markdown", "html", "adaptiveCard", "plainText", "json"]
|
||||
},
|
||||
"format": {
|
||||
"type": "string",
|
||||
"enum": ["slack", "teams", "email", "webhook", "json"]
|
||||
},
|
||||
"metadata": {"$ref": "#/$defs/stringMap"},
|
||||
"createdBy": {"type": "string"},
|
||||
"createdAt": {"type": "string", "format": "date-time"},
|
||||
"updatedBy": {"type": "string"},
|
||||
"updatedAt": {"type": "string", "format": "date-time"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"$defs": {
|
||||
"stringMap": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
".*": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user