Add unit tests for PackRunAttestation and SealedInstallEnforcer
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
release-manifest-verify / verify (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
release-manifest-verify / verify (push) Has been cancelled
- Implement comprehensive tests for PackRunAttestationService, covering attestation generation, verification, and event emission. - Add tests for SealedInstallEnforcer to validate sealed install requirements and enforcement logic. - Introduce a MonacoLoaderService stub for testing purposes to prevent Monaco workers/styles from loading during Karma runs.
This commit is contained in:
526
docs/schemas/attestation-pointer.schema.json
Normal file
526
docs/schemas/attestation-pointer.schema.json
Normal file
@@ -0,0 +1,526 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://stella-ops.org/schemas/attestation-pointer.schema.json",
|
||||
"title": "StellaOps Attestation Pointer Schema",
|
||||
"description": "Schema for attestation pointers linking findings to verification reports and attestation envelopes. Unblocks LEDGER-ATTEST-73-001 and 73-002.",
|
||||
"type": "object",
|
||||
"definitions": {
|
||||
"AttestationPointer": {
|
||||
"type": "object",
|
||||
"description": "Pointer from a finding to its related attestation artifacts",
|
||||
"required": ["pointer_id", "finding_id", "attestation_type", "created_at"],
|
||||
"properties": {
|
||||
"pointer_id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Unique identifier for this pointer"
|
||||
},
|
||||
"finding_id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Finding this pointer references"
|
||||
},
|
||||
"attestation_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"verification_report",
|
||||
"dsse_envelope",
|
||||
"slsa_provenance",
|
||||
"vex_attestation",
|
||||
"sbom_attestation",
|
||||
"scan_attestation",
|
||||
"policy_attestation",
|
||||
"approval_attestation"
|
||||
],
|
||||
"description": "Type of attestation being pointed to"
|
||||
},
|
||||
"attestation_ref": {
|
||||
"$ref": "#/definitions/AttestationRef"
|
||||
},
|
||||
"relationship": {
|
||||
"type": "string",
|
||||
"enum": ["verified_by", "attested_by", "signed_by", "approved_by", "derived_from"],
|
||||
"description": "Semantic relationship to the attestation"
|
||||
},
|
||||
"verification_result": {
|
||||
"$ref": "#/definitions/VerificationResult"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"created_by": {
|
||||
"type": "string",
|
||||
"description": "Service or user that created the pointer"
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"AttestationRef": {
|
||||
"type": "object",
|
||||
"description": "Reference to an attestation artifact",
|
||||
"required": ["digest"],
|
||||
"properties": {
|
||||
"attestation_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$",
|
||||
"description": "Content-addressable digest of the attestation"
|
||||
},
|
||||
"storage_uri": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"description": "URI to retrieve the attestation"
|
||||
},
|
||||
"payload_type": {
|
||||
"type": "string",
|
||||
"description": "DSSE payload type (e.g., application/vnd.in-toto+json)"
|
||||
},
|
||||
"predicate_type": {
|
||||
"type": "string",
|
||||
"description": "in-toto predicate type URI"
|
||||
},
|
||||
"subject_digests": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$"
|
||||
},
|
||||
"description": "Digests of subjects this attestation covers"
|
||||
},
|
||||
"signer_info": {
|
||||
"$ref": "#/definitions/SignerInfo"
|
||||
},
|
||||
"rekor_entry": {
|
||||
"$ref": "#/definitions/RekorEntryRef"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SignerInfo": {
|
||||
"type": "object",
|
||||
"description": "Information about the attestation signer",
|
||||
"properties": {
|
||||
"key_id": {
|
||||
"type": "string",
|
||||
"description": "Key identifier"
|
||||
},
|
||||
"issuer": {
|
||||
"type": "string",
|
||||
"description": "Certificate issuer (for Fulcio keyless signing)"
|
||||
},
|
||||
"subject": {
|
||||
"type": "string",
|
||||
"description": "Certificate subject (email, OIDC identity)"
|
||||
},
|
||||
"certificate_chain": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "PEM-encoded certificate chain"
|
||||
},
|
||||
"signed_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RekorEntryRef": {
|
||||
"type": "object",
|
||||
"description": "Reference to Rekor transparency log entry",
|
||||
"properties": {
|
||||
"log_index": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"log_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"uuid": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{64}$"
|
||||
},
|
||||
"integrated_time": {
|
||||
"type": "integer",
|
||||
"description": "Unix timestamp of log entry"
|
||||
}
|
||||
}
|
||||
},
|
||||
"VerificationResult": {
|
||||
"type": "object",
|
||||
"description": "Result of attestation verification",
|
||||
"required": ["verified", "verified_at"],
|
||||
"properties": {
|
||||
"verified": {
|
||||
"type": "boolean",
|
||||
"description": "Whether verification passed"
|
||||
},
|
||||
"verified_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"verifier": {
|
||||
"type": "string",
|
||||
"description": "Service that performed verification"
|
||||
},
|
||||
"verifier_version": {
|
||||
"type": "string"
|
||||
},
|
||||
"policy_ref": {
|
||||
"type": "string",
|
||||
"description": "Reference to verification policy used"
|
||||
},
|
||||
"checks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/VerificationCheck"
|
||||
}
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"VerificationCheck": {
|
||||
"type": "object",
|
||||
"description": "Individual verification check result",
|
||||
"required": ["check_type", "passed"],
|
||||
"properties": {
|
||||
"check_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"signature_valid",
|
||||
"certificate_valid",
|
||||
"certificate_not_expired",
|
||||
"certificate_not_revoked",
|
||||
"rekor_entry_valid",
|
||||
"timestamp_valid",
|
||||
"policy_met",
|
||||
"identity_verified",
|
||||
"issuer_trusted"
|
||||
]
|
||||
},
|
||||
"passed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"details": {
|
||||
"type": "string"
|
||||
},
|
||||
"evidence": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"VerificationReport": {
|
||||
"type": "object",
|
||||
"description": "Full verification report for a finding",
|
||||
"required": ["report_id", "finding_id", "created_at", "overall_result"],
|
||||
"properties": {
|
||||
"report_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"finding_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"overall_result": {
|
||||
"type": "string",
|
||||
"enum": ["passed", "failed", "partial", "not_applicable"]
|
||||
},
|
||||
"attestation_results": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AttestationVerificationResult"
|
||||
}
|
||||
},
|
||||
"policy_evaluations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/PolicyEvaluationResult"
|
||||
}
|
||||
},
|
||||
"summary": {
|
||||
"type": "string"
|
||||
},
|
||||
"recommendations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"AttestationVerificationResult": {
|
||||
"type": "object",
|
||||
"description": "Verification result for a specific attestation",
|
||||
"required": ["attestation_ref", "verification_result"],
|
||||
"properties": {
|
||||
"attestation_ref": {
|
||||
"$ref": "#/definitions/AttestationRef"
|
||||
},
|
||||
"verification_result": {
|
||||
"$ref": "#/definitions/VerificationResult"
|
||||
},
|
||||
"relevance": {
|
||||
"type": "string",
|
||||
"enum": ["primary", "supporting", "contextual"],
|
||||
"description": "How relevant this attestation is to the finding"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PolicyEvaluationResult": {
|
||||
"type": "object",
|
||||
"description": "Result of policy evaluation against attestations",
|
||||
"required": ["policy_id", "result"],
|
||||
"properties": {
|
||||
"policy_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"policy_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"policy_version": {
|
||||
"type": "string"
|
||||
},
|
||||
"result": {
|
||||
"type": "string",
|
||||
"enum": ["passed", "failed", "skipped", "error"]
|
||||
},
|
||||
"reason": {
|
||||
"type": "string"
|
||||
},
|
||||
"attestations_evaluated": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Attestation IDs evaluated by this policy"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DsseEnvelope": {
|
||||
"type": "object",
|
||||
"description": "DSSE envelope containing attestation",
|
||||
"required": ["payloadType", "payload", "signatures"],
|
||||
"properties": {
|
||||
"payloadType": {
|
||||
"type": "string",
|
||||
"description": "MIME type of payload"
|
||||
},
|
||||
"payload": {
|
||||
"type": "string",
|
||||
"contentEncoding": "base64",
|
||||
"description": "Base64-encoded payload"
|
||||
},
|
||||
"signatures": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/DsseSignature"
|
||||
},
|
||||
"minItems": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"DsseSignature": {
|
||||
"type": "object",
|
||||
"description": "Signature on DSSE envelope",
|
||||
"required": ["sig"],
|
||||
"properties": {
|
||||
"keyid": {
|
||||
"type": "string"
|
||||
},
|
||||
"sig": {
|
||||
"type": "string",
|
||||
"contentEncoding": "base64"
|
||||
},
|
||||
"cert": {
|
||||
"type": "string",
|
||||
"contentEncoding": "base64",
|
||||
"description": "Fulcio certificate for keyless signing"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AttestationSearchQuery": {
|
||||
"type": "object",
|
||||
"description": "Query for searching attestations by finding criteria",
|
||||
"properties": {
|
||||
"finding_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
},
|
||||
"attestation_types": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"verification_status": {
|
||||
"type": "string",
|
||||
"enum": ["verified", "unverified", "failed", "any"]
|
||||
},
|
||||
"created_after": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"created_before": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"signer_identity": {
|
||||
"type": "string",
|
||||
"description": "Filter by signer email or identity"
|
||||
},
|
||||
"predicate_type": {
|
||||
"type": "string",
|
||||
"description": "Filter by in-toto predicate type"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AttestationSearchResult": {
|
||||
"type": "object",
|
||||
"description": "Result of attestation search",
|
||||
"required": ["pointers", "total_count"],
|
||||
"properties": {
|
||||
"pointers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AttestationPointer"
|
||||
}
|
||||
},
|
||||
"total_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"next_page_token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"FindingAttestationSummary": {
|
||||
"type": "object",
|
||||
"description": "Summary of attestations for a finding",
|
||||
"required": ["finding_id", "attestation_count"],
|
||||
"properties": {
|
||||
"finding_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"attestation_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"verified_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"latest_attestation": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"attestation_types": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"overall_verification_status": {
|
||||
"type": "string",
|
||||
"enum": ["all_verified", "partially_verified", "none_verified", "no_attestations"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"pointers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AttestationPointer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"examples": [
|
||||
{
|
||||
"pointers": [
|
||||
{
|
||||
"pointer_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"finding_id": "660e8400-e29b-41d4-a716-446655440001",
|
||||
"attestation_type": "dsse_envelope",
|
||||
"attestation_ref": {
|
||||
"attestation_id": "770e8400-e29b-41d4-a716-446655440002",
|
||||
"digest": "sha256:abc123def456789012345678901234567890123456789012345678901234abcd",
|
||||
"storage_uri": "s3://attestations/770e8400.../attestation.json",
|
||||
"payload_type": "application/vnd.in-toto+json",
|
||||
"predicate_type": "https://slsa.dev/provenance/v1",
|
||||
"subject_digests": [
|
||||
"sha256:def456..."
|
||||
],
|
||||
"signer_info": {
|
||||
"key_id": "fulcio:abc123",
|
||||
"issuer": "https://accounts.google.com",
|
||||
"subject": "scanner@stellaops.iam.gserviceaccount.com",
|
||||
"signed_at": "2025-12-06T10:00:00Z"
|
||||
},
|
||||
"rekor_entry": {
|
||||
"log_index": 12345678,
|
||||
"log_id": "c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d",
|
||||
"uuid": "24296fb24b8ad77a12345678901234567890123456789012345678901234abcd",
|
||||
"integrated_time": 1733479200
|
||||
}
|
||||
},
|
||||
"relationship": "verified_by",
|
||||
"verification_result": {
|
||||
"verified": true,
|
||||
"verified_at": "2025-12-06T10:05:00Z",
|
||||
"verifier": "stellaops-attestor",
|
||||
"verifier_version": "2025.10.0",
|
||||
"checks": [
|
||||
{
|
||||
"check_type": "signature_valid",
|
||||
"passed": true,
|
||||
"details": "ECDSA signature verified"
|
||||
},
|
||||
{
|
||||
"check_type": "certificate_valid",
|
||||
"passed": true,
|
||||
"details": "Fulcio certificate chain verified"
|
||||
},
|
||||
{
|
||||
"check_type": "rekor_entry_valid",
|
||||
"passed": true,
|
||||
"details": "Rekor inclusion proof verified"
|
||||
}
|
||||
],
|
||||
"warnings": [],
|
||||
"errors": []
|
||||
},
|
||||
"created_at": "2025-12-06T10:05:00Z",
|
||||
"created_by": "attestor-service"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
622
docs/schemas/console-observability.schema.json
Normal file
622
docs/schemas/console-observability.schema.json
Normal file
@@ -0,0 +1,622 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://stella-ops.org/schemas/console-observability.schema.json",
|
||||
"title": "StellaOps Console Observability Schema",
|
||||
"description": "Schema for console observability widgets, asset captures, and deterministic hashes. Unblocks DOCS-CONSOLE-OBS-52-001/002 and CONOBS5201 (2+ tasks).",
|
||||
"type": "object",
|
||||
"definitions": {
|
||||
"WidgetCapture": {
|
||||
"type": "object",
|
||||
"description": "Captured widget screenshot/payload",
|
||||
"required": ["capture_id", "widget_id", "captured_at", "digest"],
|
||||
"properties": {
|
||||
"capture_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"widget_id": {
|
||||
"type": "string",
|
||||
"description": "Widget identifier"
|
||||
},
|
||||
"widget_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"findings_summary",
|
||||
"severity_distribution",
|
||||
"risk_trend",
|
||||
"remediation_progress",
|
||||
"compliance_status",
|
||||
"asset_inventory",
|
||||
"vulnerability_timeline",
|
||||
"exception_status",
|
||||
"scan_activity",
|
||||
"alert_feed"
|
||||
]
|
||||
},
|
||||
"captured_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$",
|
||||
"description": "Content hash for determinism verification"
|
||||
},
|
||||
"screenshot": {
|
||||
"$ref": "#/definitions/ScreenshotRef"
|
||||
},
|
||||
"payload": {
|
||||
"$ref": "#/definitions/WidgetPayload"
|
||||
},
|
||||
"viewport": {
|
||||
"$ref": "#/definitions/ViewportConfig"
|
||||
},
|
||||
"theme": {
|
||||
"type": "string",
|
||||
"enum": ["light", "dark", "high_contrast"]
|
||||
},
|
||||
"locale": {
|
||||
"type": "string",
|
||||
"default": "en-US"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ScreenshotRef": {
|
||||
"type": "object",
|
||||
"description": "Reference to captured screenshot",
|
||||
"properties": {
|
||||
"filename": {
|
||||
"type": "string"
|
||||
},
|
||||
"format": {
|
||||
"type": "string",
|
||||
"enum": ["png", "webp", "svg"]
|
||||
},
|
||||
"width": {
|
||||
"type": "integer"
|
||||
},
|
||||
"height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"storage_uri": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$"
|
||||
}
|
||||
}
|
||||
},
|
||||
"WidgetPayload": {
|
||||
"type": "object",
|
||||
"description": "Widget data payload",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"description": "Canonical JSON data for widget"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$",
|
||||
"description": "Hash of canonical JSON payload"
|
||||
},
|
||||
"schema_version": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ViewportConfig": {
|
||||
"type": "object",
|
||||
"description": "Viewport configuration for capture",
|
||||
"properties": {
|
||||
"width": {
|
||||
"type": "integer",
|
||||
"default": 1920
|
||||
},
|
||||
"height": {
|
||||
"type": "integer",
|
||||
"default": 1080
|
||||
},
|
||||
"device_scale_factor": {
|
||||
"type": "number",
|
||||
"default": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"DashboardCapture": {
|
||||
"type": "object",
|
||||
"description": "Full dashboard capture",
|
||||
"required": ["capture_id", "dashboard_id", "captured_at"],
|
||||
"properties": {
|
||||
"capture_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"dashboard_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"dashboard_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"captured_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"widgets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/WidgetCapture"
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"$ref": "#/definitions/DashboardLayout"
|
||||
},
|
||||
"aggregate_digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$",
|
||||
"description": "Hash of all widget digests combined"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DashboardLayout": {
|
||||
"type": "object",
|
||||
"description": "Dashboard layout configuration",
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "integer",
|
||||
"default": 12
|
||||
},
|
||||
"row_height": {
|
||||
"type": "integer",
|
||||
"default": 100
|
||||
},
|
||||
"widgets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/WidgetPosition"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"WidgetPosition": {
|
||||
"type": "object",
|
||||
"description": "Widget position in grid",
|
||||
"required": ["widget_id", "x", "y", "width", "height"],
|
||||
"properties": {
|
||||
"widget_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"x": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"y": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"width": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"height": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"ObservabilityHubConfig": {
|
||||
"type": "object",
|
||||
"description": "Observability Hub configuration",
|
||||
"properties": {
|
||||
"hub_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"dashboards": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/DashboardConfig"
|
||||
}
|
||||
},
|
||||
"metrics_sources": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/MetricsSource"
|
||||
}
|
||||
},
|
||||
"alert_rules": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AlertRule"
|
||||
}
|
||||
},
|
||||
"retention_days": {
|
||||
"type": "integer",
|
||||
"default": 90
|
||||
}
|
||||
}
|
||||
},
|
||||
"DashboardConfig": {
|
||||
"type": "object",
|
||||
"description": "Dashboard configuration",
|
||||
"required": ["dashboard_id", "name"],
|
||||
"properties": {
|
||||
"dashboard_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"category": {
|
||||
"type": "string",
|
||||
"enum": ["security", "compliance", "operations", "executive"]
|
||||
},
|
||||
"refresh_interval_seconds": {
|
||||
"type": "integer",
|
||||
"default": 300
|
||||
},
|
||||
"time_range": {
|
||||
"$ref": "#/definitions/TimeRange"
|
||||
},
|
||||
"filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/FilterConfig"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"MetricsSource": {
|
||||
"type": "object",
|
||||
"description": "Metrics data source",
|
||||
"required": ["source_id", "type"],
|
||||
"properties": {
|
||||
"source_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["prometheus", "opentelemetry", "internal", "api"]
|
||||
},
|
||||
"endpoint": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"refresh_interval_seconds": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AlertRule": {
|
||||
"type": "object",
|
||||
"description": "Alert rule definition",
|
||||
"required": ["rule_id", "name", "condition"],
|
||||
"properties": {
|
||||
"rule_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"condition": {
|
||||
"$ref": "#/definitions/AlertCondition"
|
||||
},
|
||||
"severity": {
|
||||
"type": "string",
|
||||
"enum": ["critical", "high", "medium", "low", "info"]
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"notification_channels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"AlertCondition": {
|
||||
"type": "object",
|
||||
"description": "Alert trigger condition",
|
||||
"properties": {
|
||||
"metric": {
|
||||
"type": "string"
|
||||
},
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"enum": ["gt", "gte", "lt", "lte", "eq", "neq"]
|
||||
},
|
||||
"threshold": {
|
||||
"type": "number"
|
||||
},
|
||||
"duration_seconds": {
|
||||
"type": "integer",
|
||||
"description": "Duration condition must be true"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimeRange": {
|
||||
"type": "object",
|
||||
"description": "Time range configuration",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["relative", "absolute"]
|
||||
},
|
||||
"relative_value": {
|
||||
"type": "string",
|
||||
"description": "e.g., 24h, 7d, 30d"
|
||||
},
|
||||
"start": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"end": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
}
|
||||
},
|
||||
"FilterConfig": {
|
||||
"type": "object",
|
||||
"description": "Dashboard filter configuration",
|
||||
"properties": {
|
||||
"filter_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["select", "multi_select", "date_range", "text"]
|
||||
},
|
||||
"field": {
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "string"
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ForensicsCapture": {
|
||||
"type": "object",
|
||||
"description": "Forensics data capture",
|
||||
"required": ["capture_id", "incident_id", "captured_at"],
|
||||
"properties": {
|
||||
"capture_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"incident_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"captured_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"capture_type": {
|
||||
"type": "string",
|
||||
"enum": ["snapshot", "timeline", "correlation", "evidence_chain"]
|
||||
},
|
||||
"data_points": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ForensicsDataPoint"
|
||||
}
|
||||
},
|
||||
"correlations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/CorrelationLink"
|
||||
}
|
||||
},
|
||||
"evidence_digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ForensicsDataPoint": {
|
||||
"type": "object",
|
||||
"description": "Individual forensics data point",
|
||||
"properties": {
|
||||
"point_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"source": {
|
||||
"type": "string"
|
||||
},
|
||||
"data_type": {
|
||||
"type": "string",
|
||||
"enum": ["finding", "event", "metric", "log", "alert"]
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CorrelationLink": {
|
||||
"type": "object",
|
||||
"description": "Correlation between data points",
|
||||
"properties": {
|
||||
"source_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"target_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"relationship": {
|
||||
"type": "string",
|
||||
"enum": ["caused_by", "related_to", "precedes", "follows", "indicates"]
|
||||
},
|
||||
"confidence": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"AssetManifest": {
|
||||
"type": "object",
|
||||
"description": "Manifest of console assets for documentation",
|
||||
"required": ["manifest_id", "version", "assets"],
|
||||
"properties": {
|
||||
"manifest_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"generated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"assets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AssetEntry"
|
||||
}
|
||||
},
|
||||
"aggregate_digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AssetEntry": {
|
||||
"type": "object",
|
||||
"description": "Individual asset entry",
|
||||
"required": ["asset_id", "filename", "digest"],
|
||||
"properties": {
|
||||
"asset_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"filename": {
|
||||
"type": "string"
|
||||
},
|
||||
"category": {
|
||||
"type": "string",
|
||||
"enum": ["screenshot", "payload", "config", "schema"]
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$"
|
||||
},
|
||||
"size_bytes": {
|
||||
"type": "integer"
|
||||
},
|
||||
"mime_type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"captures": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/WidgetCapture"
|
||||
}
|
||||
},
|
||||
"manifest": {
|
||||
"$ref": "#/definitions/AssetManifest"
|
||||
}
|
||||
},
|
||||
"examples": [
|
||||
{
|
||||
"manifest": {
|
||||
"manifest_id": "console-obs-2025.10",
|
||||
"version": "2025.10.0",
|
||||
"generated_at": "2025-12-06T10:00:00Z",
|
||||
"assets": [
|
||||
{
|
||||
"asset_id": "findings-summary-widget",
|
||||
"filename": "findings-summary.png",
|
||||
"category": "screenshot",
|
||||
"description": "Findings summary widget showing severity distribution",
|
||||
"digest": "sha256:abc123def456789012345678901234567890123456789012345678901234abcd",
|
||||
"size_bytes": 45678,
|
||||
"mime_type": "image/png"
|
||||
},
|
||||
{
|
||||
"asset_id": "findings-summary-payload",
|
||||
"filename": "findings-summary.json",
|
||||
"category": "payload",
|
||||
"description": "Canonical JSON payload for findings summary",
|
||||
"digest": "sha256:def456abc789012345678901234567890123456789012345678901234abcdef",
|
||||
"size_bytes": 2345,
|
||||
"mime_type": "application/json"
|
||||
}
|
||||
],
|
||||
"aggregate_digest": "sha256:agg123def456789012345678901234567890123456789012345678901234agg"
|
||||
},
|
||||
"captures": [
|
||||
{
|
||||
"capture_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"widget_id": "findings-summary",
|
||||
"widget_type": "findings_summary",
|
||||
"captured_at": "2025-12-06T10:00:00Z",
|
||||
"digest": "sha256:abc123def456789012345678901234567890123456789012345678901234abcd",
|
||||
"screenshot": {
|
||||
"filename": "findings-summary.png",
|
||||
"format": "png",
|
||||
"width": 400,
|
||||
"height": 300,
|
||||
"digest": "sha256:abc123def456789012345678901234567890123456789012345678901234abcd"
|
||||
},
|
||||
"payload": {
|
||||
"data": {
|
||||
"critical": 5,
|
||||
"high": 23,
|
||||
"medium": 67,
|
||||
"low": 134,
|
||||
"total": 229
|
||||
},
|
||||
"digest": "sha256:def456abc789012345678901234567890123456789012345678901234abcdef"
|
||||
},
|
||||
"viewport": {
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
},
|
||||
"theme": "light"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
624
docs/schemas/deployment-service-list.schema.json
Normal file
624
docs/schemas/deployment-service-list.schema.json
Normal file
@@ -0,0 +1,624 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://stella-ops.org/schemas/deployment-service-list.schema.json",
|
||||
"title": "StellaOps Deployment Service List Schema",
|
||||
"description": "Schema for deployment service list, compose configuration, and version pins. Unblocks COMPOSE-44-001 through 45-003 (7 tasks).",
|
||||
"type": "object",
|
||||
"definitions": {
|
||||
"ServiceDefinition": {
|
||||
"type": "object",
|
||||
"description": "Service definition for deployment",
|
||||
"required": ["service_id", "name", "image", "version"],
|
||||
"properties": {
|
||||
"service_id": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9-]*$",
|
||||
"description": "Unique service identifier (kebab-case)"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Human-readable service name"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"image": {
|
||||
"type": "string",
|
||||
"description": "Container image (without tag)"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(-[a-z0-9.]+)?$",
|
||||
"description": "Service version (semver)"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$",
|
||||
"description": "Image digest for pinning"
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 65535,
|
||||
"description": "Primary service port"
|
||||
},
|
||||
"health_check": {
|
||||
"$ref": "#/definitions/HealthCheck"
|
||||
},
|
||||
"dependencies": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Service IDs this service depends on"
|
||||
},
|
||||
"environment": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/EnvVarDefinition"
|
||||
}
|
||||
},
|
||||
"volumes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/VolumeMount"
|
||||
}
|
||||
},
|
||||
"secrets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/SecretReference"
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"$ref": "#/definitions/ResourceLimits"
|
||||
},
|
||||
"replicas": {
|
||||
"$ref": "#/definitions/ReplicaConfig"
|
||||
},
|
||||
"labels": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"annotations": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"HealthCheck": {
|
||||
"type": "object",
|
||||
"description": "Health check configuration",
|
||||
"properties": {
|
||||
"endpoint": {
|
||||
"type": "string",
|
||||
"default": "/health"
|
||||
},
|
||||
"port": {
|
||||
"type": "integer"
|
||||
},
|
||||
"interval_seconds": {
|
||||
"type": "integer",
|
||||
"default": 30
|
||||
},
|
||||
"timeout_seconds": {
|
||||
"type": "integer",
|
||||
"default": 10
|
||||
},
|
||||
"retries": {
|
||||
"type": "integer",
|
||||
"default": 3
|
||||
},
|
||||
"start_period_seconds": {
|
||||
"type": "integer",
|
||||
"default": 60
|
||||
}
|
||||
}
|
||||
},
|
||||
"EnvVarDefinition": {
|
||||
"type": "object",
|
||||
"description": "Environment variable definition",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"default": {
|
||||
"type": "string"
|
||||
},
|
||||
"secret": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Whether this is a secret value"
|
||||
},
|
||||
"example": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"VolumeMount": {
|
||||
"type": "object",
|
||||
"description": "Volume mount configuration",
|
||||
"required": ["name", "mount_path"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"mount_path": {
|
||||
"type": "string"
|
||||
},
|
||||
"read_only": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["persistent", "ephemeral", "config", "secret"],
|
||||
"default": "persistent"
|
||||
},
|
||||
"size": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+(Mi|Gi|Ti)$",
|
||||
"description": "Volume size (e.g., 10Gi)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SecretReference": {
|
||||
"type": "object",
|
||||
"description": "Secret reference",
|
||||
"required": ["name"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"env_var": {
|
||||
"type": "string",
|
||||
"description": "Environment variable to inject secret"
|
||||
},
|
||||
"mount_path": {
|
||||
"type": "string",
|
||||
"description": "File path to mount secret"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ResourceLimits": {
|
||||
"type": "object",
|
||||
"description": "Resource limits and requests",
|
||||
"properties": {
|
||||
"cpu_request": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+(m)?$",
|
||||
"description": "CPU request (e.g., 100m, 1)"
|
||||
},
|
||||
"cpu_limit": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+(m)?$"
|
||||
},
|
||||
"memory_request": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+(Mi|Gi)$",
|
||||
"description": "Memory request (e.g., 256Mi)"
|
||||
},
|
||||
"memory_limit": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+(Mi|Gi)$"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReplicaConfig": {
|
||||
"type": "object",
|
||||
"description": "Replica configuration",
|
||||
"properties": {
|
||||
"min": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"default": 1
|
||||
},
|
||||
"max": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"default": 1
|
||||
},
|
||||
"target_cpu_utilization": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 100,
|
||||
"description": "Target CPU utilization for autoscaling"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DeploymentProfile": {
|
||||
"type": "object",
|
||||
"description": "Deployment profile (dev/staging/prod)",
|
||||
"required": ["profile_id", "name"],
|
||||
"properties": {
|
||||
"profile_id": {
|
||||
"type": "string",
|
||||
"enum": ["dev", "staging", "production", "airgap"]
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"service_overrides": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/ServiceOverride"
|
||||
}
|
||||
},
|
||||
"global_environment": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"network_policy": {
|
||||
"$ref": "#/definitions/NetworkPolicy"
|
||||
},
|
||||
"security_context": {
|
||||
"$ref": "#/definitions/SecurityContext"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ServiceOverride": {
|
||||
"type": "object",
|
||||
"description": "Service-specific overrides for a profile",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"replicas": {
|
||||
"$ref": "#/definitions/ReplicaConfig"
|
||||
},
|
||||
"resources": {
|
||||
"$ref": "#/definitions/ResourceLimits"
|
||||
},
|
||||
"environment": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"NetworkPolicy": {
|
||||
"type": "object",
|
||||
"description": "Network policy configuration",
|
||||
"properties": {
|
||||
"egress_allowed": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"allowed_external_hosts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Allowed external hosts for egress"
|
||||
},
|
||||
"internal_only_services": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Services not exposed externally"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SecurityContext": {
|
||||
"type": "object",
|
||||
"description": "Security context configuration",
|
||||
"properties": {
|
||||
"run_as_non_root": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"read_only_root_filesystem": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"drop_capabilities": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": ["ALL"]
|
||||
},
|
||||
"add_capabilities": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ServiceList": {
|
||||
"type": "object",
|
||||
"description": "Complete service list for deployment",
|
||||
"required": ["list_id", "version", "services"],
|
||||
"properties": {
|
||||
"list_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"services": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ServiceDefinition"
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/DeploymentProfile"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"$ref": "#/definitions/ExternalDependencies"
|
||||
},
|
||||
"observability": {
|
||||
"$ref": "#/definitions/ObservabilityConfig"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ExternalDependencies": {
|
||||
"type": "object",
|
||||
"description": "External dependencies (databases, queues, etc.)",
|
||||
"properties": {
|
||||
"mongodb": {
|
||||
"$ref": "#/definitions/MongoDbConfig"
|
||||
},
|
||||
"postgres": {
|
||||
"$ref": "#/definitions/PostgresConfig"
|
||||
},
|
||||
"redis": {
|
||||
"$ref": "#/definitions/RedisConfig"
|
||||
},
|
||||
"rabbitmq": {
|
||||
"$ref": "#/definitions/RabbitMqConfig"
|
||||
},
|
||||
"s3": {
|
||||
"$ref": "#/definitions/S3Config"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MongoDbConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"default": "7.0"
|
||||
},
|
||||
"replica_set": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"PostgresConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"default": "16"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RedisConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"default": "7"
|
||||
},
|
||||
"cluster": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"RabbitMqConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"default": "3.13"
|
||||
}
|
||||
}
|
||||
},
|
||||
"S3Config": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"provider": {
|
||||
"type": "string",
|
||||
"enum": ["minio", "aws", "gcs", "azure"],
|
||||
"default": "minio"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ObservabilityConfig": {
|
||||
"type": "object",
|
||||
"description": "Observability stack configuration",
|
||||
"properties": {
|
||||
"metrics": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"endpoint": {
|
||||
"type": "string",
|
||||
"default": "/metrics"
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"default": 9090
|
||||
}
|
||||
}
|
||||
},
|
||||
"tracing": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"otlp_endpoint": {
|
||||
"type": "string"
|
||||
},
|
||||
"sampling_rate": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"default": 0.1
|
||||
}
|
||||
}
|
||||
},
|
||||
"logging": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"level": {
|
||||
"type": "string",
|
||||
"enum": ["trace", "debug", "info", "warn", "error"],
|
||||
"default": "info"
|
||||
},
|
||||
"format": {
|
||||
"type": "string",
|
||||
"enum": ["json", "text"],
|
||||
"default": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"service_list": {
|
||||
"$ref": "#/definitions/ServiceList"
|
||||
}
|
||||
},
|
||||
"examples": [
|
||||
{
|
||||
"service_list": {
|
||||
"list_id": "stellaops-2025.10",
|
||||
"version": "2025.10.0",
|
||||
"updated_at": "2025-12-06T10:00:00Z",
|
||||
"services": [
|
||||
{
|
||||
"service_id": "concelier",
|
||||
"name": "Concelier",
|
||||
"description": "Vulnerability advisory ingestion and merge engine",
|
||||
"image": "ghcr.io/stellaops/concelier",
|
||||
"version": "2025.10.0",
|
||||
"digest": "sha256:abc123def456789012345678901234567890123456789012345678901234abcd",
|
||||
"port": 8080,
|
||||
"health_check": {
|
||||
"endpoint": "/health",
|
||||
"interval_seconds": 30
|
||||
},
|
||||
"dependencies": ["mongodb", "redis"],
|
||||
"resources": {
|
||||
"cpu_request": "100m",
|
||||
"cpu_limit": "1000m",
|
||||
"memory_request": "256Mi",
|
||||
"memory_limit": "1Gi"
|
||||
}
|
||||
},
|
||||
{
|
||||
"service_id": "scanner",
|
||||
"name": "Scanner",
|
||||
"description": "Container scanning with SBOM generation",
|
||||
"image": "ghcr.io/stellaops/scanner",
|
||||
"version": "2025.10.0",
|
||||
"port": 8081,
|
||||
"dependencies": ["concelier", "s3"]
|
||||
},
|
||||
{
|
||||
"service_id": "findings-ledger",
|
||||
"name": "Findings Ledger",
|
||||
"description": "Vulnerability findings storage",
|
||||
"image": "ghcr.io/stellaops/findings-ledger",
|
||||
"version": "2025.10.0",
|
||||
"port": 8082,
|
||||
"dependencies": ["postgres", "redis"]
|
||||
}
|
||||
],
|
||||
"profiles": [
|
||||
{
|
||||
"profile_id": "dev",
|
||||
"name": "Development",
|
||||
"description": "Local development profile",
|
||||
"global_environment": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"LOG_LEVEL": "Debug"
|
||||
}
|
||||
},
|
||||
{
|
||||
"profile_id": "production",
|
||||
"name": "Production",
|
||||
"description": "Production deployment profile",
|
||||
"security_context": {
|
||||
"run_as_non_root": true,
|
||||
"read_only_root_filesystem": true,
|
||||
"drop_capabilities": ["ALL"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"mongodb": {
|
||||
"enabled": true,
|
||||
"version": "7.0"
|
||||
},
|
||||
"postgres": {
|
||||
"enabled": true,
|
||||
"version": "16"
|
||||
},
|
||||
"redis": {
|
||||
"enabled": true,
|
||||
"version": "7"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
695
docs/schemas/devportal-api.schema.json
Normal file
695
docs/schemas/devportal-api.schema.json
Normal file
@@ -0,0 +1,695 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://stella-ops.org/schemas/devportal-api.schema.json",
|
||||
"title": "StellaOps DevPortal API Schema",
|
||||
"description": "Schema for DevPortal API baseline and SDK generator integration. Unblocks APIG0101 chain (62-001 to 63-004).",
|
||||
"type": "object",
|
||||
"definitions": {
|
||||
"ApiEndpoint": {
|
||||
"type": "object",
|
||||
"description": "API endpoint definition for DevPortal",
|
||||
"required": ["path", "method", "operation_id"],
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string",
|
||||
"pattern": "^/api/v[0-9]+/",
|
||||
"description": "API path (e.g., /api/v1/findings)"
|
||||
},
|
||||
"method": {
|
||||
"type": "string",
|
||||
"enum": ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]
|
||||
},
|
||||
"operation_id": {
|
||||
"type": "string",
|
||||
"description": "Unique operation identifier for SDK generation"
|
||||
},
|
||||
"summary": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"deprecated": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"deprecation_info": {
|
||||
"$ref": "#/definitions/DeprecationInfo"
|
||||
},
|
||||
"authentication": {
|
||||
"$ref": "#/definitions/AuthenticationRequirement"
|
||||
},
|
||||
"scopes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Required OAuth2 scopes"
|
||||
},
|
||||
"rate_limit": {
|
||||
"$ref": "#/definitions/RateLimitConfig"
|
||||
},
|
||||
"request": {
|
||||
"$ref": "#/definitions/RequestSpec"
|
||||
},
|
||||
"responses": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/ResponseSpec"
|
||||
}
|
||||
},
|
||||
"examples": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/EndpointExample"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"DeprecationInfo": {
|
||||
"type": "object",
|
||||
"description": "Deprecation details for sunset planning",
|
||||
"properties": {
|
||||
"deprecated_at": {
|
||||
"type": "string",
|
||||
"format": "date"
|
||||
},
|
||||
"sunset_at": {
|
||||
"type": "string",
|
||||
"format": "date"
|
||||
},
|
||||
"replacement": {
|
||||
"type": "string",
|
||||
"description": "Replacement endpoint path"
|
||||
},
|
||||
"migration_guide": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"description": "Link to migration documentation"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AuthenticationRequirement": {
|
||||
"type": "object",
|
||||
"description": "Authentication requirements for endpoint",
|
||||
"properties": {
|
||||
"required": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"schemes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["bearer", "api_key", "oauth2", "mtls", "basic"]
|
||||
}
|
||||
},
|
||||
"oauth2_flows": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["authorization_code", "client_credentials", "device_code"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"RateLimitConfig": {
|
||||
"type": "object",
|
||||
"description": "Rate limiting configuration",
|
||||
"properties": {
|
||||
"requests_per_minute": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"requests_per_hour": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"burst_limit": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"tier": {
|
||||
"type": "string",
|
||||
"enum": ["free", "standard", "premium", "enterprise"],
|
||||
"description": "Rate limit tier"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RequestSpec": {
|
||||
"type": "object",
|
||||
"description": "Request specification",
|
||||
"properties": {
|
||||
"content_types": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": ["application/json"]
|
||||
},
|
||||
"body_schema": {
|
||||
"type": "string",
|
||||
"description": "JSON Schema $ref for request body"
|
||||
},
|
||||
"parameters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ParameterSpec"
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/HeaderSpec"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ParameterSpec": {
|
||||
"type": "object",
|
||||
"description": "Parameter specification",
|
||||
"required": ["name", "in"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"in": {
|
||||
"type": "string",
|
||||
"enum": ["path", "query", "header", "cookie"]
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"description": "JSON Schema for parameter"
|
||||
},
|
||||
"example": {}
|
||||
}
|
||||
},
|
||||
"HeaderSpec": {
|
||||
"type": "object",
|
||||
"description": "Header specification",
|
||||
"required": ["name"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ResponseSpec": {
|
||||
"type": "object",
|
||||
"description": "Response specification",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"content_types": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"body_schema": {
|
||||
"type": "string",
|
||||
"description": "JSON Schema $ref for response body"
|
||||
},
|
||||
"headers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/HeaderSpec"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"EndpointExample": {
|
||||
"type": "object",
|
||||
"description": "Example request/response pair",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"request": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"response": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ApiService": {
|
||||
"type": "object",
|
||||
"description": "API service definition for DevPortal",
|
||||
"required": ["service_id", "name", "version", "endpoints"],
|
||||
"properties": {
|
||||
"service_id": {
|
||||
"type": "string",
|
||||
"description": "Unique service identifier"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Human-readable service name"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
|
||||
},
|
||||
"base_url": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"openapi_url": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"description": "URL to OpenAPI spec"
|
||||
},
|
||||
"documentation_url": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["stable", "beta", "alpha", "deprecated", "sunset"]
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"endpoints": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ApiEndpoint"
|
||||
}
|
||||
},
|
||||
"webhooks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/WebhookDefinition"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"WebhookDefinition": {
|
||||
"type": "object",
|
||||
"description": "Webhook event definition",
|
||||
"required": ["event_type", "payload_schema"],
|
||||
"properties": {
|
||||
"event_type": {
|
||||
"type": "string",
|
||||
"description": "Event type (e.g., finding.created)"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"payload_schema": {
|
||||
"type": "string",
|
||||
"description": "JSON Schema $ref for webhook payload"
|
||||
},
|
||||
"example_payload": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"SdkConfig": {
|
||||
"type": "object",
|
||||
"description": "SDK generator configuration",
|
||||
"required": ["language", "package_name"],
|
||||
"properties": {
|
||||
"language": {
|
||||
"type": "string",
|
||||
"enum": ["typescript", "python", "go", "java", "csharp", "ruby", "php"]
|
||||
},
|
||||
"package_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"package_version": {
|
||||
"type": "string"
|
||||
},
|
||||
"output_directory": {
|
||||
"type": "string"
|
||||
},
|
||||
"generator_options": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"description": "Language-specific generator options"
|
||||
},
|
||||
"custom_templates": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Custom template paths"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SdkGeneratorRequest": {
|
||||
"type": "object",
|
||||
"description": "Request to generate SDK from API spec",
|
||||
"required": ["service_id", "sdk_configs"],
|
||||
"properties": {
|
||||
"service_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"openapi_spec_url": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"sdk_configs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/SdkConfig"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"include_examples": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"include_tests": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"SdkGeneratorResult": {
|
||||
"type": "object",
|
||||
"description": "Result of SDK generation",
|
||||
"required": ["job_id", "status"],
|
||||
"properties": {
|
||||
"job_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["pending", "running", "completed", "failed"]
|
||||
},
|
||||
"started_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"completed_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"artifacts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/SdkArtifact"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"SdkArtifact": {
|
||||
"type": "object",
|
||||
"description": "Generated SDK artifact",
|
||||
"required": ["language", "artifact_url"],
|
||||
"properties": {
|
||||
"language": {
|
||||
"type": "string"
|
||||
},
|
||||
"package_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"artifact_url": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"checksum": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$"
|
||||
},
|
||||
"registry_url": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"description": "Package registry URL (npm, pypi, etc.)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DevPortalCatalog": {
|
||||
"type": "object",
|
||||
"description": "Full API catalog for DevPortal",
|
||||
"required": ["catalog_id", "version", "services"],
|
||||
"properties": {
|
||||
"catalog_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"services": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ApiService"
|
||||
}
|
||||
},
|
||||
"global_tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/TagDefinition"
|
||||
}
|
||||
},
|
||||
"authentication_info": {
|
||||
"$ref": "#/definitions/AuthenticationInfo"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TagDefinition": {
|
||||
"type": "object",
|
||||
"description": "Tag definition for categorization",
|
||||
"required": ["name"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"external_docs": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AuthenticationInfo": {
|
||||
"type": "object",
|
||||
"description": "Global authentication information",
|
||||
"properties": {
|
||||
"oauth2_authorization_url": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"oauth2_token_url": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"api_key_header": {
|
||||
"type": "string",
|
||||
"default": "X-API-Key"
|
||||
},
|
||||
"documentation_url": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ApiCompatibilityReport": {
|
||||
"type": "object",
|
||||
"description": "API compatibility check report",
|
||||
"required": ["report_id", "checked_at", "result"],
|
||||
"properties": {
|
||||
"report_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"checked_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"base_version": {
|
||||
"type": "string"
|
||||
},
|
||||
"target_version": {
|
||||
"type": "string"
|
||||
},
|
||||
"result": {
|
||||
"type": "string",
|
||||
"enum": ["compatible", "breaking", "minor_changes"]
|
||||
},
|
||||
"breaking_changes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ApiChange"
|
||||
}
|
||||
},
|
||||
"non_breaking_changes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ApiChange"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ApiChange": {
|
||||
"type": "object",
|
||||
"description": "Individual API change",
|
||||
"required": ["change_type", "path"],
|
||||
"properties": {
|
||||
"change_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"endpoint_added",
|
||||
"endpoint_removed",
|
||||
"parameter_added",
|
||||
"parameter_removed",
|
||||
"parameter_type_changed",
|
||||
"response_changed",
|
||||
"schema_changed",
|
||||
"deprecation_added"
|
||||
]
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"method": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"severity": {
|
||||
"type": "string",
|
||||
"enum": ["breaking", "warning", "info"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"catalog": {
|
||||
"$ref": "#/definitions/DevPortalCatalog"
|
||||
}
|
||||
},
|
||||
"examples": [
|
||||
{
|
||||
"catalog": {
|
||||
"catalog_id": "stellaops-api-catalog",
|
||||
"version": "2025.10.0",
|
||||
"updated_at": "2025-12-06T10:00:00Z",
|
||||
"services": [
|
||||
{
|
||||
"service_id": "findings-ledger",
|
||||
"name": "Findings Ledger",
|
||||
"description": "Vulnerability findings storage and query service",
|
||||
"version": "1.0.0",
|
||||
"base_url": "https://api.stellaops.io/findings",
|
||||
"openapi_url": "https://api.stellaops.io/findings/.well-known/openapi.json",
|
||||
"status": "stable",
|
||||
"tags": ["findings", "vulnerabilities", "ledger"],
|
||||
"endpoints": [
|
||||
{
|
||||
"path": "/api/v1/findings",
|
||||
"method": "GET",
|
||||
"operation_id": "listFindings",
|
||||
"summary": "List findings with pagination and filtering",
|
||||
"tags": ["findings"],
|
||||
"authentication": {
|
||||
"required": true,
|
||||
"schemes": ["bearer", "oauth2"]
|
||||
},
|
||||
"scopes": ["findings:read"],
|
||||
"rate_limit": {
|
||||
"requests_per_minute": 100,
|
||||
"tier": "standard"
|
||||
},
|
||||
"request": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "page",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"default": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"default": 50,
|
||||
"maximum": 200
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Paginated list of findings",
|
||||
"content_types": ["application/json"]
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"authentication_info": {
|
||||
"oauth2_authorization_url": "https://auth.stellaops.io/authorize",
|
||||
"oauth2_token_url": "https://auth.stellaops.io/token",
|
||||
"api_key_header": "X-StellaOps-API-Key"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
663
docs/schemas/evidence-locker-dsse.schema.json
Normal file
663
docs/schemas/evidence-locker-dsse.schema.json
Normal file
@@ -0,0 +1,663 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://stella-ops.org/schemas/evidence-locker-dsse.schema.json",
|
||||
"title": "StellaOps Evidence Locker DSSE Schema",
|
||||
"description": "Schema for Evidence Locker DSSE attestations, Merkle locker payloads, and evidence batch operations. Unblocks EXCITITOR-OBS-52/53/54.",
|
||||
"type": "object",
|
||||
"definitions": {
|
||||
"EvidenceLockerBatch": {
|
||||
"type": "object",
|
||||
"description": "A batch of evidence artifacts submitted to the Evidence Locker",
|
||||
"required": ["batch_id", "artifacts", "created_at"],
|
||||
"properties": {
|
||||
"batch_id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Unique identifier for this evidence batch"
|
||||
},
|
||||
"artifacts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/EvidenceArtifact"
|
||||
},
|
||||
"minItems": 1,
|
||||
"description": "List of evidence artifacts in this batch"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"created_by": {
|
||||
"type": "string",
|
||||
"description": "Identity of the batch creator"
|
||||
},
|
||||
"tenant_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"project_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"pipeline_context": {
|
||||
"$ref": "#/definitions/PipelineContext"
|
||||
},
|
||||
"aggregate_digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$",
|
||||
"description": "SHA-256 digest of all artifact digests concatenated and sorted"
|
||||
},
|
||||
"merkle_root": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$",
|
||||
"description": "Merkle tree root of the batch"
|
||||
},
|
||||
"dsse_envelope": {
|
||||
"$ref": "#/definitions/DsseEnvelope"
|
||||
},
|
||||
"retention_policy": {
|
||||
"$ref": "#/definitions/RetentionPolicy"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["pending", "committed", "anchored", "sealed", "expired"],
|
||||
"description": "Current status of the batch"
|
||||
}
|
||||
}
|
||||
},
|
||||
"EvidenceArtifact": {
|
||||
"type": "object",
|
||||
"description": "A single evidence artifact within a batch",
|
||||
"required": ["artifact_id", "artifact_type", "digest", "stored_at"],
|
||||
"properties": {
|
||||
"artifact_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"artifact_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"sbom",
|
||||
"vex",
|
||||
"scan_result",
|
||||
"attestation",
|
||||
"policy_evaluation",
|
||||
"callgraph",
|
||||
"runtime_facts",
|
||||
"timeline_event",
|
||||
"audit_log",
|
||||
"configuration",
|
||||
"signature"
|
||||
],
|
||||
"description": "Type of evidence artifact"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$",
|
||||
"description": "SHA-256 digest of the artifact content"
|
||||
},
|
||||
"content_type": {
|
||||
"type": "string",
|
||||
"description": "MIME type of the artifact (e.g., application/json)"
|
||||
},
|
||||
"size_bytes": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"storage_uri": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"description": "URI to retrieve the artifact from object storage"
|
||||
},
|
||||
"stored_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"labels": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Key-value labels for filtering and organization"
|
||||
},
|
||||
"subject": {
|
||||
"$ref": "#/definitions/ArtifactSubject"
|
||||
},
|
||||
"provenance": {
|
||||
"$ref": "#/definitions/ArtifactProvenance"
|
||||
},
|
||||
"merkle_position": {
|
||||
"$ref": "#/definitions/MerklePosition"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ArtifactSubject": {
|
||||
"type": "object",
|
||||
"description": "Subject the artifact relates to (e.g., a component or vulnerability)",
|
||||
"properties": {
|
||||
"subject_type": {
|
||||
"type": "string",
|
||||
"enum": ["component", "vulnerability", "product", "scan", "pipeline", "policy"]
|
||||
},
|
||||
"identifier": {
|
||||
"type": "string",
|
||||
"description": "Subject identifier (PURL, CVE ID, etc.)"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ArtifactProvenance": {
|
||||
"type": "object",
|
||||
"description": "Provenance information for an artifact",
|
||||
"properties": {
|
||||
"producer": {
|
||||
"type": "string",
|
||||
"description": "Service or tool that produced this artifact"
|
||||
},
|
||||
"producer_version": {
|
||||
"type": "string"
|
||||
},
|
||||
"produced_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"build_invocation_id": {
|
||||
"type": "string",
|
||||
"description": "CI/CD build or pipeline invocation ID"
|
||||
},
|
||||
"entry_point": {
|
||||
"type": "string",
|
||||
"description": "Entry point command or script"
|
||||
},
|
||||
"input_digests": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$"
|
||||
},
|
||||
"description": "Digests of inputs used to produce this artifact"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MerklePosition": {
|
||||
"type": "object",
|
||||
"description": "Position in the Merkle tree for tamper detection",
|
||||
"required": ["index", "tree_size", "proof"],
|
||||
"properties": {
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Leaf index in the Merkle tree"
|
||||
},
|
||||
"tree_size": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"description": "Total number of leaves in the tree"
|
||||
},
|
||||
"proof": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$"
|
||||
},
|
||||
"description": "Audit path hashes for verification"
|
||||
},
|
||||
"root_digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$",
|
||||
"description": "Merkle root at time of inclusion"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PipelineContext": {
|
||||
"type": "object",
|
||||
"description": "Context of the pipeline that created the batch",
|
||||
"properties": {
|
||||
"pipeline_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"pipeline_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"run_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"step_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"repository": {
|
||||
"type": "string"
|
||||
},
|
||||
"commit_sha": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{40}$"
|
||||
},
|
||||
"branch": {
|
||||
"type": "string"
|
||||
},
|
||||
"environment": {
|
||||
"type": "string",
|
||||
"enum": ["development", "staging", "production"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"DsseEnvelope": {
|
||||
"type": "object",
|
||||
"description": "DSSE (Dead Simple Signing Envelope) for batch attestation",
|
||||
"required": ["payloadType", "payload", "signatures"],
|
||||
"properties": {
|
||||
"payloadType": {
|
||||
"type": "string",
|
||||
"const": "application/vnd.stellaops.evidence-batch.v1+json",
|
||||
"description": "DSSE payload type"
|
||||
},
|
||||
"payload": {
|
||||
"type": "string",
|
||||
"contentEncoding": "base64",
|
||||
"description": "Base64-encoded batch payload"
|
||||
},
|
||||
"signatures": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/DsseSignature"
|
||||
},
|
||||
"minItems": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"DsseSignature": {
|
||||
"type": "object",
|
||||
"description": "A signature on the DSSE envelope",
|
||||
"required": ["sig"],
|
||||
"properties": {
|
||||
"keyid": {
|
||||
"type": "string",
|
||||
"description": "Key identifier (e.g., Fulcio certificate fingerprint)"
|
||||
},
|
||||
"sig": {
|
||||
"type": "string",
|
||||
"contentEncoding": "base64",
|
||||
"description": "Base64-encoded signature"
|
||||
},
|
||||
"cert": {
|
||||
"type": "string",
|
||||
"contentEncoding": "base64",
|
||||
"description": "Base64-encoded signing certificate (for keyless signing)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RetentionPolicy": {
|
||||
"type": "object",
|
||||
"description": "Retention policy for evidence artifacts",
|
||||
"properties": {
|
||||
"retention_days": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"description": "Number of days to retain artifacts"
|
||||
},
|
||||
"retention_class": {
|
||||
"type": "string",
|
||||
"enum": ["standard", "extended", "compliance", "indefinite"],
|
||||
"description": "Retention class for policy lookup"
|
||||
},
|
||||
"expires_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"hold_until": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Legal hold expiration (overrides retention_days)"
|
||||
},
|
||||
"archive_after_days": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Days before archiving to cold storage"
|
||||
},
|
||||
"delete_on_expiry": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether to delete artifacts when retention expires"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TimelineEvent": {
|
||||
"type": "object",
|
||||
"description": "Timeline event linked to evidence artifacts",
|
||||
"required": ["event_id", "event_type", "occurred_at"],
|
||||
"properties": {
|
||||
"event_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"event_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"evidence_batch_created",
|
||||
"evidence_batch_committed",
|
||||
"merkle_anchor_published",
|
||||
"artifact_accessed",
|
||||
"artifact_verified",
|
||||
"retention_extended",
|
||||
"artifact_archived",
|
||||
"artifact_deleted",
|
||||
"batch_sealed",
|
||||
"verification_failed"
|
||||
]
|
||||
},
|
||||
"occurred_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"actor": {
|
||||
"type": "string",
|
||||
"description": "User or service that triggered the event"
|
||||
},
|
||||
"batch_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"artifact_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"description": "Affected artifact IDs"
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"evidence_refs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/EvidenceRef"
|
||||
},
|
||||
"description": "References to related evidence"
|
||||
}
|
||||
}
|
||||
},
|
||||
"EvidenceRef": {
|
||||
"type": "object",
|
||||
"description": "Reference to evidence artifact",
|
||||
"required": ["artifact_id", "digest"],
|
||||
"properties": {
|
||||
"artifact_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$"
|
||||
},
|
||||
"storage_uri": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"artifact_type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MerkleAnchor": {
|
||||
"type": "object",
|
||||
"description": "Merkle tree anchor published to transparency log",
|
||||
"required": ["anchor_id", "merkle_root", "tree_size", "published_at"],
|
||||
"properties": {
|
||||
"anchor_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"merkle_root": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$"
|
||||
},
|
||||
"tree_size": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"published_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"batch_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"description": "Batches included in this anchor"
|
||||
},
|
||||
"previous_anchor_id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Previous anchor in the chain"
|
||||
},
|
||||
"consistency_proof": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$"
|
||||
},
|
||||
"description": "Consistency proof from previous anchor"
|
||||
},
|
||||
"rekor_entry": {
|
||||
"$ref": "#/definitions/RekorEntry"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RekorEntry": {
|
||||
"type": "object",
|
||||
"description": "Entry in Sigstore Rekor transparency log",
|
||||
"properties": {
|
||||
"log_index": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"log_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"integrated_time": {
|
||||
"type": "integer",
|
||||
"description": "Unix timestamp when entry was integrated"
|
||||
},
|
||||
"uuid": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{64}$"
|
||||
},
|
||||
"body": {
|
||||
"type": "string",
|
||||
"contentEncoding": "base64"
|
||||
},
|
||||
"inclusion_proof": {
|
||||
"$ref": "#/definitions/InclusionProof"
|
||||
}
|
||||
}
|
||||
},
|
||||
"InclusionProof": {
|
||||
"type": "object",
|
||||
"description": "Inclusion proof for transparency log",
|
||||
"required": ["log_index", "root_hash", "tree_size", "hashes"],
|
||||
"properties": {
|
||||
"log_index": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"root_hash": {
|
||||
"type": "string",
|
||||
"contentEncoding": "base64"
|
||||
},
|
||||
"tree_size": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"hashes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"contentEncoding": "base64"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"VerificationRequest": {
|
||||
"type": "object",
|
||||
"description": "Request to verify evidence artifact integrity",
|
||||
"required": ["artifact_id"],
|
||||
"properties": {
|
||||
"artifact_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"expected_digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$"
|
||||
},
|
||||
"verify_merkle_proof": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"verify_dsse_signature": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"verify_rekor_inclusion": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"VerificationResult": {
|
||||
"type": "object",
|
||||
"description": "Result of evidence verification",
|
||||
"required": ["artifact_id", "verified", "timestamp"],
|
||||
"properties": {
|
||||
"artifact_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"verified": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"checks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/VerificationCheck"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"type": "string",
|
||||
"description": "Error message if verification failed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"VerificationCheck": {
|
||||
"type": "object",
|
||||
"description": "Individual verification check result",
|
||||
"required": ["check_type", "passed"],
|
||||
"properties": {
|
||||
"check_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"digest_match",
|
||||
"merkle_proof_valid",
|
||||
"dsse_signature_valid",
|
||||
"certificate_valid",
|
||||
"rekor_inclusion_valid",
|
||||
"timestamp_valid"
|
||||
]
|
||||
},
|
||||
"passed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"details": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"batches": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/EvidenceLockerBatch"
|
||||
}
|
||||
}
|
||||
},
|
||||
"examples": [
|
||||
{
|
||||
"batches": [
|
||||
{
|
||||
"batch_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"artifacts": [
|
||||
{
|
||||
"artifact_id": "660e8400-e29b-41d4-a716-446655440001",
|
||||
"artifact_type": "sbom",
|
||||
"digest": "sha256:abc123def456789012345678901234567890123456789012345678901234abcd",
|
||||
"content_type": "application/vnd.cyclonedx+json",
|
||||
"size_bytes": 15234,
|
||||
"storage_uri": "s3://evidence-locker/batches/550e8400.../sbom.json",
|
||||
"stored_at": "2025-12-06T10:00:00Z",
|
||||
"labels": {
|
||||
"project": "frontend-app",
|
||||
"environment": "production"
|
||||
},
|
||||
"subject": {
|
||||
"subject_type": "component",
|
||||
"identifier": "pkg:npm/frontend-app@1.0.0",
|
||||
"digest": "sha256:def456..."
|
||||
},
|
||||
"provenance": {
|
||||
"producer": "stellaops-scanner",
|
||||
"producer_version": "2025.10.0",
|
||||
"produced_at": "2025-12-06T09:55:00Z",
|
||||
"build_invocation_id": "ci-12345"
|
||||
},
|
||||
"merkle_position": {
|
||||
"index": 0,
|
||||
"tree_size": 3,
|
||||
"proof": [
|
||||
"sha256:111...",
|
||||
"sha256:222..."
|
||||
],
|
||||
"root_digest": "sha256:merkleroot..."
|
||||
}
|
||||
}
|
||||
],
|
||||
"created_at": "2025-12-06T10:00:00Z",
|
||||
"created_by": "stellaops-pipeline",
|
||||
"tenant_id": "tenant-001",
|
||||
"aggregate_digest": "sha256:aggregate123...",
|
||||
"merkle_root": "sha256:merkleroot...",
|
||||
"dsse_envelope": {
|
||||
"payloadType": "application/vnd.stellaops.evidence-batch.v1+json",
|
||||
"payload": "eyJiYXRjaF9pZCI6IjU1MGU4NDAwLi4uIn0=",
|
||||
"signatures": [
|
||||
{
|
||||
"keyid": "fulcio:abc123",
|
||||
"sig": "MEUCIQDxxx..."
|
||||
}
|
||||
]
|
||||
},
|
||||
"retention_policy": {
|
||||
"retention_days": 365,
|
||||
"retention_class": "compliance",
|
||||
"archive_after_days": 90
|
||||
},
|
||||
"status": "committed"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
745
docs/schemas/exception-lifecycle.schema.json
Normal file
745
docs/schemas/exception-lifecycle.schema.json
Normal file
@@ -0,0 +1,745 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://stella-ops.org/schemas/exception-lifecycle.schema.json",
|
||||
"title": "StellaOps Exception Lifecycle Schema",
|
||||
"description": "Schema for exception lifecycle, routing, approvals, and governance. Unblocks DOCS-EXC-25-001 through 25-006 (5 tasks).",
|
||||
"type": "object",
|
||||
"definitions": {
|
||||
"Exception": {
|
||||
"type": "object",
|
||||
"description": "Security exception request",
|
||||
"required": ["exception_id", "finding_id", "status", "justification", "requested_at", "requested_by"],
|
||||
"properties": {
|
||||
"exception_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"finding_id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Finding this exception applies to"
|
||||
},
|
||||
"finding_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"description": "Multiple findings for bulk exception"
|
||||
},
|
||||
"exception_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"false_positive",
|
||||
"risk_accepted",
|
||||
"compensating_control",
|
||||
"deferred_remediation",
|
||||
"not_applicable",
|
||||
"wont_fix"
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/definitions/ExceptionStatus"
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"minLength": 10,
|
||||
"description": "Business justification for exception"
|
||||
},
|
||||
"compensating_controls": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/CompensatingControl"
|
||||
}
|
||||
},
|
||||
"scope": {
|
||||
"$ref": "#/definitions/ExceptionScope"
|
||||
},
|
||||
"effective_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"expires_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"requested_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"requested_by": {
|
||||
"type": "string",
|
||||
"description": "User who requested exception"
|
||||
},
|
||||
"approvals": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Approval"
|
||||
}
|
||||
},
|
||||
"routing": {
|
||||
"$ref": "#/definitions/RoutingInfo"
|
||||
},
|
||||
"audit_trail": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AuditEntry"
|
||||
}
|
||||
},
|
||||
"risk_assessment": {
|
||||
"$ref": "#/definitions/RiskAssessment"
|
||||
},
|
||||
"attachments": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Attachment"
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ExceptionStatus": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"draft",
|
||||
"pending_review",
|
||||
"pending_approval",
|
||||
"approved",
|
||||
"rejected",
|
||||
"expired",
|
||||
"revoked",
|
||||
"superseded"
|
||||
]
|
||||
},
|
||||
"CompensatingControl": {
|
||||
"type": "object",
|
||||
"description": "Compensating control for accepted risk",
|
||||
"required": ["control_id", "description"],
|
||||
"properties": {
|
||||
"control_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"control_type": {
|
||||
"type": "string",
|
||||
"enum": ["technical", "administrative", "physical", "procedural"]
|
||||
},
|
||||
"effectiveness": {
|
||||
"type": "string",
|
||||
"enum": ["high", "medium", "low"]
|
||||
},
|
||||
"verification_method": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_verified_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ExceptionScope": {
|
||||
"type": "object",
|
||||
"description": "Scope of the exception",
|
||||
"properties": {
|
||||
"scope_type": {
|
||||
"type": "string",
|
||||
"enum": ["finding", "component", "project", "organization"]
|
||||
},
|
||||
"tenant_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"project_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
},
|
||||
"component_patterns": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "PURL patterns to match"
|
||||
},
|
||||
"cve_patterns": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "CVE patterns to match"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Approval": {
|
||||
"type": "object",
|
||||
"description": "Approval record",
|
||||
"required": ["approver_id", "decision", "decided_at"],
|
||||
"properties": {
|
||||
"approval_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"approver_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"approver_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"approver_role": {
|
||||
"type": "string"
|
||||
},
|
||||
"decision": {
|
||||
"type": "string",
|
||||
"enum": ["approved", "rejected", "deferred"]
|
||||
},
|
||||
"decided_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"comments": {
|
||||
"type": "string"
|
||||
},
|
||||
"conditions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Conditions attached to approval"
|
||||
},
|
||||
"signature": {
|
||||
"type": "string",
|
||||
"description": "Digital signature of approval"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RoutingInfo": {
|
||||
"type": "object",
|
||||
"description": "Routing configuration for exception workflow",
|
||||
"properties": {
|
||||
"workflow_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"current_step": {
|
||||
"type": "string"
|
||||
},
|
||||
"approval_chain": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ApprovalStep"
|
||||
}
|
||||
},
|
||||
"escalation_policy": {
|
||||
"$ref": "#/definitions/EscalationPolicy"
|
||||
},
|
||||
"notifications": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/NotificationConfig"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ApprovalStep": {
|
||||
"type": "object",
|
||||
"description": "Step in approval chain",
|
||||
"required": ["step_id", "approvers"],
|
||||
"properties": {
|
||||
"step_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"step_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"approvers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ApproverConfig"
|
||||
}
|
||||
},
|
||||
"approval_type": {
|
||||
"type": "string",
|
||||
"enum": ["any", "all", "quorum"],
|
||||
"default": "any"
|
||||
},
|
||||
"quorum_count": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"timeout_hours": {
|
||||
"type": "integer"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["pending", "completed", "skipped"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"ApproverConfig": {
|
||||
"type": "object",
|
||||
"description": "Approver configuration",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["user", "role", "group", "dynamic"]
|
||||
},
|
||||
"identifier": {
|
||||
"type": "string"
|
||||
},
|
||||
"fallback_approvers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"EscalationPolicy": {
|
||||
"type": "object",
|
||||
"description": "Escalation policy for stalled approvals",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"escalation_after_hours": {
|
||||
"type": "integer",
|
||||
"default": 48
|
||||
},
|
||||
"max_escalation_levels": {
|
||||
"type": "integer",
|
||||
"default": 3
|
||||
},
|
||||
"escalation_targets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"auto_approve_on_timeout": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"auto_reject_on_timeout": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"NotificationConfig": {
|
||||
"type": "object",
|
||||
"description": "Notification configuration",
|
||||
"properties": {
|
||||
"event": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"exception_created",
|
||||
"pending_approval",
|
||||
"approved",
|
||||
"rejected",
|
||||
"expiring_soon",
|
||||
"expired",
|
||||
"escalated"
|
||||
]
|
||||
},
|
||||
"channels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["email", "slack", "teams", "webhook"]
|
||||
}
|
||||
},
|
||||
"recipients": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"AuditEntry": {
|
||||
"type": "object",
|
||||
"description": "Audit trail entry",
|
||||
"required": ["action", "actor", "timestamp"],
|
||||
"properties": {
|
||||
"entry_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"action": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"created",
|
||||
"updated",
|
||||
"submitted",
|
||||
"approved",
|
||||
"rejected",
|
||||
"revoked",
|
||||
"expired",
|
||||
"escalated",
|
||||
"comment_added"
|
||||
]
|
||||
},
|
||||
"actor": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"ip_address": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RiskAssessment": {
|
||||
"type": "object",
|
||||
"description": "Risk assessment for exception",
|
||||
"properties": {
|
||||
"original_risk_score": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 10
|
||||
},
|
||||
"residual_risk_score": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 10,
|
||||
"description": "Risk after compensating controls"
|
||||
},
|
||||
"risk_factors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/RiskFactor"
|
||||
}
|
||||
},
|
||||
"business_impact": {
|
||||
"type": "string",
|
||||
"enum": ["critical", "high", "medium", "low", "minimal"]
|
||||
},
|
||||
"data_sensitivity": {
|
||||
"type": "string",
|
||||
"enum": ["public", "internal", "confidential", "restricted"]
|
||||
},
|
||||
"assessed_by": {
|
||||
"type": "string"
|
||||
},
|
||||
"assessed_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RiskFactor": {
|
||||
"type": "object",
|
||||
"description": "Individual risk factor",
|
||||
"properties": {
|
||||
"factor_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"impact": {
|
||||
"type": "string",
|
||||
"enum": ["increase", "decrease", "neutral"]
|
||||
},
|
||||
"weight": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
},
|
||||
"rationale": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Attachment": {
|
||||
"type": "object",
|
||||
"description": "Supporting attachment",
|
||||
"required": ["attachment_id", "filename"],
|
||||
"properties": {
|
||||
"attachment_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"filename": {
|
||||
"type": "string"
|
||||
},
|
||||
"content_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"size_bytes": {
|
||||
"type": "integer"
|
||||
},
|
||||
"storage_uri": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"checksum": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$"
|
||||
},
|
||||
"uploaded_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"uploaded_by": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ExceptionPolicy": {
|
||||
"type": "object",
|
||||
"description": "Exception governance policy",
|
||||
"required": ["policy_id", "name"],
|
||||
"properties": {
|
||||
"policy_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"max_exception_duration_days": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"require_compensating_controls": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"require_risk_assessment": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"severity_thresholds": {
|
||||
"$ref": "#/definitions/SeverityThresholds"
|
||||
},
|
||||
"auto_renewal": {
|
||||
"$ref": "#/definitions/AutoRenewalConfig"
|
||||
},
|
||||
"compliance_frameworks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Applicable compliance frameworks"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SeverityThresholds": {
|
||||
"type": "object",
|
||||
"description": "Approval thresholds by severity",
|
||||
"properties": {
|
||||
"critical": {
|
||||
"$ref": "#/definitions/ThresholdConfig"
|
||||
},
|
||||
"high": {
|
||||
"$ref": "#/definitions/ThresholdConfig"
|
||||
},
|
||||
"medium": {
|
||||
"$ref": "#/definitions/ThresholdConfig"
|
||||
},
|
||||
"low": {
|
||||
"$ref": "#/definitions/ThresholdConfig"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ThresholdConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"max_duration_days": {
|
||||
"type": "integer"
|
||||
},
|
||||
"required_approver_roles": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"min_approvers": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"allow_exception": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"AutoRenewalConfig": {
|
||||
"type": "object",
|
||||
"description": "Auto-renewal configuration",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"max_renewals": {
|
||||
"type": "integer"
|
||||
},
|
||||
"renewal_review_required": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ExceptionSearchQuery": {
|
||||
"type": "object",
|
||||
"description": "Query for searching exceptions",
|
||||
"properties": {
|
||||
"exception_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
},
|
||||
"finding_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
},
|
||||
"statuses": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ExceptionStatus"
|
||||
}
|
||||
},
|
||||
"exception_types": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"requested_by": {
|
||||
"type": "string"
|
||||
},
|
||||
"approved_by": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_after": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"created_before": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"expiring_within_days": {
|
||||
"type": "integer"
|
||||
},
|
||||
"page": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"default": 1
|
||||
},
|
||||
"page_size": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 200,
|
||||
"default": 50
|
||||
}
|
||||
}
|
||||
},
|
||||
"ExceptionSearchResult": {
|
||||
"type": "object",
|
||||
"description": "Search result",
|
||||
"required": ["exceptions", "total_count"],
|
||||
"properties": {
|
||||
"exceptions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Exception"
|
||||
}
|
||||
},
|
||||
"total_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"page": {
|
||||
"type": "integer"
|
||||
},
|
||||
"page_size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"next_page_token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"exceptions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Exception"
|
||||
}
|
||||
}
|
||||
},
|
||||
"examples": [
|
||||
{
|
||||
"exceptions": [
|
||||
{
|
||||
"exception_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"finding_id": "660e8400-e29b-41d4-a716-446655440001",
|
||||
"exception_type": "risk_accepted",
|
||||
"status": "approved",
|
||||
"justification": "This vulnerability exists in a test-only dependency that is not deployed to production. The affected code path is never executed in any deployed environment.",
|
||||
"compensating_controls": [
|
||||
{
|
||||
"control_id": "CC-001",
|
||||
"description": "Network segmentation prevents access to affected component",
|
||||
"control_type": "technical",
|
||||
"effectiveness": "high"
|
||||
}
|
||||
],
|
||||
"scope": {
|
||||
"scope_type": "component",
|
||||
"component_patterns": ["pkg:npm/test-lib@*"]
|
||||
},
|
||||
"effective_at": "2025-12-06T00:00:00Z",
|
||||
"expires_at": "2026-06-06T00:00:00Z",
|
||||
"requested_at": "2025-12-01T10:00:00Z",
|
||||
"requested_by": "dev-team-lead@example.com",
|
||||
"approvals": [
|
||||
{
|
||||
"approval_id": "770e8400-e29b-41d4-a716-446655440002",
|
||||
"approver_id": "security-manager@example.com",
|
||||
"approver_name": "Jane Security",
|
||||
"approver_role": "Security Manager",
|
||||
"decision": "approved",
|
||||
"decided_at": "2025-12-05T14:00:00Z",
|
||||
"comments": "Approved with 6-month duration due to low residual risk",
|
||||
"conditions": ["Re-evaluate if component moves to production"]
|
||||
}
|
||||
],
|
||||
"risk_assessment": {
|
||||
"original_risk_score": 7.5,
|
||||
"residual_risk_score": 2.0,
|
||||
"business_impact": "low",
|
||||
"data_sensitivity": "internal"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
673
docs/schemas/excititor-chunk-api.openapi.yaml
Normal file
673
docs/schemas/excititor-chunk-api.openapi.yaml
Normal file
@@ -0,0 +1,673 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: StellaOps Excititor Chunk API
|
||||
version: 1.0.0
|
||||
description: |
|
||||
API for VEX document chunked ingestion and processing in Excititor service.
|
||||
Unblocks EXCITITOR-DOCS-0001, EXCITITOR-ENG-0001, EXCITITOR-OPS-0001 (3 tasks).
|
||||
contact:
|
||||
name: StellaOps Platform Team
|
||||
url: https://stella-ops.org
|
||||
license:
|
||||
name: AGPL-3.0-or-later
|
||||
url: https://www.gnu.org/licenses/agpl-3.0.html
|
||||
|
||||
servers:
|
||||
- url: /api/v1/excititor
|
||||
description: Excititor API base path
|
||||
|
||||
tags:
|
||||
- name: chunks
|
||||
description: Chunked document upload operations
|
||||
- name: vex
|
||||
description: VEX document ingestion
|
||||
- name: processing
|
||||
description: Document processing status
|
||||
- name: health
|
||||
description: Service health endpoints
|
||||
|
||||
paths:
|
||||
/chunks/initiate:
|
||||
post:
|
||||
operationId: initiateChunkedUpload
|
||||
summary: Initiate a chunked upload session
|
||||
description: Start a new chunked upload session for large VEX documents
|
||||
tags:
|
||||
- chunks
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- oauth2: [excititor:write]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChunkedUploadInitRequest'
|
||||
responses:
|
||||
'201':
|
||||
description: Upload session created
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChunkedUploadSession'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'429':
|
||||
$ref: '#/components/responses/TooManyRequests'
|
||||
|
||||
/chunks/{session_id}:
|
||||
put:
|
||||
operationId: uploadChunk
|
||||
summary: Upload a chunk
|
||||
description: Upload a single chunk for an active upload session
|
||||
tags:
|
||||
- chunks
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- oauth2: [excititor:write]
|
||||
parameters:
|
||||
- name: session_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- name: X-Chunk-Index
|
||||
in: header
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 0
|
||||
- name: X-Chunk-Digest
|
||||
in: header
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: '^sha256:[a-f0-9]{64}$'
|
||||
- name: Content-Range
|
||||
in: header
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/octet-stream:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
responses:
|
||||
'200':
|
||||
description: Chunk uploaded successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChunkUploadResult'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'409':
|
||||
description: Chunk already uploaded or out of sequence
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
|
||||
get:
|
||||
operationId: getUploadSessionStatus
|
||||
summary: Get upload session status
|
||||
description: Retrieve the current status of a chunked upload session
|
||||
tags:
|
||||
- chunks
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- oauth2: [excititor:read]
|
||||
parameters:
|
||||
- name: session_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
'200':
|
||||
description: Upload session status
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChunkedUploadSession'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
delete:
|
||||
operationId: cancelUploadSession
|
||||
summary: Cancel upload session
|
||||
description: Cancel an active upload session and clean up partial data
|
||||
tags:
|
||||
- chunks
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- oauth2: [excititor:write]
|
||||
parameters:
|
||||
- name: session_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
'204':
|
||||
description: Session cancelled
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/chunks/{session_id}/complete:
|
||||
post:
|
||||
operationId: completeChunkedUpload
|
||||
summary: Complete chunked upload
|
||||
description: Finalize a chunked upload and trigger VEX processing
|
||||
tags:
|
||||
- chunks
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- oauth2: [excititor:write]
|
||||
parameters:
|
||||
- name: session_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChunkedUploadCompleteRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Upload completed, processing started
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/VexIngestionJob'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'409':
|
||||
description: Missing chunks or invalid digest
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
|
||||
/vex/ingest:
|
||||
post:
|
||||
operationId: ingestVexDocument
|
||||
summary: Ingest a VEX document
|
||||
description: Ingest a small VEX document directly (for documents < 10MB)
|
||||
tags:
|
||||
- vex
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- oauth2: [excititor:write]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/VexIngestionRequest'
|
||||
application/vnd.openvex+json:
|
||||
schema:
|
||||
type: object
|
||||
application/vnd.csaf+json:
|
||||
schema:
|
||||
type: object
|
||||
application/vnd.cyclonedx+json:
|
||||
schema:
|
||||
type: object
|
||||
responses:
|
||||
'202':
|
||||
description: VEX document accepted for processing
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/VexIngestionJob'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'413':
|
||||
description: Payload too large - use chunked upload
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
|
||||
/vex/jobs/{job_id}:
|
||||
get:
|
||||
operationId: getIngestionJobStatus
|
||||
summary: Get ingestion job status
|
||||
description: Retrieve the status of a VEX ingestion job
|
||||
tags:
|
||||
- processing
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- oauth2: [excititor:read]
|
||||
parameters:
|
||||
- name: job_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
'200':
|
||||
description: Job status
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/VexIngestionJob'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/vex/jobs:
|
||||
get:
|
||||
operationId: listIngestionJobs
|
||||
summary: List ingestion jobs
|
||||
description: List VEX ingestion jobs with filtering and pagination
|
||||
tags:
|
||||
- processing
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- oauth2: [excititor:read]
|
||||
parameters:
|
||||
- name: status
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
enum: [pending, processing, completed, failed]
|
||||
- name: created_after
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
- name: created_before
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
- name: page
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 1
|
||||
default: 1
|
||||
- name: page_size
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 1
|
||||
maximum: 100
|
||||
default: 20
|
||||
responses:
|
||||
'200':
|
||||
description: List of jobs
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/VexIngestionJobList'
|
||||
|
||||
/health:
|
||||
get:
|
||||
operationId: healthCheck
|
||||
summary: Health check
|
||||
description: Service health check endpoint
|
||||
tags:
|
||||
- health
|
||||
security: []
|
||||
responses:
|
||||
'200':
|
||||
description: Service healthy
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/HealthStatus'
|
||||
'503':
|
||||
description: Service unhealthy
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/HealthStatus'
|
||||
|
||||
/health/ready:
|
||||
get:
|
||||
operationId: readinessCheck
|
||||
summary: Readiness check
|
||||
description: Kubernetes readiness probe endpoint
|
||||
tags:
|
||||
- health
|
||||
security: []
|
||||
responses:
|
||||
'200':
|
||||
description: Service ready
|
||||
'503':
|
||||
description: Service not ready
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
oauth2:
|
||||
type: oauth2
|
||||
flows:
|
||||
clientCredentials:
|
||||
tokenUrl: /oauth/token
|
||||
scopes:
|
||||
excititor:read: Read VEX data
|
||||
excititor:write: Write VEX data
|
||||
|
||||
schemas:
|
||||
ChunkedUploadInitRequest:
|
||||
type: object
|
||||
required:
|
||||
- filename
|
||||
- total_size
|
||||
- content_type
|
||||
properties:
|
||||
filename:
|
||||
type: string
|
||||
description: Original filename
|
||||
total_size:
|
||||
type: integer
|
||||
minimum: 1
|
||||
description: Total file size in bytes
|
||||
content_type:
|
||||
type: string
|
||||
enum:
|
||||
- application/json
|
||||
- application/vnd.openvex+json
|
||||
- application/vnd.csaf+json
|
||||
- application/vnd.cyclonedx+json
|
||||
expected_digest:
|
||||
type: string
|
||||
pattern: '^sha256:[a-f0-9]{64}$'
|
||||
description: Expected SHA-256 digest of complete file
|
||||
chunk_size:
|
||||
type: integer
|
||||
minimum: 1048576
|
||||
maximum: 104857600
|
||||
default: 10485760
|
||||
description: Chunk size in bytes (1MB - 100MB, default 10MB)
|
||||
metadata:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
description: Optional metadata for the upload
|
||||
|
||||
ChunkedUploadSession:
|
||||
type: object
|
||||
required:
|
||||
- session_id
|
||||
- status
|
||||
- created_at
|
||||
properties:
|
||||
session_id:
|
||||
type: string
|
||||
format: uuid
|
||||
status:
|
||||
type: string
|
||||
enum: [active, completed, cancelled, expired]
|
||||
filename:
|
||||
type: string
|
||||
total_size:
|
||||
type: integer
|
||||
chunk_size:
|
||||
type: integer
|
||||
total_chunks:
|
||||
type: integer
|
||||
uploaded_chunks:
|
||||
type: array
|
||||
items:
|
||||
type: integer
|
||||
chunks_remaining:
|
||||
type: integer
|
||||
bytes_uploaded:
|
||||
type: integer
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
expires_at:
|
||||
type: string
|
||||
format: date-time
|
||||
upload_url:
|
||||
type: string
|
||||
format: uri
|
||||
description: URL for chunk uploads
|
||||
|
||||
ChunkUploadResult:
|
||||
type: object
|
||||
required:
|
||||
- chunk_index
|
||||
- received
|
||||
properties:
|
||||
chunk_index:
|
||||
type: integer
|
||||
received:
|
||||
type: boolean
|
||||
digest_verified:
|
||||
type: boolean
|
||||
bytes_received:
|
||||
type: integer
|
||||
chunks_remaining:
|
||||
type: integer
|
||||
|
||||
ChunkedUploadCompleteRequest:
|
||||
type: object
|
||||
required:
|
||||
- final_digest
|
||||
properties:
|
||||
final_digest:
|
||||
type: string
|
||||
pattern: '^sha256:[a-f0-9]{64}$'
|
||||
description: SHA-256 digest of reassembled file
|
||||
process_immediately:
|
||||
type: boolean
|
||||
default: true
|
||||
description: Start processing immediately after assembly
|
||||
|
||||
VexIngestionRequest:
|
||||
type: object
|
||||
required:
|
||||
- document
|
||||
properties:
|
||||
document:
|
||||
type: object
|
||||
description: VEX document (OpenVEX, CSAF, or CycloneDX format)
|
||||
format:
|
||||
type: string
|
||||
enum: [openvex, csaf, cyclonedx, auto]
|
||||
default: auto
|
||||
source:
|
||||
type: string
|
||||
description: Source identifier for the VEX document
|
||||
priority:
|
||||
type: string
|
||||
enum: [low, normal, high]
|
||||
default: normal
|
||||
metadata:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
|
||||
VexIngestionJob:
|
||||
type: object
|
||||
required:
|
||||
- job_id
|
||||
- status
|
||||
- created_at
|
||||
properties:
|
||||
job_id:
|
||||
type: string
|
||||
format: uuid
|
||||
status:
|
||||
type: string
|
||||
enum: [pending, validating, processing, indexing, completed, failed]
|
||||
format_detected:
|
||||
type: string
|
||||
enum: [openvex, csaf, cyclonedx, unknown]
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
started_at:
|
||||
type: string
|
||||
format: date-time
|
||||
completed_at:
|
||||
type: string
|
||||
format: date-time
|
||||
document_digest:
|
||||
type: string
|
||||
pattern: '^sha256:[a-f0-9]{64}$'
|
||||
statements_count:
|
||||
type: integer
|
||||
description: Number of VEX statements processed
|
||||
products_count:
|
||||
type: integer
|
||||
description: Number of products affected
|
||||
vulnerabilities_count:
|
||||
type: integer
|
||||
description: Number of vulnerabilities referenced
|
||||
errors:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ProcessingError'
|
||||
warnings:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
result_ref:
|
||||
type: string
|
||||
description: Reference to processing result
|
||||
|
||||
VexIngestionJobList:
|
||||
type: object
|
||||
required:
|
||||
- jobs
|
||||
- total_count
|
||||
properties:
|
||||
jobs:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/VexIngestionJob'
|
||||
total_count:
|
||||
type: integer
|
||||
page:
|
||||
type: integer
|
||||
page_size:
|
||||
type: integer
|
||||
next_page_token:
|
||||
type: string
|
||||
|
||||
ProcessingError:
|
||||
type: object
|
||||
required:
|
||||
- code
|
||||
- message
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
location:
|
||||
type: string
|
||||
description: JSON path to error location
|
||||
details:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
|
||||
HealthStatus:
|
||||
type: object
|
||||
required:
|
||||
- status
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
enum: [healthy, degraded, unhealthy]
|
||||
version:
|
||||
type: string
|
||||
uptime_seconds:
|
||||
type: integer
|
||||
checks:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
enum: [pass, warn, fail]
|
||||
message:
|
||||
type: string
|
||||
|
||||
ProblemDetails:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- title
|
||||
- status
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
format: uri
|
||||
title:
|
||||
type: string
|
||||
status:
|
||||
type: integer
|
||||
detail:
|
||||
type: string
|
||||
instance:
|
||||
type: string
|
||||
format: uri
|
||||
errors:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
field:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
|
||||
responses:
|
||||
BadRequest:
|
||||
description: Bad request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
Unauthorized:
|
||||
description: Unauthorized
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
NotFound:
|
||||
description: Resource not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
TooManyRequests:
|
||||
description: Rate limit exceeded
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
headers:
|
||||
Retry-After:
|
||||
schema:
|
||||
type: integer
|
||||
description: Seconds until rate limit resets
|
||||
1029
docs/schemas/findings-ledger-api.openapi.yaml
Normal file
1029
docs/schemas/findings-ledger-api.openapi.yaml
Normal file
File diff suppressed because it is too large
Load Diff
681
docs/schemas/lnm-overlay.schema.json
Normal file
681
docs/schemas/lnm-overlay.schema.json
Normal file
@@ -0,0 +1,681 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://stella-ops.org/schemas/lnm-overlay.schema.json",
|
||||
"title": "StellaOps Link-Not-Merge Overlay Schema",
|
||||
"description": "Schema for Link-Not-Merge (LNM) overlay metadata and graph inspector integration. Unblocks EXCITITOR-GRAPH-21-001 through 21-005.",
|
||||
"type": "object",
|
||||
"definitions": {
|
||||
"LnmOverlay": {
|
||||
"type": "object",
|
||||
"description": "Link-Not-Merge overlay structure for VEX observation and linkset merge metadata",
|
||||
"required": ["overlay_id", "source_type", "timestamp"],
|
||||
"properties": {
|
||||
"overlay_id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Unique identifier for this overlay"
|
||||
},
|
||||
"source_type": {
|
||||
"type": "string",
|
||||
"enum": ["observation", "linkset", "advisory", "vex", "sbom"],
|
||||
"description": "Type of source contributing to this overlay"
|
||||
},
|
||||
"source_ref": {
|
||||
"$ref": "#/definitions/SourceRef"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "When this overlay was created"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Version of the overlay schema"
|
||||
},
|
||||
"links": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/OverlayLink"
|
||||
},
|
||||
"description": "Links to related entities"
|
||||
},
|
||||
"conflicts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ConflictMarker"
|
||||
},
|
||||
"description": "Conflict markers from merge operations"
|
||||
},
|
||||
"provenance": {
|
||||
"$ref": "#/definitions/OverlayProvenance"
|
||||
},
|
||||
"indexes": {
|
||||
"$ref": "#/definitions/OverlayIndexes"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SourceRef": {
|
||||
"type": "object",
|
||||
"description": "Reference to the source document/entity",
|
||||
"required": ["type", "identifier"],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["advisory", "vex", "sbom", "scan_result", "linkset", "observation"]
|
||||
},
|
||||
"identifier": {
|
||||
"type": "string",
|
||||
"description": "Unique identifier of the source (e.g., advisory ID, SBOM digest)"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$",
|
||||
"description": "Content-addressable digest of the source"
|
||||
},
|
||||
"uri": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"description": "URI to retrieve the source"
|
||||
},
|
||||
"fetched_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
}
|
||||
},
|
||||
"OverlayLink": {
|
||||
"type": "object",
|
||||
"description": "Link between entities in the overlay graph",
|
||||
"required": ["link_type", "source", "target"],
|
||||
"properties": {
|
||||
"link_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"link_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"affects",
|
||||
"mitigates",
|
||||
"remediates",
|
||||
"supersedes",
|
||||
"references",
|
||||
"contains",
|
||||
"depends_on",
|
||||
"exploits",
|
||||
"derived_from"
|
||||
],
|
||||
"description": "Semantic relationship type"
|
||||
},
|
||||
"source": {
|
||||
"$ref": "#/definitions/EntityRef"
|
||||
},
|
||||
"target": {
|
||||
"$ref": "#/definitions/EntityRef"
|
||||
},
|
||||
"confidence": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"description": "Confidence score for this link"
|
||||
},
|
||||
"evidence": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/LinkEvidence"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"EntityRef": {
|
||||
"type": "object",
|
||||
"description": "Reference to an entity in the graph",
|
||||
"required": ["entity_type", "identifier"],
|
||||
"properties": {
|
||||
"entity_type": {
|
||||
"type": "string",
|
||||
"enum": ["vulnerability", "component", "product", "advisory", "vex_statement", "sbom", "finding"]
|
||||
},
|
||||
"identifier": {
|
||||
"type": "string",
|
||||
"description": "Entity identifier (CVE ID, PURL, product ID, etc.)"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Version specifier if applicable"
|
||||
}
|
||||
}
|
||||
},
|
||||
"LinkEvidence": {
|
||||
"type": "object",
|
||||
"description": "Evidence supporting a link relationship",
|
||||
"required": ["type"],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["explicit", "inferred", "heuristic", "manual"]
|
||||
},
|
||||
"source_ref": {
|
||||
"$ref": "#/definitions/SourceRef"
|
||||
},
|
||||
"statement": {
|
||||
"type": "string",
|
||||
"description": "Evidence statement or justification"
|
||||
},
|
||||
"score": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"ConflictMarker": {
|
||||
"type": "object",
|
||||
"description": "Marker for merge conflicts between overlapping sources",
|
||||
"required": ["conflict_type", "entities", "resolution_status"],
|
||||
"properties": {
|
||||
"conflict_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"conflict_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"status_mismatch",
|
||||
"severity_mismatch",
|
||||
"version_range_overlap",
|
||||
"product_identity_conflict",
|
||||
"justification_conflict",
|
||||
"timestamp_ordering"
|
||||
],
|
||||
"description": "Type of conflict detected"
|
||||
},
|
||||
"entities": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ConflictingEntity"
|
||||
},
|
||||
"minItems": 2,
|
||||
"description": "Entities involved in the conflict"
|
||||
},
|
||||
"resolution_status": {
|
||||
"type": "string",
|
||||
"enum": ["unresolved", "auto_resolved", "manually_resolved", "deferred"],
|
||||
"description": "Current resolution status"
|
||||
},
|
||||
"resolution": {
|
||||
"$ref": "#/definitions/ConflictResolution"
|
||||
},
|
||||
"detected_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ConflictingEntity": {
|
||||
"type": "object",
|
||||
"description": "Entity involved in a conflict",
|
||||
"required": ["source_ref", "value"],
|
||||
"properties": {
|
||||
"source_ref": {
|
||||
"$ref": "#/definitions/SourceRef"
|
||||
},
|
||||
"value": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"description": "The conflicting value from this source"
|
||||
},
|
||||
"trust_level": {
|
||||
"type": "string",
|
||||
"enum": ["authoritative", "trusted", "community", "unknown"],
|
||||
"description": "Trust level of this source"
|
||||
},
|
||||
"precedence": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Precedence rank for resolution"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ConflictResolution": {
|
||||
"type": "object",
|
||||
"description": "Resolution decision for a conflict",
|
||||
"required": ["strategy", "resolved_at"],
|
||||
"properties": {
|
||||
"strategy": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"latest_wins",
|
||||
"highest_precedence",
|
||||
"most_specific",
|
||||
"manual_selection",
|
||||
"merge_composite"
|
||||
],
|
||||
"description": "Resolution strategy used"
|
||||
},
|
||||
"selected_source": {
|
||||
"$ref": "#/definitions/SourceRef"
|
||||
},
|
||||
"resolved_value": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"description": "The resolved value"
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Justification for the resolution"
|
||||
},
|
||||
"resolved_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"resolved_by": {
|
||||
"type": "string",
|
||||
"description": "User or system that resolved the conflict"
|
||||
}
|
||||
}
|
||||
},
|
||||
"OverlayProvenance": {
|
||||
"type": "object",
|
||||
"description": "Provenance information for the overlay",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"created_by": {
|
||||
"type": "string"
|
||||
},
|
||||
"pipeline_id": {
|
||||
"type": "string",
|
||||
"description": "ID of the ingestion pipeline that created this overlay"
|
||||
},
|
||||
"pipeline_version": {
|
||||
"type": "string"
|
||||
},
|
||||
"input_digests": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$"
|
||||
},
|
||||
"description": "Digests of all inputs used to create this overlay"
|
||||
},
|
||||
"attestation_ref": {
|
||||
"type": "string",
|
||||
"description": "Reference to DSSE attestation for this overlay"
|
||||
}
|
||||
}
|
||||
},
|
||||
"OverlayIndexes": {
|
||||
"type": "object",
|
||||
"description": "Index configuration for graph inspector queries",
|
||||
"properties": {
|
||||
"by_vulnerability": {
|
||||
"$ref": "#/definitions/IndexConfig"
|
||||
},
|
||||
"by_component": {
|
||||
"$ref": "#/definitions/IndexConfig"
|
||||
},
|
||||
"by_product": {
|
||||
"$ref": "#/definitions/IndexConfig"
|
||||
},
|
||||
"by_source": {
|
||||
"$ref": "#/definitions/IndexConfig"
|
||||
},
|
||||
"by_conflict_status": {
|
||||
"$ref": "#/definitions/IndexConfig"
|
||||
}
|
||||
}
|
||||
},
|
||||
"IndexConfig": {
|
||||
"type": "object",
|
||||
"description": "Configuration for a specific index",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"fields": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Fields to include in the index"
|
||||
},
|
||||
"materialized": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Whether to use a materialized view"
|
||||
},
|
||||
"refresh_interval_seconds": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Refresh interval for materialized views (0 = immediate)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"BatchVexFetchRequest": {
|
||||
"type": "object",
|
||||
"description": "Request for batched VEX document fetches",
|
||||
"required": ["product_ids"],
|
||||
"properties": {
|
||||
"product_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Product identifiers (PURLs, CPEs) to fetch VEX for"
|
||||
},
|
||||
"vulnerability_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Optional: filter to specific vulnerabilities"
|
||||
},
|
||||
"include_overlays": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Include overlay metadata in response"
|
||||
},
|
||||
"include_conflicts": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Include conflict markers in response"
|
||||
},
|
||||
"max_results": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 1000,
|
||||
"default": 100
|
||||
},
|
||||
"continuation_token": {
|
||||
"type": "string",
|
||||
"description": "Token for pagination"
|
||||
}
|
||||
}
|
||||
},
|
||||
"BatchVexFetchResponse": {
|
||||
"type": "object",
|
||||
"description": "Response from batched VEX document fetch",
|
||||
"required": ["results", "total_count"],
|
||||
"properties": {
|
||||
"results": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/VexOverlayResult"
|
||||
}
|
||||
},
|
||||
"total_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"continuation_token": {
|
||||
"type": "string"
|
||||
},
|
||||
"fetch_timestamp": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
}
|
||||
},
|
||||
"VexOverlayResult": {
|
||||
"type": "object",
|
||||
"description": "VEX result with overlay metadata",
|
||||
"required": ["product_id"],
|
||||
"properties": {
|
||||
"product_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"vex_statements": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/VexStatementSummary"
|
||||
}
|
||||
},
|
||||
"overlay": {
|
||||
"$ref": "#/definitions/LnmOverlay"
|
||||
},
|
||||
"conflicts_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"VexStatementSummary": {
|
||||
"type": "object",
|
||||
"description": "Summary of a VEX statement",
|
||||
"required": ["vulnerability_id", "status"],
|
||||
"properties": {
|
||||
"vulnerability_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["not_affected", "affected", "fixed", "under_investigation"]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string"
|
||||
},
|
||||
"source_ref": {
|
||||
"$ref": "#/definitions/SourceRef"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
}
|
||||
},
|
||||
"GraphInspectorQuery": {
|
||||
"type": "object",
|
||||
"description": "Query for the graph inspector UI",
|
||||
"required": ["query_type"],
|
||||
"properties": {
|
||||
"query_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"entity_neighbors",
|
||||
"path_between",
|
||||
"conflicts_for_entity",
|
||||
"overlay_history",
|
||||
"affected_products",
|
||||
"vulnerability_coverage"
|
||||
]
|
||||
},
|
||||
"entity_ref": {
|
||||
"$ref": "#/definitions/EntityRef"
|
||||
},
|
||||
"filters": {
|
||||
"$ref": "#/definitions/QueryFilters"
|
||||
},
|
||||
"depth": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 10,
|
||||
"default": 2,
|
||||
"description": "Graph traversal depth"
|
||||
},
|
||||
"include_metadata": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"QueryFilters": {
|
||||
"type": "object",
|
||||
"description": "Filters for graph queries",
|
||||
"properties": {
|
||||
"link_types": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Filter by link types"
|
||||
},
|
||||
"entity_types": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Filter by entity types"
|
||||
},
|
||||
"source_types": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Filter by source types"
|
||||
},
|
||||
"time_range": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"from": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"to": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
}
|
||||
},
|
||||
"min_confidence": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
},
|
||||
"include_conflicts": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"conflict_status": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["unresolved", "auto_resolved", "manually_resolved", "deferred"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"overlays": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/LnmOverlay"
|
||||
}
|
||||
}
|
||||
},
|
||||
"examples": [
|
||||
{
|
||||
"overlays": [
|
||||
{
|
||||
"overlay_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"source_type": "vex",
|
||||
"source_ref": {
|
||||
"type": "vex",
|
||||
"identifier": "CSAF-2025-0001",
|
||||
"digest": "sha256:abc123def456789...",
|
||||
"uri": "https://security.vendor.com/csaf/2025-0001.json"
|
||||
},
|
||||
"timestamp": "2025-12-06T10:00:00Z",
|
||||
"version": "1.0.0",
|
||||
"links": [
|
||||
{
|
||||
"link_id": "660e8400-e29b-41d4-a716-446655440001",
|
||||
"link_type": "affects",
|
||||
"source": {
|
||||
"entity_type": "vulnerability",
|
||||
"identifier": "CVE-2025-1234"
|
||||
},
|
||||
"target": {
|
||||
"entity_type": "component",
|
||||
"identifier": "pkg:npm/lodash@4.17.20"
|
||||
},
|
||||
"confidence": 0.95,
|
||||
"evidence": [
|
||||
{
|
||||
"type": "explicit",
|
||||
"statement": "Vendor advisory explicitly lists lodash@4.17.20 as affected"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"conflicts": [
|
||||
{
|
||||
"conflict_id": "770e8400-e29b-41d4-a716-446655440002",
|
||||
"conflict_type": "status_mismatch",
|
||||
"entities": [
|
||||
{
|
||||
"source_ref": {
|
||||
"type": "vex",
|
||||
"identifier": "CSAF-2025-0001"
|
||||
},
|
||||
"value": {
|
||||
"status": "affected"
|
||||
},
|
||||
"trust_level": "authoritative",
|
||||
"precedence": 1
|
||||
},
|
||||
{
|
||||
"source_ref": {
|
||||
"type": "vex",
|
||||
"identifier": "OPENVEX-COMM-2025-0001"
|
||||
},
|
||||
"value": {
|
||||
"status": "not_affected"
|
||||
},
|
||||
"trust_level": "community",
|
||||
"precedence": 3
|
||||
}
|
||||
],
|
||||
"resolution_status": "auto_resolved",
|
||||
"resolution": {
|
||||
"strategy": "highest_precedence",
|
||||
"selected_source": {
|
||||
"type": "vex",
|
||||
"identifier": "CSAF-2025-0001"
|
||||
},
|
||||
"resolved_value": {
|
||||
"status": "affected"
|
||||
},
|
||||
"justification": "Authoritative vendor source has highest precedence",
|
||||
"resolved_at": "2025-12-06T10:05:00Z",
|
||||
"resolved_by": "lnm-pipeline"
|
||||
},
|
||||
"detected_at": "2025-12-06T10:00:00Z"
|
||||
}
|
||||
],
|
||||
"provenance": {
|
||||
"created_at": "2025-12-06T10:00:00Z",
|
||||
"created_by": "lnm-pipeline",
|
||||
"pipeline_id": "lnm-ingestion-001",
|
||||
"pipeline_version": "2025.10.0",
|
||||
"input_digests": [
|
||||
"sha256:abc123...",
|
||||
"sha256:def456..."
|
||||
]
|
||||
},
|
||||
"indexes": {
|
||||
"by_vulnerability": {
|
||||
"enabled": true,
|
||||
"fields": ["vulnerability_id", "status", "timestamp"],
|
||||
"materialized": true,
|
||||
"refresh_interval_seconds": 60
|
||||
},
|
||||
"by_component": {
|
||||
"enabled": true,
|
||||
"fields": ["component_purl", "version_range"],
|
||||
"materialized": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
516
docs/schemas/orchestrator-envelope.schema.json
Normal file
516
docs/schemas/orchestrator-envelope.schema.json
Normal file
@@ -0,0 +1,516 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://stella-ops.org/schemas/orchestrator-envelope.schema.json",
|
||||
"title": "StellaOps Orchestrator Event Envelope Schema",
|
||||
"description": "Schema for orchestrator-compatible event envelopes used by Scanner and other services. Unblocks SCANNER-EVENTS-16-301.",
|
||||
"type": "object",
|
||||
"definitions": {
|
||||
"EventEnvelope": {
|
||||
"type": "object",
|
||||
"description": "Standard event envelope for orchestrator event bus",
|
||||
"required": ["envelope_id", "event_type", "timestamp", "source", "payload"],
|
||||
"properties": {
|
||||
"envelope_id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Unique identifier for this event envelope"
|
||||
},
|
||||
"event_type": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z]+\\.[a-z_]+\\.[a-z_]+$",
|
||||
"description": "Dot-notation event type (e.g., scanner.scan.completed)",
|
||||
"examples": [
|
||||
"scanner.scan.started",
|
||||
"scanner.scan.completed",
|
||||
"scanner.scan.failed",
|
||||
"scanner.sbom.generated",
|
||||
"scanner.vulnerability.detected",
|
||||
"notifier.alert.sent",
|
||||
"policy.evaluation.completed"
|
||||
]
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "ISO 8601 timestamp when event was created"
|
||||
},
|
||||
"source": {
|
||||
"$ref": "#/definitions/EventSource"
|
||||
},
|
||||
"correlation_id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Correlation ID for tracing related events"
|
||||
},
|
||||
"causation_id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "ID of the event that caused this event"
|
||||
},
|
||||
"tenant_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"project_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"payload": {
|
||||
"type": "object",
|
||||
"description": "Event-specific payload",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"metadata": {
|
||||
"$ref": "#/definitions/EventMetadata"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"default": "1.0",
|
||||
"description": "Event schema version"
|
||||
}
|
||||
}
|
||||
},
|
||||
"EventSource": {
|
||||
"type": "object",
|
||||
"description": "Source of the event",
|
||||
"required": ["service", "instance_id"],
|
||||
"properties": {
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "Service name (e.g., scanner, notifier, policy-engine)"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Service version"
|
||||
},
|
||||
"instance_id": {
|
||||
"type": "string",
|
||||
"description": "Instance identifier (hostname, pod name, etc.)"
|
||||
},
|
||||
"region": {
|
||||
"type": "string",
|
||||
"description": "Deployment region"
|
||||
}
|
||||
}
|
||||
},
|
||||
"EventMetadata": {
|
||||
"type": "object",
|
||||
"description": "Additional metadata for the event",
|
||||
"properties": {
|
||||
"trace_id": {
|
||||
"type": "string",
|
||||
"description": "OpenTelemetry trace ID"
|
||||
},
|
||||
"span_id": {
|
||||
"type": "string",
|
||||
"description": "OpenTelemetry span ID"
|
||||
},
|
||||
"priority": {
|
||||
"type": "string",
|
||||
"enum": ["low", "normal", "high", "critical"],
|
||||
"default": "normal"
|
||||
},
|
||||
"ttl_seconds": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Time-to-live for the event"
|
||||
},
|
||||
"retry_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"default": 0
|
||||
},
|
||||
"idempotency_key": {
|
||||
"type": "string",
|
||||
"description": "Key for idempotent processing"
|
||||
},
|
||||
"content_type": {
|
||||
"type": "string",
|
||||
"default": "application/json"
|
||||
},
|
||||
"compression": {
|
||||
"type": "string",
|
||||
"enum": ["none", "gzip", "lz4"],
|
||||
"default": "none"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ScannerEventPayload": {
|
||||
"type": "object",
|
||||
"description": "Base payload for scanner events",
|
||||
"properties": {
|
||||
"scan_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"job_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"target": {
|
||||
"$ref": "#/definitions/ScanTarget"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["started", "in_progress", "completed", "failed", "cancelled"]
|
||||
},
|
||||
"started_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"completed_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"duration_ms": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"results_summary": {
|
||||
"$ref": "#/definitions/ScanResultsSummary"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/ErrorInfo"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ScanTarget": {
|
||||
"type": "object",
|
||||
"description": "Target being scanned",
|
||||
"required": ["type", "identifier"],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["container_image", "repository", "filesystem", "sbom", "package"]
|
||||
},
|
||||
"identifier": {
|
||||
"type": "string",
|
||||
"description": "Target identifier (image name, repo URL, path)"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$"
|
||||
},
|
||||
"tag": {
|
||||
"type": "string"
|
||||
},
|
||||
"platform": {
|
||||
"type": "string",
|
||||
"description": "Platform (e.g., linux/amd64)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ScanResultsSummary": {
|
||||
"type": "object",
|
||||
"description": "Summary of scan results",
|
||||
"properties": {
|
||||
"total_vulnerabilities": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"by_severity": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"critical": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"high": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"medium": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"low": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"info": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"components_scanned": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"sbom_generated": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sbom_ref": {
|
||||
"type": "string",
|
||||
"description": "Reference to generated SBOM"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ErrorInfo": {
|
||||
"type": "object",
|
||||
"description": "Error information for failed events",
|
||||
"required": ["code", "message"],
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"stack_trace": {
|
||||
"type": "string"
|
||||
},
|
||||
"recoverable": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"VulnerabilityDetectedPayload": {
|
||||
"type": "object",
|
||||
"description": "Payload for vulnerability detection events",
|
||||
"required": ["scan_id", "vulnerability"],
|
||||
"properties": {
|
||||
"scan_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"vulnerability": {
|
||||
"$ref": "#/definitions/VulnerabilityInfo"
|
||||
},
|
||||
"affected_component": {
|
||||
"$ref": "#/definitions/ComponentInfo"
|
||||
},
|
||||
"reachability": {
|
||||
"type": "string",
|
||||
"enum": ["reachable", "unreachable", "potentially_reachable", "unknown"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"VulnerabilityInfo": {
|
||||
"type": "object",
|
||||
"required": ["id", "severity"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "CVE ID or vulnerability identifier"
|
||||
},
|
||||
"severity": {
|
||||
"type": "string",
|
||||
"enum": ["critical", "high", "medium", "low", "info"]
|
||||
},
|
||||
"cvss_score": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 10
|
||||
},
|
||||
"cvss_vector": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"references": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
}
|
||||
},
|
||||
"fix_available": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"fixed_version": {
|
||||
"type": "string"
|
||||
},
|
||||
"kev_listed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"epss_score": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"ComponentInfo": {
|
||||
"type": "object",
|
||||
"required": ["purl"],
|
||||
"properties": {
|
||||
"purl": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"ecosystem": {
|
||||
"type": "string"
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "Location in the target (e.g., layer, file path)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"NotifierIngestionEvent": {
|
||||
"type": "object",
|
||||
"description": "Event structure for Notifier ingestion",
|
||||
"required": ["envelope_id", "event_type", "severity_threshold_met"],
|
||||
"properties": {
|
||||
"envelope_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"event_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"severity_threshold_met": {
|
||||
"type": "boolean",
|
||||
"description": "Whether event meets notification severity threshold"
|
||||
},
|
||||
"notification_channels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["email", "slack", "teams", "webhook", "pagerduty"]
|
||||
}
|
||||
},
|
||||
"digest_eligible": {
|
||||
"type": "boolean",
|
||||
"description": "Whether event should be batched into digest"
|
||||
},
|
||||
"immediate_dispatch": {
|
||||
"type": "boolean",
|
||||
"description": "Whether event requires immediate dispatch"
|
||||
}
|
||||
}
|
||||
},
|
||||
"EventBatch": {
|
||||
"type": "object",
|
||||
"description": "Batch of events for bulk processing",
|
||||
"required": ["batch_id", "events"],
|
||||
"properties": {
|
||||
"batch_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"events": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/EventEnvelope"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"total_count": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"EventSubscription": {
|
||||
"type": "object",
|
||||
"description": "Subscription to event types",
|
||||
"required": ["subscription_id", "event_patterns", "endpoint"],
|
||||
"properties": {
|
||||
"subscription_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"event_patterns": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "Glob pattern for event types (e.g., scanner.* or scanner.scan.completed)"
|
||||
}
|
||||
},
|
||||
"endpoint": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"description": "Webhook endpoint for event delivery"
|
||||
},
|
||||
"filters": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"description": "Additional filters on payload fields"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"events": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/EventEnvelope"
|
||||
}
|
||||
}
|
||||
},
|
||||
"examples": [
|
||||
{
|
||||
"events": [
|
||||
{
|
||||
"envelope_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"event_type": "scanner.scan.completed",
|
||||
"timestamp": "2025-12-06T10:00:00Z",
|
||||
"source": {
|
||||
"service": "scanner",
|
||||
"version": "2025.10.0",
|
||||
"instance_id": "scanner-pod-abc123"
|
||||
},
|
||||
"correlation_id": "660e8400-e29b-41d4-a716-446655440001",
|
||||
"tenant_id": "770e8400-e29b-41d4-a716-446655440002",
|
||||
"project_id": "880e8400-e29b-41d4-a716-446655440003",
|
||||
"payload": {
|
||||
"scan_id": "990e8400-e29b-41d4-a716-446655440004",
|
||||
"job_id": "aa0e8400-e29b-41d4-a716-446655440005",
|
||||
"target": {
|
||||
"type": "container_image",
|
||||
"identifier": "myregistry.io/app:v1.0.0",
|
||||
"digest": "sha256:abc123def456..."
|
||||
},
|
||||
"status": "completed",
|
||||
"started_at": "2025-12-06T09:55:00Z",
|
||||
"completed_at": "2025-12-06T10:00:00Z",
|
||||
"duration_ms": 300000,
|
||||
"results_summary": {
|
||||
"total_vulnerabilities": 15,
|
||||
"by_severity": {
|
||||
"critical": 1,
|
||||
"high": 3,
|
||||
"medium": 7,
|
||||
"low": 4,
|
||||
"info": 0
|
||||
},
|
||||
"components_scanned": 127,
|
||||
"sbom_generated": true,
|
||||
"sbom_ref": "s3://sboms/990e8400.../sbom.json"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"trace_id": "abc123trace",
|
||||
"span_id": "def456span",
|
||||
"priority": "normal"
|
||||
},
|
||||
"version": "1.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user