Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
feat(sbomservice): Add placeholder for SHA256SUMS in LNM v1 fixtures docs(devportal): Create README for SDK archives in public directory build(devportal): Implement offline bundle build script test(devportal): Add link checker script for validating links in documentation test(devportal): Create performance check script for dist folder size test(devportal): Implement accessibility check script using Playwright and Axe docs(devportal): Add SDK quickstart guide with examples for Node.js, Python, and cURL feat(excititor): Implement MongoDB storage for airgap import records test(findings): Add unit tests for export filters hash determinism feat(findings): Define attestation contracts for ledger web service feat(graph): Add MongoDB options and service collection extensions for graph indexing test(graph): Implement integration tests for MongoDB provider and service collection extensions feat(zastava): Define configuration options for Zastava surface secrets build(tests): Create script to run Concelier linkset tests with TRX output
672 lines
20 KiB
YAML
672 lines
20 KiB
YAML
openapi: 3.0.3
|
|
info:
|
|
title: StellaOps Findings Ledger API
|
|
version: 1.0.0-beta1
|
|
description: >-
|
|
Canonical, aggregation-only surface for append-only findings events, projections, and
|
|
Merkle anchoring metadata. Aligns with schema in docs/modules/findings-ledger/schema.md.
|
|
servers:
|
|
- url: https://{env}.ledger.api.stellaops.local
|
|
description: Default environment-scoped host
|
|
variables:
|
|
env:
|
|
default: prod
|
|
enum: [dev, staging, prod, airgap]
|
|
- url: https://ledger.{region}.offline.bundle
|
|
description: Offline bundle host for air-gapped deployments
|
|
variables:
|
|
region:
|
|
default: local
|
|
enum: [local]
|
|
security:
|
|
- bearerAuth: []
|
|
- mTLS: []
|
|
paths:
|
|
/v1/ledger/events:
|
|
get:
|
|
summary: List ledger events
|
|
operationId: listLedgerEvents
|
|
tags: [ledger]
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- name: chainId
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
- name: sinceSequence
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
minimum: 0
|
|
- name: limit
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
default: 200
|
|
maximum: 1000
|
|
responses:
|
|
'200':
|
|
description: Paged ledger events in deterministic order (chainId, sequence_No asc)
|
|
headers:
|
|
Req-Cursor:
|
|
schema:
|
|
type: string
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/LedgerEventPage'
|
|
post:
|
|
summary: Append deterministic ledger event
|
|
operationId: appendLedgerEvent
|
|
tags: [ledger]
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/LedgerEventAppendRequest'
|
|
responses:
|
|
'201':
|
|
description: Event persisted
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/LedgerEvent'
|
|
'409':
|
|
description: Hash/sequence conflict (non-deterministic input)
|
|
/v1/ledger/projections/findings:
|
|
get:
|
|
summary: Get latest projection for findings
|
|
operationId: listFindingProjections
|
|
tags: [projections]
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- name: findingId
|
|
in: query
|
|
schema:
|
|
type: string
|
|
- name: policyVersion
|
|
in: query
|
|
schema:
|
|
type: string
|
|
- name: status
|
|
in: query
|
|
schema:
|
|
type: string
|
|
- name: limit
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
default: 200
|
|
maximum: 1000
|
|
responses:
|
|
'200':
|
|
description: Projection rows with cycleHash for replay validation
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/FindingProjectionPage'
|
|
/v1/ledger/export/findings:
|
|
get:
|
|
summary: Export findings in deterministic order
|
|
operationId: exportFindings
|
|
tags: [export]
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- $ref: '#/components/parameters/Shape'
|
|
- $ref: '#/components/parameters/SinceSequence'
|
|
- $ref: '#/components/parameters/UntilSequence'
|
|
- $ref: '#/components/parameters/SinceObservedAt'
|
|
- $ref: '#/components/parameters/UntilObservedAt'
|
|
- name: finding_status
|
|
in: query
|
|
schema: { type: string }
|
|
- name: severity
|
|
in: query
|
|
schema: { type: number }
|
|
- name: risk_profile_version
|
|
in: query
|
|
schema: { type: string }
|
|
- $ref: '#/components/parameters/PageSize'
|
|
- $ref: '#/components/parameters/PageToken'
|
|
responses:
|
|
'200':
|
|
description: Paged findings export
|
|
headers:
|
|
X-Stella-Next-Page-Token:
|
|
schema: { type: string }
|
|
X-Stella-Result-Count:
|
|
schema: { type: integer }
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/FindingExportPage'
|
|
application/x-ndjson:
|
|
schema:
|
|
type: string
|
|
description: NDJSON stream of FindingExportItem
|
|
/v1/ledger/export/vex:
|
|
get:
|
|
summary: Export VEX statements
|
|
operationId: exportVex
|
|
tags: [export]
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- $ref: '#/components/parameters/Shape'
|
|
- $ref: '#/components/parameters/SinceSequence'
|
|
- $ref: '#/components/parameters/UntilSequence'
|
|
- $ref: '#/components/parameters/SinceObservedAt'
|
|
- $ref: '#/components/parameters/UntilObservedAt'
|
|
- name: product_id
|
|
in: query
|
|
schema: { type: string }
|
|
- name: advisory_id
|
|
in: query
|
|
schema: { type: string }
|
|
- name: status
|
|
in: query
|
|
schema: { type: string }
|
|
- name: statement_type
|
|
in: query
|
|
schema: { type: string }
|
|
- $ref: '#/components/parameters/PageSize'
|
|
- $ref: '#/components/parameters/PageToken'
|
|
responses:
|
|
'200':
|
|
description: Paged VEX export
|
|
headers:
|
|
X-Stella-Next-Page-Token:
|
|
schema: { type: string }
|
|
X-Stella-Result-Count:
|
|
schema: { type: integer }
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/VexExportPage'
|
|
application/x-ndjson:
|
|
schema:
|
|
type: string
|
|
description: NDJSON stream of VexExportItem
|
|
/v1/ledger/export/advisories:
|
|
get:
|
|
summary: Export advisories
|
|
operationId: exportAdvisories
|
|
tags: [export]
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- $ref: '#/components/parameters/Shape'
|
|
- $ref: '#/components/parameters/SinceSequence'
|
|
- $ref: '#/components/parameters/UntilSequence'
|
|
- $ref: '#/components/parameters/SinceObservedAt'
|
|
- $ref: '#/components/parameters/UntilObservedAt'
|
|
- name: severity
|
|
in: query
|
|
schema: { type: string }
|
|
- name: source
|
|
in: query
|
|
schema: { type: string }
|
|
- name: cwe_id
|
|
in: query
|
|
schema: { type: string }
|
|
- name: kev
|
|
in: query
|
|
schema: { type: boolean }
|
|
- name: cvss_version
|
|
in: query
|
|
schema: { type: string }
|
|
- name: cvss_score_min
|
|
in: query
|
|
schema: { type: number }
|
|
- name: cvss_score_max
|
|
in: query
|
|
schema: { type: number }
|
|
- $ref: '#/components/parameters/PageSize'
|
|
- $ref: '#/components/parameters/PageToken'
|
|
responses:
|
|
'200':
|
|
description: Paged advisory export
|
|
headers:
|
|
X-Stella-Next-Page-Token:
|
|
schema: { type: string }
|
|
X-Stella-Result-Count:
|
|
schema: { type: integer }
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/AdvisoryExportPage'
|
|
application/x-ndjson:
|
|
schema:
|
|
type: string
|
|
description: NDJSON stream of AdvisoryExportItem
|
|
/v1/ledger/export/sboms:
|
|
get:
|
|
summary: Export SBOMs
|
|
operationId: exportSboms
|
|
tags: [export]
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- $ref: '#/components/parameters/Shape'
|
|
- $ref: '#/components/parameters/SinceSequence'
|
|
- $ref: '#/components/parameters/UntilSequence'
|
|
- $ref: '#/components/parameters/SinceObservedAt'
|
|
- $ref: '#/components/parameters/UntilObservedAt'
|
|
- name: subject_digest
|
|
in: query
|
|
schema: { type: string }
|
|
- name: sbom_format
|
|
in: query
|
|
schema: { type: string }
|
|
- name: component_purl
|
|
in: query
|
|
schema: { type: string }
|
|
- name: contains_native
|
|
in: query
|
|
schema: { type: boolean }
|
|
- name: slsa_build_type
|
|
in: query
|
|
schema: { type: string }
|
|
- $ref: '#/components/parameters/PageSize'
|
|
- $ref: '#/components/parameters/PageToken'
|
|
responses:
|
|
'200':
|
|
description: Paged SBOM export
|
|
headers:
|
|
X-Stella-Next-Page-Token:
|
|
schema: { type: string }
|
|
X-Stella-Result-Count:
|
|
schema: { type: integer }
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/SbomExportPage'
|
|
application/x-ndjson:
|
|
schema:
|
|
type: string
|
|
description: NDJSON stream of SbomExportItem
|
|
/v1/ledger/attestations:
|
|
get:
|
|
summary: List attestation verifications
|
|
operationId: listLedgerAttestations
|
|
tags: [attestation]
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- name: artifactId
|
|
in: query
|
|
schema: { type: string }
|
|
- name: findingId
|
|
in: query
|
|
schema: { type: string }
|
|
- name: attestationId
|
|
in: query
|
|
schema: { type: string }
|
|
- name: status
|
|
in: query
|
|
schema:
|
|
type: string
|
|
enum: [verified, failed, unknown]
|
|
- name: sinceRecordedAt
|
|
in: query
|
|
schema: { type: string, format: date-time }
|
|
- name: untilRecordedAt
|
|
in: query
|
|
schema: { type: string, format: date-time }
|
|
- $ref: '#/components/parameters/AttestationLimit'
|
|
- $ref: '#/components/parameters/PageToken'
|
|
responses:
|
|
'200':
|
|
description: Paged attestation verifications
|
|
headers:
|
|
X-Stella-Next-Page-Token:
|
|
schema: { type: string }
|
|
X-Stella-Result-Count:
|
|
schema: { type: integer }
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/AttestationExportPage'
|
|
application/x-ndjson:
|
|
schema:
|
|
type: string
|
|
description: NDJSON stream of AttestationExportItem
|
|
/.well-known/openapi:
|
|
get:
|
|
summary: Serve Findings Ledger OpenAPI document
|
|
operationId: getOpenApi
|
|
tags: [metadata]
|
|
responses:
|
|
'200':
|
|
description: OpenAPI YAML document
|
|
content:
|
|
application/yaml:
|
|
schema:
|
|
type: string
|
|
components:
|
|
securitySchemes:
|
|
bearerAuth:
|
|
type: http
|
|
scheme: bearer
|
|
bearerFormat: JWT
|
|
mTLS:
|
|
type: mutualTLS
|
|
parameters:
|
|
TenantId:
|
|
name: X-Stella-Tenant
|
|
in: header
|
|
required: true
|
|
schema:
|
|
type: string
|
|
Shape:
|
|
name: shape
|
|
in: query
|
|
required: true
|
|
schema:
|
|
type: string
|
|
enum: [canonical, compact]
|
|
SinceSequence:
|
|
name: since_sequence
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
minimum: 0
|
|
UntilSequence:
|
|
name: until_sequence
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
minimum: 0
|
|
SinceObservedAt:
|
|
name: since_observed_at
|
|
in: query
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
UntilObservedAt:
|
|
name: until_observed_at
|
|
in: query
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
PageSize:
|
|
name: page_size
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
default: 500
|
|
maximum: 5000
|
|
PageToken:
|
|
name: page_token
|
|
in: query
|
|
schema:
|
|
type: string
|
|
AttestationLimit:
|
|
name: limit
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
default: 200
|
|
maximum: 1000
|
|
schemas:
|
|
LedgerEvent:
|
|
type: object
|
|
required: [event]
|
|
properties:
|
|
event:
|
|
type: object
|
|
required: [id, type, tenant, chainId, sequence, policyVersion, occurredAt, recordedAt, payload]
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
type:
|
|
type: string
|
|
description: ledger_event_type (see schema.md §2.2)
|
|
tenant:
|
|
type: string
|
|
chainId:
|
|
type: string
|
|
format: uuid
|
|
sequence:
|
|
type: integer
|
|
policyVersion:
|
|
type: string
|
|
finding:
|
|
type: object
|
|
properties:
|
|
id: { type: string }
|
|
artifactId: { type: string }
|
|
vulnId: { type: string }
|
|
actor:
|
|
type: object
|
|
properties:
|
|
id: { type: string }
|
|
type: { type: string }
|
|
occurredAt:
|
|
type: string
|
|
format: date-time
|
|
recordedAt:
|
|
type: string
|
|
format: date-time
|
|
payload:
|
|
type: object
|
|
additionalProperties: true
|
|
evidenceBundleRef:
|
|
type: string
|
|
eventHash:
|
|
type: string
|
|
previousHash:
|
|
type: string
|
|
merkleLeafHash:
|
|
type: string
|
|
LedgerEventPage:
|
|
type: object
|
|
required: [items]
|
|
properties:
|
|
items:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/LedgerEvent'
|
|
nextCursor:
|
|
type: string
|
|
LedgerEventAppendRequest:
|
|
type: object
|
|
required: [id, type, tenant, chainId, sequence, policyVersion, occurredAt, payload]
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
type:
|
|
type: string
|
|
tenant:
|
|
type: string
|
|
chainId:
|
|
type: string
|
|
format: uuid
|
|
sequence:
|
|
type: integer
|
|
policyVersion:
|
|
type: string
|
|
finding:
|
|
type: object
|
|
properties:
|
|
id: { type: string }
|
|
artifactId: { type: string }
|
|
vulnId: { type: string }
|
|
actor:
|
|
type: object
|
|
properties:
|
|
id: { type: string }
|
|
type: { type: string }
|
|
occurredAt:
|
|
type: string
|
|
format: date-time
|
|
payload:
|
|
type: object
|
|
additionalProperties: true
|
|
evidenceBundleRef:
|
|
type: string
|
|
previousHash:
|
|
type: string
|
|
description: Optional; validated if supplied
|
|
merkleLeafHash:
|
|
type: string
|
|
description: Optional; server recomputes to validate determinism
|
|
FindingProjection:
|
|
type: object
|
|
required: [tenantId, findingId, policyVersion, status, cycleHash]
|
|
properties:
|
|
tenantId: { type: string }
|
|
findingId: { type: string }
|
|
policyVersion: { type: string }
|
|
status: { type: string }
|
|
severity:
|
|
type: number
|
|
format: double
|
|
riskScore:
|
|
type: number
|
|
format: double
|
|
riskSeverity:
|
|
type: string
|
|
riskProfileVersion:
|
|
type: string
|
|
riskExplanationId:
|
|
type: string
|
|
format: uuid
|
|
labels:
|
|
type: object
|
|
additionalProperties: true
|
|
currentEventId:
|
|
type: string
|
|
format: uuid
|
|
cycleHash:
|
|
type: string
|
|
updatedAt:
|
|
type: string
|
|
format: date-time
|
|
FindingProjectionPage:
|
|
type: object
|
|
required: [items]
|
|
properties:
|
|
items:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/FindingProjection'
|
|
nextCursor:
|
|
type: string
|
|
ExportProvenance:
|
|
type: object
|
|
properties:
|
|
policyVersion: { type: string }
|
|
cycleHash: { type: string }
|
|
ledgerEventHash: { type: string }
|
|
FindingExportItem:
|
|
type: object
|
|
properties:
|
|
eventSequence: { type: integer }
|
|
observedAt: { type: string, format: date-time }
|
|
findingId: { type: string }
|
|
policyVersion: { type: string }
|
|
status: { type: string }
|
|
severity: { type: number, format: double }
|
|
cycleHash: { type: string }
|
|
evidenceBundleRef: { type: string }
|
|
provenance:
|
|
$ref: '#/components/schemas/ExportProvenance'
|
|
labels:
|
|
type: object
|
|
additionalProperties: true
|
|
VexExportItem:
|
|
type: object
|
|
properties:
|
|
eventSequence: { type: integer }
|
|
observedAt: { type: string, format: date-time }
|
|
vexStatementId: { type: string }
|
|
productId: { type: string }
|
|
status: { type: string }
|
|
statementType: { type: string }
|
|
knownExploited: { type: boolean }
|
|
cycleHash: { type: string }
|
|
provenance:
|
|
$ref: '#/components/schemas/ExportProvenance'
|
|
AdvisoryExportItem:
|
|
type: object
|
|
properties:
|
|
eventSequence: { type: integer }
|
|
published: { type: string, format: date-time }
|
|
advisoryId: { type: string }
|
|
source: { type: string }
|
|
title: { type: string }
|
|
severity: { type: string }
|
|
cvssScore: { type: number, format: double }
|
|
cvssVector: { type: string }
|
|
kev: { type: boolean }
|
|
cycleHash: { type: string }
|
|
provenance:
|
|
$ref: '#/components/schemas/ExportProvenance'
|
|
SbomExportItem:
|
|
type: object
|
|
properties:
|
|
eventSequence: { type: integer }
|
|
createdAt: { type: string, format: date-time }
|
|
sbomId: { type: string }
|
|
subjectDigest: { type: string }
|
|
sbomFormat: { type: string }
|
|
componentsCount: { type: integer }
|
|
hasVulnerabilities: { type: boolean }
|
|
cycleHash: { type: string }
|
|
provenance:
|
|
$ref: '#/components/schemas/ExportProvenance'
|
|
FindingExportPage:
|
|
type: object
|
|
properties:
|
|
items:
|
|
type: array
|
|
items: { $ref: '#/components/schemas/FindingExportItem' }
|
|
nextPageToken: { type: string }
|
|
VexExportPage:
|
|
type: object
|
|
properties:
|
|
items:
|
|
type: array
|
|
items: { $ref: '#/components/schemas/VexExportItem' }
|
|
nextPageToken: { type: string }
|
|
AdvisoryExportPage:
|
|
type: object
|
|
properties:
|
|
items:
|
|
type: array
|
|
items: { $ref: '#/components/schemas/AdvisoryExportItem' }
|
|
nextPageToken: { type: string }
|
|
SbomExportPage:
|
|
type: object
|
|
properties:
|
|
items:
|
|
type: array
|
|
items: { $ref: '#/components/schemas/SbomExportItem' }
|
|
nextPageToken: { type: string }
|
|
AttestationExportItem:
|
|
type: object
|
|
properties:
|
|
attestationId: { type: string }
|
|
artifactId: { type: string }
|
|
findingId: { type: string }
|
|
verificationStatus: { type: string }
|
|
verificationTime: { type: string, format: date-time }
|
|
dsseDigest: { type: string }
|
|
rekorEntryId: { type: string }
|
|
evidenceBundleRef: { type: string }
|
|
ledgerEventId: { type: string, format: uuid }
|
|
recordedAt: { type: string, format: date-time }
|
|
merkleLeafHash: { type: string }
|
|
rootHash: { type: string }
|
|
AttestationExportPage:
|
|
type: object
|
|
properties:
|
|
items:
|
|
type: array
|
|
items: { $ref: '#/components/schemas/AttestationExportItem' }
|
|
nextPageToken: { type: string }
|