Add unit tests for SBOM ingestion and transformation
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Implement `SbomIngestServiceCollectionExtensionsTests` to verify the SBOM ingestion pipeline exports snapshots correctly.
- Create `SbomIngestTransformerTests` to ensure the transformation produces expected nodes and edges, including deduplication of license nodes and normalization of timestamps.
- Add `SbomSnapshotExporterTests` to test the export functionality for manifest, adjacency, nodes, and edges.
- Introduce `VexOverlayTransformerTests` to validate the transformation of VEX nodes and edges.
- Set up project file for the test project with necessary dependencies and configurations.
- Include JSON fixture files for testing purposes.
This commit is contained in:
master
2025-11-04 07:49:39 +02:00
parent f72c5c513a
commit 2eb6852d34
491 changed files with 39445 additions and 3917 deletions

210
docs/advisory-ai/api.md Normal file
View File

@@ -0,0 +1,210 @@
> **Imposed rule:** Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied.
# Advisory AI API Reference (Sprint 110 Preview)
_Updated: 2025-11-03 • Owner: Docs Guild & Advisory AI Guild • Status: In progress_
## 1. Overview
The Advisory AI service exposes deterministic, guardrail-enforced endpoints for generating advisory summaries, conflict explanations, and remediation plans. Each request is backed by the Aggregation-Only Contract (AOC); inputs originate from immutable Conseiller/Excititor evidence and SBOM context, and every output ships with verifiable citations and cache digests.
This document captures the API surface targeted for Sprint 110. The surface is gated behind Authority scopes and designed to operate identically online or offline (local inference profiles).
## 2. Base conventions
| Item | Value |
|------|-------|
| Base path | `/v1/advisory-ai` |
| Media types | `application/json` (request + response) |
| Authentication | OAuth2 access token (JWT, DPoP-bound or mTLS as per tenant policy) |
| Required scopes | See [Authentication & scopes](#3-authentication--scopes) |
| Idempotency | Requests are cached by `(taskType, advisoryKey, policyVersion, profile, artifactId/purl, preferredSections)` unless `forceRefresh` is `true` |
| Determinism | Guardrails reject outputs lacking citations; cache digests allow replay and offline verification |
## 3. Authentication & scopes
Advisory AI calls must include `aoc:verify` plus an Advisory AI scope. Authority enforces tenant binding for all combinations.
| Scope | Purpose | Typical principals |
|-------|---------|--------------------|
| `advisory-ai:view` | Read cached artefacts (`GET /outputs/{{hash}}`) | Console backend, evidence exporters |
| `advisory-ai:operate` | Submit inference jobs (`POST /summaries`, `/conflicts`, `/remediation`) | Platform services, CLI automation |
| `advisory-ai:admin` | Manage profiles & policy (`PATCH /profiles`, future) | Platform operators |
Requests without `aoc:verify` are rejected with `invalid_scope`. Tokens aimed at remote inference profiles must also satisfy tenant consent (`requireTenantConsent` in Authority config).
## 4. Profiles & inference selection
Profiles determine which model backend and guardrail stack execute the request. The `profile` field defaults to `default` (`fips-local`).
| Profile | Description |
|---------|-------------|
| `default` / `fips-local` | Local deterministic model packaged with Offline Kit; FIPS-compliant crypto |
| `gost-local` | Local profile using GOST-approved crypto stack |
| `cloud-openai` | Remote inference via cloud connector (disabled unless tenant consent flag set) |
| Custom | Installations may register additional profiles via Authority `advisory-ai` admin APIs |
## 5. Common request envelope
All task endpoints accept the same JSON payload; `taskType` is implied by the route.
```json
{
"advisoryKey": "csaf:redhat:RHSA-2025:1001",
"artifactId": "registry.stella-ops.internal/runtime/api",
"artifactPurl": "pkg:oci/runtime-api@sha256:d2c3...",
"policyVersion": "2025.10.1",
"profile": "fips-local",
"preferredSections": ["Summary", "Remediation"],
"forceRefresh": false
}
```
Field notes:
- `advisoryKey` **required**. Matches Conseiller advisory identifier or VEX statement key.
- `artifactId` / `artifactPurl` optional but recommended for remediation tasks (enables SBOM context).
- `policyVersion` locks evaluation to a specific Policy Engine digest. Omit for "current".
- `profile` selects inference profile (see §4). Unknown values return `400`.
- `preferredSections` prioritises advisory sections; the orchestrator still enforces AOC.
- `forceRefresh` bypasses cache, regenerating output and resealing DSSE bundle.
## 6. Responses & caching
Successful responses share a common envelope:
```json
{
"taskType": "Summary",
"profile": "fips-local",
"generatedAt": "2025-11-03T18:22:43Z",
"inputDigest": "sha256:6f3b...",
"outputHash": "sha256:1d7e...",
"ttlSeconds": 86400,
"content": {
"format": "markdown",
"body": "### Summary
1. [Vendor statement][1] ..."
},
"citations": [
{
"index": 1,
"kind": "advisory",
"sourceId": "concelier:csaf:redhat:RHSA-2025:1001:paragraph:12",
"uri": "https://access.redhat.com/errata/RHSA-2025:1001"
}
],
"context": {
"planCacheKey": "adv-summary:csaf:redhat:RHSA-2025:1001:fips-local",
"chunks": 42,
"vectorMatches": 12,
"sbom": {
"artifactId": "registry.stella-ops.internal/runtime/api",
"versionTimeline": 8,
"dependencyPaths": 5,
"dependencyNodes": 17
}
}
}
```
- `content.format` is `markdown` for human-readable payloads; machine-readable JSON attachments will use `json`. The CLI and Console render Markdown directly.
- `citations` indexes correspond to bracketed references in the Markdown body.
- `context.planCacheKey` lets operators resubmit the same request or inspect the plan (`GET /v1/advisory-ai/plans/`cacheKey``) optional when enabled.
- Cached copies honour tenant-specific TTLs (default 24h). Exceeding TTL triggers regeneration on next request.
## 7. Endpoints
### 7.1 `POST /v1/advisory-ai/summaries`
Generate or retrieve a cached advisory summary. Requires `advisory-ai:operate`.
- **Request body:** Common envelope (preferred sections default to `Summary`).
- **Response:** Summary output (see §6 example).
- **Errors:**
- `400 advisory.summary.missingAdvisoryKey` empty or malformed `advisoryKey`.
- `404 advisory.summary.advisoryNotFound` Conseiller cannot resolve the advisory or tenant forbidden.
- `409 advisory.summary.contextUnavailable` SBOM context still indexing; retry later.
### 7.2 `POST /v1/advisory-ai/conflicts`
Explain conflicting VEX statements, ranked by trust metadata.
- **Additional payload hints:** Set `preferredSections` to include `Conflicts` or targeted statement IDs.
- **Response extensions:** `content.format` remains Markdown; `context.conflicts` array highlights conflicting statement IDs and trust scores.
- **Errors:** include `404 advisory.conflict.vexNotFound`, `409 advisory.conflict.trustDataPending` (waiting on Excititor linksets).
### 7.3 `POST /v1/advisory-ai/remediation`
Produce remediation plan with fix versions and verification steps.
- **Additional payload hints:** Provide `artifactId` or `artifactPurl` to unlock SBOM timeline + dependency analysis.
- **Response extensions:** `content.format` Markdown plus `context.remediation` with recommended fix versions (`package`, `fixedVersion`, `rationale`).
- **Errors:** `422 advisory.remediation.noFixAvailable` (vendor has not published fix), `409 advisory.remediation.policyHold` (policy forbids automated remediation).
### 7.4 `GET /v1/advisory-ai/outputs/{{outputHash}}`
Fetch cached artefact (same envelope as §6). Requires `advisory-ai:view`.
- **Headers:** Supports `If-None-Match` with the `outputHash` (Etag) for cache validation.
- **Errors:** `404 advisory.output.notFound` if cache expired or tenant lacks access.
### 7.5 `GET /v1/advisory-ai/plans/{{cacheKey}}` (optional)
When plan preview is enabled (feature flag `advisoryAi.planPreview.enabled`), this endpoint returns the orchestration plan using `AdvisoryPipelinePlanResponse` (task metadata, chunk/vector counts). Requires `advisory-ai:operate`.
## 8. Error model
Errors follow a standard problem+JSON envelope:
```json
{
"status": 400,
"code": "advisory.summary.missingAdvisoryKey",
"message": "advisoryKey must be provided",
"traceId": "01HECAJ6RE8T5H4P6Q0XZ7ZD4T",
"retryAfter": 30
}
```
| HTTP | Code prefix | Meaning |
|------|-------------|---------|
| 400 | `advisory.summary.*`, `advisory.remediation.*` | Validation failures or unsupported profile/task combinations |
| 401 | `auth.invalid_token` | Token expired/invalid; ensure DPoP proof matches access token |
| 403 | `auth.insufficient_scope` | Missing `advisory-ai` scope or tenant consent |
| 404 | `advisory.*.notFound` | Advisory/key not available for tenant |
| 409 | `advisory.*.contextUnavailable` | Dependencies (SBOM, VEX, policy) not ready; retry after indicated seconds |
| 422 | `advisory.*.noFixAvailable` | Remediation cannot be produced given current evidence |
| 429 | `rate_limit.exceeded` | Caller breached tenant or profile rate limit; examine `Retry-After` |
| 503 | `advisory.backend.unavailable` | Model backend offline or remote profile disabled |
All errors include `traceId` for cross-service correlation and log search.
## 9. Rate limiting & quotas
Advisory AI honours per-tenant quotas configured under `advisoryAi.rateLimits`:
- Default: 30 summary/conflict requests per minute per tenant & profile.
- Remediation requests default to 10/minute due to heavier SBOM analysis.
- Cached `GET /outputs/{{hash}}` calls share the `advisory-ai:view` bucket (60/minute).
Limits are enforced at the gateway; the API returns `429` with standard `Retry-After` seconds. Operators can adjust limits via Authority configuration bundles and propagate offline using the Offline Kit.
## 10. Observability & audit
- Metrics: `advisory_ai_requests_total``tenant,task,profile``, `advisory_ai_latency_seconds`, `advisory_ai_validation_failures_total`, `advisory_ai_cache_hits_total`.
- Logs: Structured with `traceId`, `tenant`, `task`, `profile`, `outputHash`, `cacheStatus` (`hit`|`miss`|`bypass`). Prompt bodies are **never** logged; guardrail violations emit sanitized snippets only.
- Audit events: `advisory_ai.output.generated`, `advisory_ai.output.accessed`, `advisory_ai.guardrail.blocked` ship to the Authority audit stream with tenant + actor metadata.
## 11. Offline & sovereignty considerations
- Offline installations bundle prompt templates, guardrail configs, and local model weights. Remote profiles (`cloud-openai`) remain disabled unless operators explicitly enable them and record consent per tenant.
- Cached outputs include DSSE attestations when DSSE mode is enabled. Export Center ingests cached artefacts via `GET /outputs/{{hash}}` using `advisory-ai:view`.
- Force-refresh regenerates outputs using the same cache key, allowing auditors to replay evidence during compliance reviews.
## 12. Change log
| Date (UTC) | Change |
|------------|--------|
| 2025-11-03 | Initial sprint-110 preview covering summary/conflict/remediation endpoints, cache retrieval, plan preview, and error/rate limit model. |