6.4 KiB
6.4 KiB
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
lastSeenDESC; pagination uses a deterministiclimit. - Limits: observation projection default
limit=200, max500; chunk stream defaultlimit=500, max2000. - Tenancy: reads respect
X-Stella-Tenantwhen provided; otherwise fall back toDefaultTenantconfiguration. - Auth: bearer token with
vex.readscope 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 inVexClaimStatus)since(ISO-8601, UTC)limit(ints within bounds)
- Mapping back to storage:
observationId={providerId}:{document.digest}document.digestlocates the raw record invex_raw.anchorscontain JSON pointers/paragraph locators from source metadata.
Headers:
Excititor-Results-Truncated: true|falseExcititor-Results-Total: <int>
/v1/vex/evidence/chunks
- Query params:
vulnerabilityId(required),productKey(required), optionalproviderId,status,since,limit. - Limits: default
limit=500, max2000. - 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
sinceor narrowing providers/statuses.
OpenAPI (excerpt):
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):
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:
{"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:
VexAttestationPayloadwith fields:attestationId,supplierId,observationId,linksetId,vulnerabilityId,productKey,justificationSummary,issuedAt,metadata{}.
- Semantics:
attestationIdmatches the export/attestation ID used when signing (Resolve/Worker flows).observationId/linksetIdmap back to evidence identifiers; clients can stitch provenance for citations.
- Auth:
vex.readscope; tenant header optional (payloads are tenant-agnostic).
Error model
- Standard API envelope with
ValidationProblemfor missing required params. scopefailures return403with 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.