consolidation of some of the modules, localization fixes, product advisories work, qa work
This commit is contained in:
125
docs-archived/modules/excititor/operations/evidence-api.md
Normal file
125
docs-archived/modules/excititor/operations/evidence-api.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# Excititor Advisory-AI evidence APIs (projection + chunks)
|
||||
|
||||
> Covers the read-only evidence surfaces shipped in Sprints 119–120: `/v1/vex/observations/{vulnerabilityId}/{productKey}` and `/v1/vex/evidence/chunks`.
|
||||
|
||||
## Scope and determinism
|
||||
|
||||
- **Aggregation-only**: no consensus, severity merging, or reachability. Responses carry raw statements plus provenance/signature metadata.
|
||||
- **Stable ordering**: both endpoints sort by `lastSeen` DESC; pagination uses a deterministic `limit`.
|
||||
- **Limits**: observation projection default `limit=200`, max `500`; chunk stream default `limit=500`, max `2000`.
|
||||
- **Tenancy**: reads respect `X-Stella-Tenant` when provided; otherwise fall back to `DefaultTenant` configuration.
|
||||
- **Auth**: bearer token with `vex.read` scope required.
|
||||
|
||||
## `/v1/vex/observations/{vulnerabilityId}/{productKey}`
|
||||
|
||||
- **Response**: JSON object with `vulnerabilityId`, `productKey`, `generatedAt`, `totalCount`, `truncated`, `statements[]`.
|
||||
- **Statement fields**: `observationId`, `providerId`, `status`, `justification`, `detail`, `firstSeen`, `lastSeen`, `scope{key,name,version,purl,cpe,componentIdentifiers[]}`, `anchors[]`, `document{digest,format,revision,sourceUri}`, `signature{type,keyId,issuer,verifiedAt}`.
|
||||
- **Filters**:
|
||||
- `providerId` (multi-valued, comma-separated)
|
||||
- `status` (values in `VexClaimStatus`)
|
||||
- `since` (ISO-8601, UTC)
|
||||
- `limit` (ints within bounds)
|
||||
- **Mapping back to storage**:
|
||||
- `observationId` = `{providerId}:{document.digest}`
|
||||
- `document.digest` locates the raw record in `vex_raw`.
|
||||
- `anchors` contain JSON pointers/paragraph locators from source metadata.
|
||||
|
||||
Headers:
|
||||
- `Excititor-Results-Truncated: true|false`
|
||||
- `Excititor-Results-Total: <int>`
|
||||
|
||||
## `/v1/vex/evidence/chunks`
|
||||
|
||||
- **Query params**: `vulnerabilityId` (required), `productKey` (required), optional `providerId`, `status`, `since`, `limit`.
|
||||
- **Limits**: default `limit=500`, max `2000`.
|
||||
- **Response**: **NDJSON** stream; each line is a `VexEvidenceChunkResponse`.
|
||||
- **Chunk fields**: `observationId`, `linksetId`, `vulnerabilityId`, `productKey`, `providerId`, `status`, `justification`, `detail`, `scopeScore` (from confidence or signals), `firstSeen`, `lastSeen`, `scope{...}`, `document{digest,format,sourceUri,revision}`, `signature{type,subject,issuer,keyId,verifiedAt,transparencyRef}`, `metadata` (flattened additionalMetadata).
|
||||
- **Headers**: `Excititor-Results-Total`, `Excititor-Results-Truncated` (mirrors projection API naming).
|
||||
- **Streaming guidance (SDK/clients)**:
|
||||
- Use HTTP client that supports response streaming; read line-by-line and JSON-deserialize per line.
|
||||
- Treat stream as an NDJSON list up to `limit`; no outer array.
|
||||
- Back-off or paginate by adjusting `since` or narrowing providers/statuses.
|
||||
|
||||
OpenAPI (excerpt):
|
||||
|
||||
```yaml
|
||||
paths:
|
||||
/v1/vex/evidence/chunks:
|
||||
get:
|
||||
summary: Stream evidence chunks for a vulnerability/product
|
||||
parameters:
|
||||
- in: query
|
||||
name: vulnerabilityId
|
||||
schema: { type: string }
|
||||
required: true
|
||||
- in: query
|
||||
name: productKey
|
||||
schema: { type: string }
|
||||
required: true
|
||||
- in: query
|
||||
name: providerId
|
||||
schema: { type: string }
|
||||
description: Comma-separated provider ids
|
||||
- in: query
|
||||
name: status
|
||||
schema: { type: string }
|
||||
description: Comma-separated VEX statuses
|
||||
- in: query
|
||||
name: since
|
||||
schema: { type: string, format: date-time }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, minimum: 1, maximum: 2000, default: 500 }
|
||||
responses:
|
||||
"200":
|
||||
description: NDJSON stream of VexEvidenceChunkResponse
|
||||
headers:
|
||||
Excititor-Results-Total: { schema: { type: integer } }
|
||||
Excititor-Results-Truncated: { schema: { type: boolean } }
|
||||
content:
|
||||
application/x-ndjson:
|
||||
schema:
|
||||
type: string
|
||||
description: One JSON object per line (VexEvidenceChunkResponse)
|
||||
```
|
||||
|
||||
Example (curl):
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer <token>" \
|
||||
-H "X-Stella-Tenant: acme" \
|
||||
"https://exc.example.test/v1/vex/evidence/chunks?vulnerabilityId=CVE-2025-0001&productKey=pkg:docker/demo&limit=2" |
|
||||
head -n 2
|
||||
```
|
||||
|
||||
Sample NDJSON line:
|
||||
|
||||
```json
|
||||
{"observationId":"provider-a:4d2f...","linksetId":"CVE-2025-0001:pkg:docker/demo","vulnerabilityId":"CVE-2025-0001","productKey":"pkg:docker/demo","providerId":"provider-a","status":"Affected","justification":"ComponentNotPresent","detail":"demo detail","scopeScore":0.9,"firstSeen":"2025-11-10T12:00:00Z","lastSeen":"2025-11-12T12:00:00Z","scope":{"key":"pkg:docker/demo","name":"demo","version":"1.0.0","purl":"pkg:docker/demo","cpe":null,"componentIdentifiers":["component-a"]},"document":{"digest":"sha256:e7...","format":"sbomcyclonedx","sourceUri":"https://example.test/vex.json","revision":"r1"},"signature":{"type":"cosign","subject":"demo","issuer":"issuer","keyId":"kid","verifiedAt":"2025-11-12T12:00:00Z","transparencyRef":null},"metadata":{}}
|
||||
```
|
||||
|
||||
## `/v1/vex/attestations/{attestationId}`
|
||||
|
||||
- **Purpose**: Lookup attestation provenance (supplier ↔ observation/linkset ↔ product/vulnerability) without touching consensus.
|
||||
- **Response**: `VexAttestationPayload` with fields:
|
||||
- `attestationId`, `supplierId`, `observationId`, `linksetId`, `vulnerabilityId`, `productKey`, `justificationSummary`, `issuedAt`, `metadata{}`.
|
||||
- **Semantics**:
|
||||
- `attestationId` matches the export/attestation ID used when signing (Resolve/Worker flows).
|
||||
- `observationId`/`linksetId` map back to evidence identifiers; clients can stitch provenance for citations.
|
||||
- **Auth**: `vex.read` scope; tenant header optional (payloads are tenant-agnostic).
|
||||
|
||||
## Error model
|
||||
|
||||
- Standard API envelope with `ValidationProblem` for missing required params.
|
||||
- `scope` failures return `403` with problem details.
|
||||
- Tenancy parse failures return `400`.
|
||||
|
||||
## Backwards compatibility
|
||||
|
||||
- No legacy routes are deprecated by these endpoints; they are additive and remain aggregation-only.
|
||||
|
||||
## References
|
||||
|
||||
- Implementation: `src/Excititor/StellaOps.Excititor.WebService/Program.cs` (`/v1/vex/observations/**`, `/v1/vex/evidence/chunks`).
|
||||
- Telemetry: `src/Excititor/StellaOps.Excititor.WebService/Telemetry/EvidenceTelemetry.cs` (`excititor.vex.observation.*`, `excititor.vex.chunks.*`).
|
||||
- Data model: `src/Excititor/StellaOps.Excititor.WebService/Contracts/VexObservationContracts.cs`, `Contracts/VexEvidenceChunkContracts.cs`.
|
||||
Reference in New Issue
Block a user