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:
2025-10-19 18:36:22 +03:00
parent 7e2fa0a42a
commit 5ce40d2eeb
966 changed files with 91038 additions and 1850 deletions

View File

@@ -149,14 +149,114 @@ Client then generates SBOM **only** for the `missing` layers and reposts `/sc
---
### 2.3 Policy Endpoints
### 2.3 Policy Endpoints *(preview feature flag: `scanner.features.enablePolicyPreview`)*
| Method | Path | Purpose |
| ------ | ------------------ | ------------------------------------ |
| `GET` | `/policy/export` | Download live YAML ruleset |
| `POST` | `/policy/import` | Upload YAML or Rego; replaces active |
| `POST` | `/policy/validate` | Lint only; returns 400 on error |
| `GET` | `/policy/history` | Paginated change log (audit trail) |
All policy APIs require **`scanner.reports`** scope (or anonymous access while auth is disabled).
**Fetch schema**
```
GET /api/v1/policy/schema
Authorization: Bearer <token>
Accept: application/schema+json
```
Returns the embedded `policy-schema@1` JSON schema used by the binder.
**Run diagnostics**
```
POST /api/v1/policy/diagnostics
Content-Type: application/json
Authorization: Bearer <token>
```
```json
{
"policy": {
"format": "yaml",
"actor": "cli",
"description": "dev override",
"content": "version: \"1.0\"\nrules:\n - name: Quiet Dev\n environments: [dev]\n action:\n type: ignore\n justification: dev waiver\n"
}
}
```
**Response 200**:
```json
{
"success": false,
"version": "1.0",
"ruleCount": 1,
"errorCount": 0,
"warningCount": 1,
"generatedAt": "2025-10-19T03:25:14.112Z",
"issues": [
{ "code": "policy.rule.quiet.missing_vex", "message": "Quiet flag ignored: rule must specify requireVex justifications.", "severity": "Warning", "path": "$.rules[0]" }
],
"recommendations": [
"Review policy warnings and ensure intentional overrides are documented."
]
}
```
`success` is `false` when blocking issues remain; recommendations aggregate YAML ignore rules, VEX include/exclude hints, and vendor precedence guidance.
**Preview impact**
```
POST /api/v1/policy/preview
Authorization: Bearer <token>
Content-Type: application/json
```
```json
{
"imageDigest": "sha256:abc123",
"findings": [
{ "id": "finding-1", "severity": "Critical", "source": "NVD" }
],
"policy": {
"format": "yaml",
"content": "version: \"1.0\"\nrules:\n - name: Block Critical\n severity: [Critical]\n action: block\n"
}
}
```
**Response 200**:
```json
{
"success": true,
"policyDigest": "9c5e...",
"revisionId": "preview",
"changed": 1,
"diffs": [
{
"findingId": "finding-1",
"baseline": {"findingId": "finding-1", "status": "Pass"},
"projected": {
"findingId": "finding-1",
"status": "Blocked",
"ruleName": "Block Critical",
"ruleAction": "Block",
"score": 5.0,
"configVersion": "1.0",
"inputs": {"severityWeight": 5.0}
},
"changed": true
}
],
"issues": []
}
```
- Provide `policy` to preview staged changes; omit it to compare against the active snapshot.
- Baseline verdicts are optional; when omitted, the API synthesises pass baselines before computing diffs.
- Quieted verdicts include `quietedBy` and `quiet` flags; score inputs now surface reachability/vendor trust weights (`reachability.*`, `trustWeight.*`).
**OpenAPI**: the full API document (including these endpoints) is exposed at `/openapi/v1.json` and can be fetched for tooling or contract regeneration.
### 2.4 Scanner Queue a Scan Job *(SP9 milestone)*
@@ -238,6 +338,96 @@ Accept: application/json
Statuses: `Pending`, `Running`, `Succeeded`, `Failed`, `Cancelled`.
### 2.6 Scanner Stream Progress (SSE / JSONL)
```
GET /api/v1/scans/{scanId}/events?format=sse|jsonl
Authorization: Bearer <token with scanner.scans.read>
Accept: text/event-stream
```
When `format` is omitted the endpoint emits **Server-Sent Events** (SSE). Specify `format=jsonl` to receive newline-delimited JSON (`application/x-ndjson`). Response headers include `Cache-Control: no-store` and `X-Accel-Buffering: no` so intermediaries avoid buffering the stream.
**SSE frame** (default):
```
id: 1
event: pending
data: {"scanId":"2f6c17f9b3f548e2a28b9c412f4d63f8","sequence":1,"state":"Pending","message":"queued","timestamp":"2025-10-19T03:12:45.118Z","correlationId":"2f6c17f9b3f548e2a28b9c412f4d63f8:0001","data":{"force":false,"meta.pipeline":"github"}}
```
**JSONL frame** (`format=jsonl`):
```json
{"scanId":"2f6c17f9b3f548e2a28b9c412f4d63f8","sequence":1,"state":"Pending","message":"queued","timestamp":"2025-10-19T03:12:45.118Z","correlationId":"2f6c17f9b3f548e2a28b9c412f4d63f8:0001","data":{"force":false,"meta.pipeline":"github"}}
```
- `sequence` is monotonic starting at `1`.
- `correlationId` is deterministic (`{scanId}:{sequence:0000}`) unless a custom identifier is supplied by the publisher.
- `timestamp` is ISO8601 UTC with millisecond precision, ensuring deterministic ordering for consumers.
- The stream completes when the client disconnects or the coordinator stops publishing events.
### 2.7 Scanner Assemble Report (Signed Envelope)
```
POST /api/v1/reports
Authorization: Bearer <token with scanner.reports>
Content-Type: application/json
```
Request body mirrors policy preview inputs (image digest plus findings). The service evaluates the active policy snapshot, assembles a verdict, and signs the canonical report payload.
**Response 200**:
```json
{
"report": {
"reportId": "report-3def5f362aa475ef14b6",
"imageDigest": "sha256:deadbeef",
"verdict": "blocked",
"policy": { "revisionId": "rev-1", "digest": "27d2ec2b34feedc304fc564d252ecee1c8fa14ea581a5ff5c1ea8963313d5c8d" },
"summary": { "total": 1, "blocked": 1, "warned": 0, "ignored": 0, "quieted": 0 },
"verdicts": [
{
"findingId": "finding-1",
"status": "Blocked",
"ruleName": "Block Critical",
"ruleAction": "Block",
"score": 40.5,
"configVersion": "1.0",
"inputs": {
"reachabilityWeight": 0.45,
"baseScore": 40.5,
"severityWeight": 90,
"trustWeight": 1,
"trustWeight.NVD": 1,
"reachability.runtime": 0.45
},
"quiet": false,
"sourceTrust": "NVD",
"reachability": "runtime"
}
],
"issues": []
},
"dsse": {
"payloadType": "application/vnd.stellaops.report+json",
"payload": "<base64 canonical report>",
"signatures": [
{
"keyId": "scanner-report-signing",
"algorithm": "hs256",
"signature": "<base64 signature>"
}
]
}
}
```
- The `report` object omits null fields and is deterministic (ISO timestamps, sorted keys).
- `dsse` follows the DSSE (Dead Simple Signing Envelope) shape; `payload` is the canonical UTF-8 JSON and `signatures[0].signature` is the base64 HMAC/Ed25519 value depending on configuration.
- A runnable sample envelope is available at `samples/api/reports/report-sample.dsse.json` for tooling tests or signature verification.
**Response 404** `application/problem+json` payload with type `https://stellaops.org/problems/not-found` when the scan identifier is unknown.
> **Tip**  poll `Location` from the submission call until `status` transitions away from `Pending`/`Running`.
@@ -332,6 +522,7 @@ See `docs/dev/32_AUTH_CLIENT_GUIDE.md` for recommended profiles (online vs. air-
| `stellaops-cli auth revoke export` | Export the Authority revocation bundle | `--output <directory>` (defaults to CWD) | Writes `revocation-bundle.json`, `.json.jws`, and `.json.sha256`; verifies the digest locally and includes key metadata in the log summary. |
| `stellaops-cli auth revoke verify` | Validate a revocation bundle offline | `--bundle <path>` `--signature <path>` `--key <path>`<br>`--verbose` | Verifies detached JWS signatures, reports the computed SHA-256, and can fall back to cached JWKS when `--key` is omitted. |
| `stellaops-cli config show` | Display resolved configuration | — | Masks secret values; helpful for airgapped installs |
| `stellaops-cli runtime policy test` | Ask Scanner.WebService for runtime verdicts (Webhook parity) | `--image/-i <digest>` (repeatable, comma/space lists supported)<br>`--file/-f <path>`<br>`--namespace/--ns <name>`<br>`--label/-l key=value` (repeatable)<br>`--json` | Posts to `POST /api/v1/scanner/policy/runtime`, deduplicates image digests, and prints TTL + per-image verdict/signed/SBOM status. Accepts newline/whitespace-delimited stdin when piped; `--json` emits the raw response without additional logging. |
When running on an interactive terminal without explicit override flags, the CLI uses Spectre.Console prompts to let you choose per-run ORAS/offline bundle behaviour.