- Added ConsoleExportClient for managing export requests and responses. - Introduced ConsoleExportRequest and ConsoleExportResponse models. - Implemented methods for creating and retrieving exports with appropriate headers. feat(crypto): Add Software SM2/SM3 Cryptography Provider - Implemented SmSoftCryptoProvider for software-only SM2/SM3 cryptography. - Added support for signing and verification using SM2 algorithm. - Included hashing functionality with SM3 algorithm. - Configured options for loading keys from files and environment gate checks. test(crypto): Add unit tests for SmSoftCryptoProvider - Created comprehensive tests for signing, verifying, and hashing functionalities. - Ensured correct behavior for key management and error handling. feat(api): Enhance Console Export Models - Expanded ConsoleExport models to include detailed status and event types. - Added support for various export formats and notification options. test(time): Implement TimeAnchorPolicyService tests - Developed tests for TimeAnchorPolicyService to validate time anchors. - Covered scenarios for anchor validation, drift calculation, and policy enforcement.
14 KiB
Console Workspaces API
Tracking: CONSOLE-VULN-29-001, CONSOLE-VEX-30-001, DOCS-AIAI-31-004
1. Goals & Scope
The console workspaces provide read-only aggregates for Advisory AI operators:
/console/vuln/*surfaces tenant-scoped findings annotated with policy verdicts, VEX justifications, Scheduler reachability signals, and Advisory AI rationale./console/vex/*streams the underlying VEX statements, conflicts, and justification summaries (with SSE support for live updates).
All endpoints MUST:
- Remain deterministic offline (stable sort keys, ISO-8601 UTC timestamps, hashed assets).
- Operate with Authority-issued DPoP or mTLS client credentials that include
console:readand eithervuln:readorvex:read. - Respect tenant isolation – every request carries
X-StellaOps-Tenant.
2. Shared Request/Response Conventions
| Requirement | Description |
|---|---|
| Headers | Authorization: DPoP <token>, DPoP: <proof>, X-StellaOps-Tenant: <tenantId>, Accept: application/json (or text/event-stream for SSE). |
| Pagination | Cursor-based via pageToken; defaults to 50 items, max 200. Cursors are opaque, base64url, signed. |
| Sorting | Findings sorted by (severity desc, exploitScore desc, findingId asc). Statements sorted by (lastUpdated desc, statementId asc). |
| Dates | RFC 3339 / ISO-8601 UTC (e.g., 2025-11-08T12:02:11Z). |
| Determinism | All arrays must be pre-sorted; no server-generated uuids in responses. |
3. Vulnerability Workspace (/console/vuln/*)
3.1 GET /console/vuln/findings
Query parameters:
| Parameter | Type | Notes |
|---|---|---|
pageToken |
string | Optional cursor from previous response. |
pageSize |
int | 1-200, default 50. |
severity |
string[] | Accepts critical, high, medium, low, info. |
product |
string[] | SBOM purl or image digest anchors. |
policyBadge |
string[] | pass, warn, fail, waived. |
vexState |
string[] | not_affected, fixed, under_investigation, etc. |
reachability |
string[] | reachable, unreachable, unknown. |
search |
string | Substring match on CVE/GHSA/KEV ID (case-insensitive). |
Response body:
{
"items": [
{
"findingId": "tenant-default:advisory-ai:sha256:5d1a",
"coordinates": {
"advisoryId": "CVE-2024-12345",
"package": "pkg:npm/jsonwebtoken@9.0.2",
"component": "jwt-auth-service",
"image": "registry.local/ops/auth:2025.10.0"
},
"summary": "jsonwebtoken <10.0.0 allows algorithm downgrade.",
"severity": "high",
"cvss": 8.1,
"kev": true,
"policyBadge": "fail",
"vex": {
"statementId": "vex:tenant-default:jwt-auth:5d1a",
"state": "under_investigation",
"justification": "Advisory AI flagged reachable path via Scheduler run 42."
},
"reachability": {
"status": "reachable",
"lastObserved": "2025-11-07T23:11:04Z",
"signalsVersion": "signals-2025.310.1"
},
"evidence": {
"sbomDigest": "sha256:6c81…",
"policyRunId": "policy-run::2025-11-07::ca9f",
"attestationId": "dsse://authority/attest/84a2"
},
"timestamps": {
"firstSeen": "2025-10-31T04:22:18Z",
"lastSeen": "2025-11-07T23:16:51Z"
}
}
],
"facets": {
"severity": [
{ "value": "critical", "count": 2 },
{ "value": "high", "count": 7 }
],
"policyBadge": [
{ "value": "fail", "count": 6 },
{ "value": "warn", "count": 3 },
{ "value": "waived", "count": 1 }
],
"reachability": [
{ "value": "reachable", "count": 5 },
{ "value": "unreachable", "count": 2 },
{ "value": "unknown", "count": 1 }
]
},
"nextPageToken": "eyJjdXJzb3IiOiJmZjg0NiJ9"
}
3.2 GET /console/vuln/facets
Returns the full facet catalog (counts by severity, product, policy badge, VEX state, reachability, KEV flag). Designed for sidebar filters without paging; identical parameter surface as /findings.
3.3 GET /console/vuln/{findingId}
Returns the full finding document, including evidence timeline, policy overlays, and export-ready metadata:
{
"findingId": "tenant-default:advisory-ai:sha256:5d1a",
"details": {
"description": "jsonwebtoken <10.0.0 allows algorithm downgrade.",
"references": [
"https://nvd.nist.gov/vuln/detail/CVE-2024-12345",
"https://github.com/auth0/node-jsonwebtoken/security/advisories/GHSA-45mw-4jw3-g2wg"
],
"exploitAvailability": "known_exploit"
},
"policyBadges": [
{
"policyId": "policy://tenant-default/runtime-hardening",
"verdict": "fail",
"explainUrl": "https://console.local/policy/runs/policy-run::2025-11-07::ca9f"
}
],
"vex": {
"statementId": "vex:tenant-default:jwt-auth:5d1a",
"state": "under_investigation",
"justification": "Runtime telemetry confirmed exploitation path.",
"impactStatement": "Token exchange service remains exposed until patch 2025.11.2.",
"remediations": [
{
"type": "patch",
"description": "Upgrade jwt-auth-service to 2025.11.2.",
"deadline": "2025-11-12T00:00:00Z"
}
]
},
"reachability": {
"status": "reachable",
"callPathSamples": [
"api-gateway -> jwt-auth-service -> jsonwebtoken.verify"
],
"lastUpdated": "2025-11-07T23:11:04Z"
},
"evidence": {
"sbom": {
"digest": "sha256:6c81…",
"componentPath": [
"/src/jwt-auth/package.json",
"/src/jwt-auth/node_modules/jsonwebtoken"
]
},
"attestations": [
{
"type": "scan-report",
"attestationId": "dsse://authority/attest/84a2",
"signer": "attestor@stella-ops.org",
"bundleDigest": "sha256:e2bb…"
}
]
},
"timestamps": {
"firstSeen": "2025-10-31T04:22:18Z",
"lastSeen": "2025-11-07T23:16:51Z",
"vexLastUpdated": "2025-11-07T23:10:09Z"
}
}
3.4 POST /console/vuln/tickets
POST /console/vuln/tickets
{
"tenant": "tenant-default",
"selection": [
"tenant-default:advisory-ai:sha256:5d1a",
"tenant-default:advisory-ai:sha256:9bf4"
],
"targetSystem": "servicenow",
"metadata": {
"assignmentGroup": "runtime-security",
"priority": "P1"
}
}
Response:
{
"ticketId": "console-ticket::tenant-default::2025-11-08::00018",
"payload": {
"version": "2025-11-01",
"tenant": "tenant-default",
"findings": [
{ "findingId": "tenant-default:advisory-ai:sha256:5d1a", "severity": "high" },
{ "findingId": "tenant-default:advisory-ai:sha256:9bf4", "severity": "critical" }
],
"policyBadge": "fail",
"vexSummary": "2 reachable findings pending patch.",
"attachments": [
{
"type": "json",
"name": "console-ticket-20251108.json",
"digest": "sha256:1fdd…",
"contentType": "application/json",
"expiresAt": "2025-11-15T00:00:00Z"
}
]
},
"auditEventId": "console.ticket.export::2025-11-08::00018"
}
Requests emit console.ticket.export audit events (tenant, user, selection counts, target system).
4. VEX Workspace (/console/vex/*)
4.1 GET /console/vex/statements
Parameters mirror /console/vuln/findings plus:
| Parameter | Type | Notes |
|---|---|---|
advisoryId |
string[] | CVE/GHSA/OVAL identifiers. |
justification |
string[] | exploit_observed, component_not_present, etc. |
statementType |
string[] | vex, openvex, custom, advisory_ai. |
prefer |
string | prefer=stream enables chunked streaming (NDJSON). |
Response (paged JSON):
{
"items": [
{
"statementId": "vex:tenant-default:jwt-auth:5d1a",
"advisoryId": "CVE-2024-12345",
"product": "registry.local/ops/auth:2025.10.0",
"status": "under_investigation",
"justification": "exploit_observed",
"lastUpdated": "2025-11-07T23:10:09Z",
"source": {
"type": "advisory_ai",
"modelBuild": "aiai-console-2025-10-28",
"confidence": 0.74
},
"links": [
{
"rel": "finding",
"href": "/console/vuln/findings/tenant-default:advisory-ai:sha256:5d1a"
}
]
}
],
"nextPageToken": null
}
When Accept: text/event-stream, the endpoint emits events (see §4.3) instead of paged JSON.
4.2 GET /console/vex/statements/{statementId}
Returns the canonical statement plus provenance extracts. SSE clients can call this endpoint when they need full bodies after receiving a summary event.
4.3 GET /console/vex/events (SSE)
Streams live updates for VEX statements affecting the tenant:
- Event types:
statement.created,statement.updated,statement.deleted,statement.conflict. - Fields:
id,advisoryId,product,vexState,severityHint,policyBadge,conflictSummary,sequence. - Replay: Clients include
Last-Event-ID; server resumes from sequence. - Heartbeats every 15 seconds (
event: keepalive,data: {}).
Example event payload:
event: statement.updated
data: {
"statementId": "vex:tenant-default:jwt-auth:5d1a",
"advisoryId": "CVE-2024-12345",
"product": "registry.local/ops/auth:2025.10.0",
"state": "fixed",
"justification": "solution_available",
"sequence": 4182,
"updatedAt": "2025-11-08T11:44:32Z"
}
5. Signals & Scheduler Integration
- Reachability data is materialized by Scheduler delta jobs (
SCHED-CONSOLE-23-001)./console/vuln/findingsshould cache the most recent job ID and exposesignalsVersion. - VEX justification fields reference Excititor statement IDs; ensure the gateway checks Excititor availability and degrades gracefully (returns
state: unavailableplus telemetry). - Scheduler must publish
console.vuln.refreshevents whenever advisory/VEX deltas warrant workspace refresh; console SSE endpoint may piggyback on the same Redis/NATS channel.
6. Determinism & Offline Notes
- All responses are compressible JSON; no CDN fonts/assets referenced.
- SSE endpoints must tolerate sealed mode by operating on loopback addresses only.
authority-sealed-ci.json(see DEVOPS-AIRGAP-57-002) is the evidence Authority consumes before enabling these APIs for sealed tenants; configureairGap.sealedModeand setrequiresAirgapSealConfirmation: trueon the Console client so/tokenrefuses requests until the sealed harness uploads a fresh, passing artefact. Console responses echosealed: true/falseflags for UI badges once Authority has confirmed the evidence.
7. Sample Payloads for Docs
docs/api/console/samples/vuln-findings-sample.json– exported viascripts/generate-console-samples.ts(placeholder script to be added when backend lands).docs/api/console/samples/vex-statement-sse.ndjson– contains 5 chronological SSE events for screenshot reproduction.
Until backend implementations ship, use the examples above to unblock DOCS-AIAI-31-004; replace them with live captures once the gateway endpoints are available in staging.
Exports (draft contract v0.3)
Routes
POST /console/exports— start an evidence bundle export job.GET /console/exports/{exportId}— fetch job status and download locations.GET /console/exports/{exportId}/events— SSE stream of job progress (optional).
Security / headers
Authorization: DPoP <token>DPoP: <proof>X-StellaOps-Tenant: <tenantId>Idempotency-Key: <uuid>(recommended for POST)Accept: application/json(status) ortext/event-stream(events)- Required scopes:
console:readANDconsole:export(proposal).
Request body (POST)
{
"scope": { "tenantId": "t1", "projectId": "p1" },
"sources": [ { "type": "advisory", "ids": ["CVE-2024-12345"] } ],
"formats": ["json", "ndjson", "csv"],
"attestations": { "include": true, "sigstoreBundle": true },
"notify": { "webhooks": ["https://hooks.local/export"], "email": ["secops@example.com"] },
"priority": "normal"
}
Response: 202 Accepted
exportId: stringstatus:queued|running|succeeded|failed|expiredestimateSeconds: intretryAfter: int seconds (for polling)links:{ status: url, events?: url }
Response: GET status
{
"exportId": "console-export::tenant-default::2025-12-06::0007",
"status": "running",
"estimateSeconds": 420,
"outputs": [
{ "type": "manifest", "format": "json", "url": "https://.../manifest.json?sig=...", "sha256": "...", "expiresAt": "2025-12-06T13:10:00Z" }
],
"progress": { "percent": 42, "itemsCompleted": 210, "itemsTotal": 500, "assetsReady": 12 },
"errors": []
}
Response: SSE events
started:{ exportId, status }progress:{ exportId, percent, itemsCompleted, itemsTotal }asset_ready:{ exportId, type, id, url, sha256 }completed:{ exportId, status: "succeeded", manifestUrl }failed:{ exportId, status: "failed", code, message }
Manifest shape (downloaded via outputs)
version: string (date)exportId,tenantId,generatedAtitems[]:{ type: advisory|vex|policy|scan, id, url, sha256 }checksums:{ manifest, bundle }
Limits (proposed)
- Max request body 256 KiB; max sources 50; max outputs 1000 assets/export.
- Default job timeout 30 minutes; idle SSE timeout 60s; backoff via
Retry-After.
Error codes (proposal)
ERR_CONSOLE_EXPORT_INVALID_SOURCEERR_CONSOLE_EXPORT_TOO_LARGEERR_CONSOLE_EXPORT_RATE_LIMITERR_CONSOLE_EXPORT_UNAVAILABLE
Samples
- Request:
docs/api/console/samples/console-export-request.json - Status:
docs/api/console/samples/console-export-status.json - Manifest:
docs/api/console/samples/console-export-manifest.json - Events:
docs/api/console/samples/console-export-events.ndjson
Open items (needs guild sign-off)
- Final scopes list (
console:exportvs broaderconsole:*). - Final limits and error codes; checksum manifest format; attestation options.
- Caching/tie-break rules for downstream
/console/searchand/console/downloads.