- Implemented comprehensive tests for VexLensNormalizer including format detection and normalization scenarios. - Added tests for CpeParser covering CPE 2.3 and 2.2 formats, invalid inputs, and canonical key generation. - Created tests for ProductMapper to validate parsing and matching logic across different strictness levels. - Developed tests for PurlParser to ensure correct parsing of various PURL formats and validation of identifiers. - Introduced stubs for Monaco editor and worker to facilitate testing in the web application. - Updated project file for the test project to include necessary dependencies.
1051 lines
30 KiB
YAML
1051 lines
30 KiB
YAML
# OpenAPI 3.1 specification for StellaOps VexLens WebService
|
|
openapi: 3.1.0
|
|
info:
|
|
title: StellaOps VexLens API
|
|
version: 0.1.0-draft
|
|
description: |
|
|
VexLens Consensus Engine API for computing VEX (Vulnerability Exploitability eXchange)
|
|
status consensus from multiple sources. Supports weighted voting, lattice-based consensus,
|
|
and authoritative-first resolution modes.
|
|
|
|
Uses the platform error envelope and tenant header `X-StellaOps-Tenant`.
|
|
servers:
|
|
- url: https://api.stellaops.example.com
|
|
description: Production
|
|
- url: https://api.dev.stellaops.example.com
|
|
description: Development
|
|
security:
|
|
- oauth2: [vexlens.viewer]
|
|
- oauth2: [vexlens.operator]
|
|
- oauth2: [vexlens.admin]
|
|
|
|
tags:
|
|
- name: Consensus
|
|
description: Compute and query VEX consensus
|
|
- name: Projections
|
|
description: Query stored consensus projections
|
|
- name: Issuers
|
|
description: Manage trusted VEX document issuers
|
|
- name: Statistics
|
|
description: Consensus statistics and analytics
|
|
|
|
paths:
|
|
/api/v1/vexlens/consensus:
|
|
post:
|
|
summary: Compute consensus for a vulnerability-product pair
|
|
description: |
|
|
Computes VEX status consensus from all available statements for a vulnerability-product pair.
|
|
Applies trust weighting, conflict detection, and returns a rationale for the decision.
|
|
tags: [Consensus]
|
|
operationId: computeConsensus
|
|
parameters:
|
|
- $ref: '#/components/parameters/Tenant'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ComputeConsensusRequest'
|
|
examples:
|
|
basic:
|
|
summary: Basic consensus request
|
|
value:
|
|
vulnerabilityId: CVE-2024-1234
|
|
productKey: pkg:npm/lodash@4.17.21
|
|
with-options:
|
|
summary: With consensus options
|
|
value:
|
|
vulnerabilityId: CVE-2024-1234
|
|
productKey: pkg:npm/lodash@4.17.21
|
|
mode: WeightedVote
|
|
minimumWeightThreshold: 0.2
|
|
storeResult: true
|
|
emitEvent: true
|
|
responses:
|
|
'200':
|
|
description: Consensus computed successfully
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ComputeConsensusResponse'
|
|
examples:
|
|
unanimous:
|
|
summary: Unanimous consensus
|
|
value:
|
|
vulnerabilityId: CVE-2024-1234
|
|
productKey: pkg:npm/lodash@4.17.21
|
|
status: not_affected
|
|
justification: vulnerable_code_not_present
|
|
confidenceScore: 0.95
|
|
outcome: Unanimous
|
|
rationale:
|
|
summary: "Unanimous consensus from 3 authoritative sources"
|
|
factors:
|
|
- "All statements agree on not_affected status"
|
|
- "Vendor statement with weight 0.98"
|
|
statusWeights:
|
|
not_affected: 2.85
|
|
contributions:
|
|
- statementId: stmt-vendor-001
|
|
issuerId: npm-security
|
|
status: not_affected
|
|
justification: vulnerable_code_not_present
|
|
weight: 0.98
|
|
contribution: 0.34
|
|
isWinner: true
|
|
conflicts: null
|
|
projectionId: proj-abc123
|
|
computedAt: "2025-12-06T12:00:00Z"
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
default:
|
|
$ref: '#/components/responses/Error'
|
|
|
|
/api/v1/vexlens/consensus/batch:
|
|
post:
|
|
summary: Compute consensus for multiple pairs in batch
|
|
description: |
|
|
Computes VEX status consensus for multiple vulnerability-product pairs in a single request.
|
|
Useful for bulk processing during ingestion or policy evaluation.
|
|
tags: [Consensus]
|
|
operationId: computeConsensusBatch
|
|
parameters:
|
|
- $ref: '#/components/parameters/Tenant'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ComputeConsensusBatchRequest'
|
|
examples:
|
|
batch:
|
|
summary: Batch of 3 targets
|
|
value:
|
|
targets:
|
|
- vulnerabilityId: CVE-2024-1234
|
|
productKey: pkg:npm/lodash@4.17.21
|
|
- vulnerabilityId: CVE-2024-5678
|
|
productKey: pkg:npm/express@4.18.2
|
|
- vulnerabilityId: CVE-2024-9012
|
|
productKey: pkg:maven/org.apache.logging.log4j/log4j-core@2.17.0
|
|
mode: WeightedVote
|
|
storeResults: true
|
|
emitEvents: true
|
|
responses:
|
|
'200':
|
|
description: Batch consensus computed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ComputeConsensusBatchResponse'
|
|
default:
|
|
$ref: '#/components/responses/Error'
|
|
|
|
/api/v1/vexlens/projections:
|
|
get:
|
|
summary: Query consensus projections
|
|
description: |
|
|
Lists stored consensus projections with filtering and pagination.
|
|
Projections are immutable snapshots of consensus computation results.
|
|
tags: [Projections]
|
|
operationId: queryProjections
|
|
parameters:
|
|
- $ref: '#/components/parameters/Tenant'
|
|
- name: vulnerabilityId
|
|
in: query
|
|
description: Filter by vulnerability ID (partial match)
|
|
schema:
|
|
type: string
|
|
- name: productKey
|
|
in: query
|
|
description: Filter by product key (partial match)
|
|
schema:
|
|
type: string
|
|
- name: status
|
|
in: query
|
|
description: Filter by consensus status
|
|
schema:
|
|
$ref: '#/components/schemas/VexStatus'
|
|
- name: outcome
|
|
in: query
|
|
description: Filter by consensus outcome
|
|
schema:
|
|
$ref: '#/components/schemas/ConsensusOutcome'
|
|
- name: minimumConfidence
|
|
in: query
|
|
description: Minimum confidence score
|
|
schema:
|
|
type: number
|
|
minimum: 0
|
|
maximum: 1
|
|
- name: computedAfter
|
|
in: query
|
|
description: Filter projections computed after this time
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
- name: computedBefore
|
|
in: query
|
|
description: Filter projections computed before this time
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
- name: statusChanged
|
|
in: query
|
|
description: Filter to only projections where status changed
|
|
schema:
|
|
type: boolean
|
|
- $ref: '#/components/parameters/Limit'
|
|
- $ref: '#/components/parameters/Offset'
|
|
- name: sortBy
|
|
in: query
|
|
description: Field to sort by
|
|
schema:
|
|
type: string
|
|
enum: [ComputedAt, StoredAt, VulnerabilityId, ProductKey, ConfidenceScore]
|
|
default: ComputedAt
|
|
- name: sortDescending
|
|
in: query
|
|
description: Sort in descending order
|
|
schema:
|
|
type: boolean
|
|
default: true
|
|
responses:
|
|
'200':
|
|
description: Paginated projection list
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/QueryProjectionsResponse'
|
|
default:
|
|
$ref: '#/components/responses/Error'
|
|
|
|
/api/v1/vexlens/projections/{projectionId}:
|
|
get:
|
|
summary: Get a projection by ID
|
|
tags: [Projections]
|
|
operationId: getProjection
|
|
parameters:
|
|
- $ref: '#/components/parameters/Tenant'
|
|
- $ref: '#/components/parameters/ProjectionId'
|
|
responses:
|
|
'200':
|
|
description: Projection details
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ProjectionDetailResponse'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
default:
|
|
$ref: '#/components/responses/Error'
|
|
|
|
/api/v1/vexlens/projections/latest:
|
|
get:
|
|
summary: Get the latest projection for a vulnerability-product pair
|
|
tags: [Projections]
|
|
operationId: getLatestProjection
|
|
parameters:
|
|
- $ref: '#/components/parameters/Tenant'
|
|
- name: vulnerabilityId
|
|
in: query
|
|
required: true
|
|
description: Vulnerability ID
|
|
schema:
|
|
type: string
|
|
- name: productKey
|
|
in: query
|
|
required: true
|
|
description: Product key (PURL or CPE)
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Latest projection
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ProjectionDetailResponse'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
default:
|
|
$ref: '#/components/responses/Error'
|
|
|
|
/api/v1/vexlens/projections/history:
|
|
get:
|
|
summary: Get projection history for a vulnerability-product pair
|
|
description: Returns the history of consensus projections in chronological order.
|
|
tags: [Projections]
|
|
operationId: getProjectionHistory
|
|
parameters:
|
|
- $ref: '#/components/parameters/Tenant'
|
|
- name: vulnerabilityId
|
|
in: query
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- name: productKey
|
|
in: query
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- name: limit
|
|
in: query
|
|
description: Maximum number of history entries
|
|
schema:
|
|
type: integer
|
|
minimum: 1
|
|
maximum: 100
|
|
default: 10
|
|
responses:
|
|
'200':
|
|
description: Projection history
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ProjectionHistoryResponse'
|
|
default:
|
|
$ref: '#/components/responses/Error'
|
|
|
|
/api/v1/vexlens/issuers:
|
|
get:
|
|
summary: List registered issuers
|
|
tags: [Issuers]
|
|
operationId: listIssuers
|
|
parameters:
|
|
- $ref: '#/components/parameters/Tenant'
|
|
- name: category
|
|
in: query
|
|
description: Filter by issuer category
|
|
schema:
|
|
$ref: '#/components/schemas/IssuerCategory'
|
|
- name: minimumTrustTier
|
|
in: query
|
|
description: Minimum trust tier
|
|
schema:
|
|
$ref: '#/components/schemas/TrustTier'
|
|
- name: status
|
|
in: query
|
|
description: Filter by issuer status
|
|
schema:
|
|
$ref: '#/components/schemas/IssuerStatus'
|
|
- name: search
|
|
in: query
|
|
description: Search term for name or ID
|
|
schema:
|
|
type: string
|
|
- $ref: '#/components/parameters/Limit'
|
|
- $ref: '#/components/parameters/Offset'
|
|
responses:
|
|
'200':
|
|
description: Issuer list
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/IssuerListResponse'
|
|
default:
|
|
$ref: '#/components/responses/Error'
|
|
post:
|
|
summary: Register a new issuer
|
|
tags: [Issuers]
|
|
operationId: registerIssuer
|
|
parameters:
|
|
- $ref: '#/components/parameters/Tenant'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/RegisterIssuerRequest'
|
|
examples:
|
|
vendor:
|
|
summary: Register a vendor issuer
|
|
value:
|
|
issuerId: npm-security
|
|
name: npm Security Team
|
|
category: Vendor
|
|
trustTier: Authoritative
|
|
initialKeys:
|
|
- fingerprint: ABCD1234EFGH5678
|
|
keyType: Pgp
|
|
algorithm: EdDSA
|
|
metadata:
|
|
description: Official npm security advisories
|
|
uri: https://www.npmjs.com/advisories
|
|
email: security@npmjs.com
|
|
responses:
|
|
'201':
|
|
description: Issuer registered
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/IssuerDetailResponse'
|
|
'409':
|
|
description: Issuer already exists
|
|
$ref: '#/components/responses/Error'
|
|
default:
|
|
$ref: '#/components/responses/Error'
|
|
|
|
/api/v1/vexlens/issuers/{issuerId}:
|
|
get:
|
|
summary: Get issuer details
|
|
tags: [Issuers]
|
|
operationId: getIssuer
|
|
parameters:
|
|
- $ref: '#/components/parameters/IssuerId'
|
|
responses:
|
|
'200':
|
|
description: Issuer details
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/IssuerDetailResponse'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
default:
|
|
$ref: '#/components/responses/Error'
|
|
delete:
|
|
summary: Revoke an issuer
|
|
tags: [Issuers]
|
|
operationId: revokeIssuer
|
|
parameters:
|
|
- $ref: '#/components/parameters/IssuerId'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/RevokeRequest'
|
|
responses:
|
|
'204':
|
|
description: Issuer revoked
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
default:
|
|
$ref: '#/components/responses/Error'
|
|
|
|
/api/v1/vexlens/issuers/{issuerId}/keys:
|
|
post:
|
|
summary: Add a key to an issuer
|
|
tags: [Issuers]
|
|
operationId: addIssuerKey
|
|
parameters:
|
|
- $ref: '#/components/parameters/IssuerId'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/RegisterKeyRequest'
|
|
responses:
|
|
'200':
|
|
description: Key added
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/IssuerDetailResponse'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
default:
|
|
$ref: '#/components/responses/Error'
|
|
|
|
/api/v1/vexlens/issuers/{issuerId}/keys/{fingerprint}:
|
|
delete:
|
|
summary: Revoke an issuer key
|
|
tags: [Issuers]
|
|
operationId: revokeIssuerKey
|
|
parameters:
|
|
- $ref: '#/components/parameters/IssuerId'
|
|
- name: fingerprint
|
|
in: path
|
|
required: true
|
|
description: Key fingerprint
|
|
schema:
|
|
type: string
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/RevokeRequest'
|
|
responses:
|
|
'204':
|
|
description: Key revoked
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
default:
|
|
$ref: '#/components/responses/Error'
|
|
|
|
/api/v1/vexlens/statistics:
|
|
get:
|
|
summary: Get consensus statistics
|
|
tags: [Statistics]
|
|
operationId: getStatistics
|
|
parameters:
|
|
- $ref: '#/components/parameters/Tenant'
|
|
responses:
|
|
'200':
|
|
description: Consensus statistics
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ConsensusStatisticsResponse'
|
|
default:
|
|
$ref: '#/components/responses/Error'
|
|
|
|
components:
|
|
parameters:
|
|
Tenant:
|
|
name: X-StellaOps-Tenant
|
|
in: header
|
|
description: Tenant identifier
|
|
schema:
|
|
type: string
|
|
ProjectionId:
|
|
name: projectionId
|
|
in: path
|
|
required: true
|
|
description: Projection ID
|
|
schema:
|
|
type: string
|
|
IssuerId:
|
|
name: issuerId
|
|
in: path
|
|
required: true
|
|
description: Issuer ID
|
|
schema:
|
|
type: string
|
|
Limit:
|
|
name: limit
|
|
in: query
|
|
description: Maximum number of items to return
|
|
schema:
|
|
type: integer
|
|
minimum: 1
|
|
maximum: 100
|
|
default: 50
|
|
Offset:
|
|
name: offset
|
|
in: query
|
|
description: Number of items to skip
|
|
schema:
|
|
type: integer
|
|
minimum: 0
|
|
default: 0
|
|
|
|
schemas:
|
|
VexStatus:
|
|
type: string
|
|
enum: [not_affected, affected, fixed, under_investigation]
|
|
description: VEX status per OpenVEX specification
|
|
|
|
VexJustification:
|
|
type: string
|
|
enum:
|
|
- component_not_present
|
|
- vulnerable_code_not_present
|
|
- vulnerable_code_not_in_execute_path
|
|
- vulnerable_code_cannot_be_controlled_by_adversary
|
|
- inline_mitigations_already_exist
|
|
description: Justification for not_affected status
|
|
|
|
ConsensusMode:
|
|
type: string
|
|
enum: [HighestWeight, WeightedVote, Lattice, AuthoritativeFirst]
|
|
description: |
|
|
- HighestWeight: Single highest-weighted statement wins
|
|
- WeightedVote: Weighted voting among all statements
|
|
- Lattice: Most conservative status wins (affected > under_investigation > not_affected > fixed)
|
|
- AuthoritativeFirst: Authoritative sources override others
|
|
|
|
ConsensusOutcome:
|
|
type: string
|
|
enum: [Unanimous, Majority, Plurality, ConflictResolved, NoData, Indeterminate]
|
|
description: Outcome of consensus computation
|
|
|
|
IssuerCategory:
|
|
type: string
|
|
enum: [Vendor, Distributor, Community, Internal, Aggregator]
|
|
description: Category of VEX document issuer
|
|
|
|
TrustTier:
|
|
type: string
|
|
enum: [Authoritative, Trusted, Untrusted, Unknown]
|
|
description: Trust level for an issuer
|
|
|
|
IssuerStatus:
|
|
type: string
|
|
enum: [Active, Suspended, Revoked]
|
|
description: Status of an issuer
|
|
|
|
ComputeConsensusRequest:
|
|
type: object
|
|
required: [vulnerabilityId, productKey]
|
|
properties:
|
|
vulnerabilityId:
|
|
type: string
|
|
description: CVE or other vulnerability identifier
|
|
productKey:
|
|
type: string
|
|
description: Product identifier (PURL or CPE)
|
|
mode:
|
|
$ref: '#/components/schemas/ConsensusMode'
|
|
minimumWeightThreshold:
|
|
type: number
|
|
minimum: 0
|
|
maximum: 1
|
|
description: Minimum trust weight threshold for statements
|
|
storeResult:
|
|
type: boolean
|
|
description: Store the result as a projection
|
|
emitEvent:
|
|
type: boolean
|
|
description: Emit an event for the consensus result
|
|
|
|
ComputeConsensusResponse:
|
|
type: object
|
|
required:
|
|
- vulnerabilityId
|
|
- productKey
|
|
- status
|
|
- confidenceScore
|
|
- outcome
|
|
- rationale
|
|
- contributions
|
|
- computedAt
|
|
properties:
|
|
vulnerabilityId:
|
|
type: string
|
|
productKey:
|
|
type: string
|
|
status:
|
|
$ref: '#/components/schemas/VexStatus'
|
|
justification:
|
|
$ref: '#/components/schemas/VexJustification'
|
|
confidenceScore:
|
|
type: number
|
|
minimum: 0
|
|
maximum: 1
|
|
outcome:
|
|
$ref: '#/components/schemas/ConsensusOutcome'
|
|
rationale:
|
|
$ref: '#/components/schemas/ConsensusRationale'
|
|
contributions:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/Contribution'
|
|
conflicts:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/Conflict'
|
|
projectionId:
|
|
type: string
|
|
description: ID of stored projection (if storeResult was true)
|
|
computedAt:
|
|
type: string
|
|
format: date-time
|
|
|
|
ConsensusRationale:
|
|
type: object
|
|
required: [summary, factors, statusWeights]
|
|
properties:
|
|
summary:
|
|
type: string
|
|
description: Human-readable summary of the decision
|
|
factors:
|
|
type: array
|
|
items:
|
|
type: string
|
|
description: List of factors that influenced the decision
|
|
statusWeights:
|
|
type: object
|
|
additionalProperties:
|
|
type: number
|
|
description: Total weight per status
|
|
|
|
Contribution:
|
|
type: object
|
|
required: [statementId, status, weight, contribution, isWinner]
|
|
properties:
|
|
statementId:
|
|
type: string
|
|
issuerId:
|
|
type: string
|
|
status:
|
|
$ref: '#/components/schemas/VexStatus'
|
|
justification:
|
|
$ref: '#/components/schemas/VexJustification'
|
|
weight:
|
|
type: number
|
|
contribution:
|
|
type: number
|
|
description: Proportional contribution to consensus
|
|
isWinner:
|
|
type: boolean
|
|
description: Whether this statement won the consensus
|
|
|
|
Conflict:
|
|
type: object
|
|
required: [statement1Id, statement2Id, status1, status2, severity, resolution]
|
|
properties:
|
|
statement1Id:
|
|
type: string
|
|
statement2Id:
|
|
type: string
|
|
status1:
|
|
$ref: '#/components/schemas/VexStatus'
|
|
status2:
|
|
$ref: '#/components/schemas/VexStatus'
|
|
severity:
|
|
type: string
|
|
enum: [Low, Medium, High, Critical]
|
|
resolution:
|
|
type: string
|
|
description: How the conflict was resolved
|
|
|
|
ComputeConsensusBatchRequest:
|
|
type: object
|
|
required: [targets]
|
|
properties:
|
|
targets:
|
|
type: array
|
|
items:
|
|
type: object
|
|
required: [vulnerabilityId, productKey]
|
|
properties:
|
|
vulnerabilityId:
|
|
type: string
|
|
productKey:
|
|
type: string
|
|
minItems: 1
|
|
maxItems: 100
|
|
mode:
|
|
$ref: '#/components/schemas/ConsensusMode'
|
|
storeResults:
|
|
type: boolean
|
|
emitEvents:
|
|
type: boolean
|
|
|
|
ComputeConsensusBatchResponse:
|
|
type: object
|
|
required: [results, totalCount, successCount, failureCount, completedAt]
|
|
properties:
|
|
results:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/ComputeConsensusResponse'
|
|
totalCount:
|
|
type: integer
|
|
successCount:
|
|
type: integer
|
|
failureCount:
|
|
type: integer
|
|
completedAt:
|
|
type: string
|
|
format: date-time
|
|
|
|
QueryProjectionsResponse:
|
|
type: object
|
|
required: [projections, totalCount, offset, limit]
|
|
properties:
|
|
projections:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/ProjectionSummary'
|
|
totalCount:
|
|
type: integer
|
|
offset:
|
|
type: integer
|
|
limit:
|
|
type: integer
|
|
|
|
ProjectionSummary:
|
|
type: object
|
|
required:
|
|
- projectionId
|
|
- vulnerabilityId
|
|
- productKey
|
|
- status
|
|
- confidenceScore
|
|
- outcome
|
|
- statementCount
|
|
- conflictCount
|
|
- computedAt
|
|
- statusChanged
|
|
properties:
|
|
projectionId:
|
|
type: string
|
|
vulnerabilityId:
|
|
type: string
|
|
productKey:
|
|
type: string
|
|
status:
|
|
$ref: '#/components/schemas/VexStatus'
|
|
justification:
|
|
$ref: '#/components/schemas/VexJustification'
|
|
confidenceScore:
|
|
type: number
|
|
outcome:
|
|
type: string
|
|
statementCount:
|
|
type: integer
|
|
conflictCount:
|
|
type: integer
|
|
computedAt:
|
|
type: string
|
|
format: date-time
|
|
statusChanged:
|
|
type: boolean
|
|
|
|
ProjectionDetailResponse:
|
|
allOf:
|
|
- $ref: '#/components/schemas/ProjectionSummary'
|
|
- type: object
|
|
properties:
|
|
tenantId:
|
|
type: string
|
|
rationaleSummary:
|
|
type: string
|
|
storedAt:
|
|
type: string
|
|
format: date-time
|
|
previousProjectionId:
|
|
type: string
|
|
|
|
ProjectionHistoryResponse:
|
|
type: object
|
|
required: [vulnerabilityId, productKey, history, totalCount]
|
|
properties:
|
|
vulnerabilityId:
|
|
type: string
|
|
productKey:
|
|
type: string
|
|
history:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/ProjectionSummary'
|
|
totalCount:
|
|
type: integer
|
|
|
|
IssuerListResponse:
|
|
type: object
|
|
required: [issuers, totalCount]
|
|
properties:
|
|
issuers:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/IssuerSummary'
|
|
totalCount:
|
|
type: integer
|
|
|
|
IssuerSummary:
|
|
type: object
|
|
required: [issuerId, name, category, trustTier, status, keyCount, registeredAt]
|
|
properties:
|
|
issuerId:
|
|
type: string
|
|
name:
|
|
type: string
|
|
category:
|
|
$ref: '#/components/schemas/IssuerCategory'
|
|
trustTier:
|
|
$ref: '#/components/schemas/TrustTier'
|
|
status:
|
|
$ref: '#/components/schemas/IssuerStatus'
|
|
keyCount:
|
|
type: integer
|
|
registeredAt:
|
|
type: string
|
|
format: date-time
|
|
|
|
IssuerDetailResponse:
|
|
allOf:
|
|
- $ref: '#/components/schemas/IssuerSummary'
|
|
- type: object
|
|
properties:
|
|
keyFingerprints:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/KeyFingerprint'
|
|
metadata:
|
|
$ref: '#/components/schemas/IssuerMetadata'
|
|
lastUpdatedAt:
|
|
type: string
|
|
format: date-time
|
|
revokedAt:
|
|
type: string
|
|
format: date-time
|
|
revocationReason:
|
|
type: string
|
|
|
|
KeyFingerprint:
|
|
type: object
|
|
required: [fingerprint, keyType, status, registeredAt]
|
|
properties:
|
|
fingerprint:
|
|
type: string
|
|
keyType:
|
|
type: string
|
|
enum: [Pgp, X509, Jwk, Ssh, Sigstore]
|
|
algorithm:
|
|
type: string
|
|
status:
|
|
type: string
|
|
enum: [Active, Expired, Revoked]
|
|
registeredAt:
|
|
type: string
|
|
format: date-time
|
|
expiresAt:
|
|
type: string
|
|
format: date-time
|
|
|
|
IssuerMetadata:
|
|
type: object
|
|
properties:
|
|
description:
|
|
type: string
|
|
uri:
|
|
type: string
|
|
format: uri
|
|
email:
|
|
type: string
|
|
format: email
|
|
tags:
|
|
type: array
|
|
items:
|
|
type: string
|
|
|
|
RegisterIssuerRequest:
|
|
type: object
|
|
required: [issuerId, name, category, trustTier]
|
|
properties:
|
|
issuerId:
|
|
type: string
|
|
name:
|
|
type: string
|
|
category:
|
|
$ref: '#/components/schemas/IssuerCategory'
|
|
trustTier:
|
|
$ref: '#/components/schemas/TrustTier'
|
|
initialKeys:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/RegisterKeyRequest'
|
|
metadata:
|
|
$ref: '#/components/schemas/IssuerMetadata'
|
|
|
|
RegisterKeyRequest:
|
|
type: object
|
|
required: [fingerprint, keyType]
|
|
properties:
|
|
fingerprint:
|
|
type: string
|
|
keyType:
|
|
type: string
|
|
enum: [Pgp, X509, Jwk, Ssh, Sigstore]
|
|
algorithm:
|
|
type: string
|
|
expiresAt:
|
|
type: string
|
|
format: date-time
|
|
|
|
RevokeRequest:
|
|
type: object
|
|
required: [reason]
|
|
properties:
|
|
reason:
|
|
type: string
|
|
minLength: 1
|
|
maxLength: 500
|
|
|
|
ConsensusStatisticsResponse:
|
|
type: object
|
|
required:
|
|
- totalProjections
|
|
- byStatus
|
|
- byOutcome
|
|
- averageConfidence
|
|
- projectionsWithConflicts
|
|
- statusChangesLast24h
|
|
- computedAt
|
|
properties:
|
|
totalProjections:
|
|
type: integer
|
|
byStatus:
|
|
type: object
|
|
additionalProperties:
|
|
type: integer
|
|
byOutcome:
|
|
type: object
|
|
additionalProperties:
|
|
type: integer
|
|
averageConfidence:
|
|
type: number
|
|
projectionsWithConflicts:
|
|
type: integer
|
|
statusChangesLast24h:
|
|
type: integer
|
|
computedAt:
|
|
type: string
|
|
format: date-time
|
|
|
|
Error:
|
|
type: object
|
|
required: [code, message]
|
|
properties:
|
|
code:
|
|
type: string
|
|
message:
|
|
type: string
|
|
details:
|
|
type: object
|
|
traceId:
|
|
type: string
|
|
|
|
responses:
|
|
Error:
|
|
description: Error response
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
BadRequest:
|
|
description: Invalid request
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
examples:
|
|
validation:
|
|
value:
|
|
code: VALIDATION_ERROR
|
|
message: Invalid request parameters
|
|
details:
|
|
field: vulnerabilityId
|
|
error: Required field missing
|
|
NotFound:
|
|
description: Resource not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
examples:
|
|
notFound:
|
|
value:
|
|
code: NOT_FOUND
|
|
message: Requested resource not found
|
|
|
|
securitySchemes:
|
|
oauth2:
|
|
type: oauth2
|
|
flows:
|
|
authorizationCode:
|
|
authorizationUrl: https://auth.stellaops.example.com/oauth/authorize
|
|
tokenUrl: https://auth.stellaops.example.com/oauth/token
|
|
scopes:
|
|
vexlens.viewer: Read access to consensus projections
|
|
vexlens.operator: Compute consensus and manage projections
|
|
vexlens.admin: Full access including issuer management
|