openapi: 3.1.0 info: title: StellaOps Delta Compare API description: | REST API for comparing scan snapshots, baseline selection, actionable recommendations, counterfactual analysis, and evidence/proof bundles. Sprint: SPRINT_4200_0002_0006 version: 1.0.0 license: name: AGPL-3.0-or-later url: https://www.gnu.org/licenses/agpl-3.0.html servers: - url: /v1 description: API v1 security: - bearerAuth: [] tags: - name: Delta Compare description: Compare scan snapshots - name: Baselines description: Baseline selection and rationale - name: Actionables description: Actionable remediation recommendations - name: Counterfactuals description: Counterfactual policy analysis - name: Evidence description: Evidence and proof bundles for comparisons paths: # Delta Compare Endpoints /delta-compare/compute: post: operationId: computeDeltaCompare summary: Compute a delta comparison between two snapshots tags: - Delta Compare requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DeltaCompareRequest' responses: '200': description: Delta comparison result content: application/json: schema: $ref: '#/components/schemas/DeltaCompareResult' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /delta-compare/{comparisonId}: get: operationId: getDeltaComparison summary: Get a previously computed delta comparison tags: - Delta Compare parameters: - $ref: '#/components/parameters/comparisonId' responses: '200': description: Delta comparison result content: application/json: schema: $ref: '#/components/schemas/DeltaCompareResult' '404': $ref: '#/components/responses/NotFound' /delta-compare/{comparisonId}/summary: get: operationId: getDeltaSummary summary: Get summary statistics for a comparison tags: - Delta Compare parameters: - $ref: '#/components/parameters/comparisonId' responses: '200': description: Delta summary content: application/json: schema: $ref: '#/components/schemas/DeltaSummary' '404': $ref: '#/components/responses/NotFound' /delta-compare/{comparisonId}/can-ship: get: operationId: checkCanShip summary: Check if target can ship relative to base tags: - Delta Compare parameters: - $ref: '#/components/parameters/comparisonId' responses: '200': description: Can-ship assessment content: application/json: schema: $ref: '#/components/schemas/CanShipResponse' '404': $ref: '#/components/responses/NotFound' /delta-compare/{comparisonId}/verdict: get: operationId: getDeltaVerdict summary: Get policy verdict for the comparison tags: - Delta Compare parameters: - $ref: '#/components/parameters/comparisonId' responses: '200': description: Delta verdict content: application/json: schema: $ref: '#/components/schemas/DeltaVerdictResponse' '404': $ref: '#/components/responses/NotFound' # Baseline Endpoints /baselines/recommendations/{artifactDigest}: get: operationId: getBaselineRecommendations summary: Get baseline recommendations for an artifact tags: - Baselines parameters: - name: artifactDigest in: path required: true schema: type: string description: Artifact digest - name: limit in: query schema: type: integer default: 10 maximum: 100 responses: '200': description: Baseline recommendations content: application/json: schema: $ref: '#/components/schemas/BaselineRecommendationsResponse' '404': $ref: '#/components/responses/NotFound' /baselines/rationale/{baseDigest}/{headDigest}: get: operationId: getBaselineRationale summary: Get rationale for baseline selection tags: - Baselines parameters: - name: baseDigest in: path required: true schema: type: string - name: headDigest in: path required: true schema: type: string responses: '200': description: Baseline rationale content: application/json: schema: $ref: '#/components/schemas/BaselineRationaleResponse' '404': $ref: '#/components/responses/NotFound' # Actionables Endpoints /actionables/delta/{deltaId}: get: operationId: getActionablesForDelta summary: Get actionable recommendations for a delta comparison tags: - Actionables parameters: - name: deltaId in: path required: true schema: type: string - name: limit in: query schema: type: integer default: 50 maximum: 500 - name: offset in: query schema: type: integer default: 0 responses: '200': description: Actionables list content: application/json: schema: $ref: '#/components/schemas/ActionablesResponse' '404': $ref: '#/components/responses/NotFound' /actionables/by-priority/{priority}: get: operationId: getActionablesByPriority summary: Get actionables filtered by priority tags: - Actionables parameters: - name: priority in: path required: true schema: type: string enum: [critical, high, medium, low] - name: deltaId in: query schema: type: string description: Optional delta ID to filter by - name: limit in: query schema: type: integer default: 50 responses: '200': description: Actionables list content: application/json: schema: $ref: '#/components/schemas/ActionablesResponse' /actionables/by-type/{actionType}: get: operationId: getActionablesByType summary: Get actionables filtered by action type tags: - Actionables parameters: - name: actionType in: path required: true schema: type: string enum: [upgrade, patch, replace, configure, accept_risk, investigate] - name: limit in: query schema: type: integer default: 50 responses: '200': description: Actionables list content: application/json: schema: $ref: '#/components/schemas/ActionablesResponse' # Counterfactual Endpoints /counterfactuals/compute: post: operationId: computeCounterfactual summary: Compute counterfactual policy analysis tags: - Counterfactuals requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CounterfactualRequest' responses: '200': description: Counterfactual result content: application/json: schema: $ref: '#/components/schemas/CounterfactualResponse' '400': $ref: '#/components/responses/BadRequest' /counterfactuals/finding/{findingId}: get: operationId: getCounterfactualForFinding summary: Get counterfactual analysis for a specific finding tags: - Counterfactuals parameters: - name: findingId in: path required: true schema: type: string responses: '200': description: Counterfactual for finding content: application/json: schema: $ref: '#/components/schemas/FindingCounterfactualResponse' '404': $ref: '#/components/responses/NotFound' /counterfactuals/scan/{scanId}/summary: get: operationId: getScanCounterfactualSummary summary: Get counterfactual summary for entire scan tags: - Counterfactuals parameters: - name: scanId in: path required: true schema: type: string responses: '200': description: Scan counterfactual summary content: application/json: schema: $ref: '#/components/schemas/ScanCounterfactualSummary' '404': $ref: '#/components/responses/NotFound' # Evidence Endpoints /delta-evidence/{comparisonId}: get: operationId: getDeltaEvidence summary: Get evidence bundle for a delta comparison tags: - Evidence parameters: - $ref: '#/components/parameters/comparisonId' responses: '200': description: Delta evidence bundle content: application/json: schema: $ref: '#/components/schemas/DeltaEvidenceResponse' '404': $ref: '#/components/responses/NotFound' /delta-evidence/finding/{findingId}: get: operationId: getFindingEvidence summary: Get evidence for a specific finding tags: - Evidence parameters: - name: findingId in: path required: true schema: type: string responses: '200': description: Finding evidence content: application/json: schema: $ref: '#/components/schemas/FindingEvidenceResponse' '404': $ref: '#/components/responses/NotFound' /delta-evidence/{comparisonId}/proof-bundle: get: operationId: getDeltaProofBundle summary: Download proof bundle as tar.gz tags: - Evidence parameters: - $ref: '#/components/parameters/comparisonId' responses: '200': description: Proof bundle file content: application/gzip: schema: type: string format: binary '404': $ref: '#/components/responses/NotFound' /delta-evidence/{comparisonId}/attestations: get: operationId: getDeltaAttestations summary: Get attestation chain for comparison tags: - Evidence parameters: - $ref: '#/components/parameters/comparisonId' responses: '200': description: Attestation chain content: application/json: schema: $ref: '#/components/schemas/AttestationChainResponse' '404': $ref: '#/components/responses/NotFound' components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT parameters: comparisonId: name: comparisonId in: path required: true schema: type: string description: Delta comparison identifier responses: BadRequest: description: Bad request content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' Unauthorized: description: Unauthorized NotFound: description: Resource not found content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' schemas: # Delta Compare Schemas DeltaCompareRequest: type: object required: - baseDigest - targetDigest properties: baseDigest: type: string description: Base snapshot digest (the 'before' state) targetDigest: type: string description: Target snapshot digest (the 'after' state) includeUnchanged: type: boolean default: false description: Include findings that are unchanged severityFilter: type: string enum: [critical, high, medium, low] description: Filter by minimum severity DeltaCompareResult: type: object required: - comparisonId - baseDigest - targetDigest - riskDirection - summary - changes properties: comparisonId: type: string baseDigest: type: string targetDigest: type: string riskDirection: type: string enum: [improved, degraded, unchanged] verdictChanged: type: boolean baseVerdict: type: string targetVerdict: type: string summary: $ref: '#/components/schemas/DeltaSummary' changes: type: array items: $ref: '#/components/schemas/FindingChange' computedAt: type: string format: date-time DeltaSummary: type: object required: - canShip - riskDirection - summary properties: canShip: type: boolean riskDirection: type: string enum: [improved, degraded, unchanged] netBlockingChange: type: integer added: type: integer removed: type: integer modified: type: integer unchanged: type: integer criticalAdded: type: integer criticalRemoved: type: integer highAdded: type: integer highRemoved: type: integer mediumAdded: type: integer mediumRemoved: type: integer lowAdded: type: integer lowRemoved: type: integer summary: type: string FindingChange: type: object required: - findingId - changeType - severity properties: findingId: type: string vulnId: type: string purl: type: string changeType: type: string enum: [added, removed, modified, unchanged] severity: type: string enum: [critical, high, medium, low, info] previousSeverity: type: string enum: [critical, high, medium, low, info] isBlocking: type: boolean wasBlocking: type: boolean CanShipResponse: type: object required: - comparisonId - canShip - reason properties: comparisonId: type: string canShip: type: boolean reason: type: string blockingFindings: type: integer newBlockingFindings: type: integer DeltaVerdictResponse: type: object required: - comparisonId - baseVerdict - targetVerdict properties: comparisonId: type: string baseVerdict: type: string targetVerdict: type: string verdictChanged: type: boolean direction: type: string enum: [improved, degraded, unchanged] policyViolations: type: array items: type: string # Baseline Schemas BaselineRecommendationsResponse: type: object required: - artifactDigest - recommendations properties: artifactDigest: type: string recommendations: type: array items: $ref: '#/components/schemas/BaselineRecommendation' totalCount: type: integer BaselineRecommendation: type: object required: - digest - score - reason properties: digest: type: string score: type: number format: double reason: type: string scanDate: type: string format: date-time findingCount: type: integer criticalCount: type: integer highCount: type: integer BaselineRationaleResponse: type: object required: - baseDigest - headDigest - rationale properties: baseDigest: type: string headDigest: type: string rationale: type: string score: type: number format: double factors: type: array items: $ref: '#/components/schemas/RationaleFactor' RationaleFactor: type: object required: - name - weight - value properties: name: type: string weight: type: number format: double value: type: number format: double description: type: string # Actionables Schemas ActionablesResponse: type: object required: - actionables - totalCount properties: actionables: type: array items: $ref: '#/components/schemas/Actionable' totalCount: type: integer criticalCount: type: integer highCount: type: integer Actionable: type: object required: - id - priority - actionType - summary properties: id: type: string findingId: type: string vulnId: type: string purl: type: string priority: type: string enum: [critical, high, medium, low] actionType: type: string enum: [upgrade, patch, replace, configure, accept_risk, investigate] summary: type: string details: type: string targetVersion: type: string description: Recommended version to upgrade to effort: type: string enum: [trivial, low, medium, high, complex] confidence: type: number format: double minimum: 0 maximum: 1 # Counterfactual Schemas CounterfactualRequest: type: object required: - scanId - scenarios properties: scanId: type: string findingId: type: string description: Optional - analyze specific finding only scenarios: type: array items: $ref: '#/components/schemas/CounterfactualScenario' minItems: 1 maxItems: 10 CounterfactualScenario: type: object required: - id - changes properties: id: type: string description: type: string changes: type: array items: $ref: '#/components/schemas/PolicyChange' PolicyChange: type: object required: - type properties: type: type: string enum: [add_vex, remove_vex, change_severity_threshold, add_exception, remove_exception] vulnId: type: string purl: type: string vexStatement: type: string enum: [not_affected, affected, fixed, under_investigation] justification: type: string threshold: type: string enum: [critical, high, medium, low] CounterfactualResponse: type: object required: - scanId - scenarios properties: scanId: type: string findingId: type: string scenarios: type: array items: $ref: '#/components/schemas/CounterfactualResult' CounterfactualResult: type: object required: - scenarioId - originalVerdict - newVerdict - impactSummary properties: scenarioId: type: string originalVerdict: type: string newVerdict: type: string verdictChanged: type: boolean impactSummary: type: string findingsAffected: type: integer blockingFindingsRemoved: type: integer blockingFindingsAdded: type: integer FindingCounterfactualResponse: type: object required: - findingId - currentStatus - scenarios properties: findingId: type: string vulnId: type: string purl: type: string currentStatus: type: string isBlocking: type: boolean scenarios: type: array items: $ref: '#/components/schemas/FindingScenarioResult' FindingScenarioResult: type: object required: - scenario - wouldBeBlocking - impactDescription properties: scenario: type: string wouldBeBlocking: type: boolean newStatus: type: string impactDescription: type: string ScanCounterfactualSummary: type: object required: - scanId - totalFindings - blockingFindings properties: scanId: type: string totalFindings: type: integer blockingFindings: type: integer wouldPassWithVex: type: integer description: Findings that would pass if VEX statements were added wouldPassWithException: type: integer description: Findings that would pass if exceptions were added topRecommendations: type: array items: type: string # Evidence Schemas DeltaEvidenceResponse: type: object required: - comparisonId - baseEvidence - targetEvidence properties: comparisonId: type: string baseEvidence: $ref: '#/components/schemas/SnapshotEvidence' targetEvidence: $ref: '#/components/schemas/SnapshotEvidence' deltaHash: type: string SnapshotEvidence: type: object required: - digest properties: digest: type: string sbomHash: type: string vulnHash: type: string policyHash: type: string scanDate: type: string format: date-time FindingEvidenceResponse: type: object required: - findingId properties: findingId: type: string vulnId: type: string purl: type: string reachability: $ref: '#/components/schemas/EvidenceSection' vex: $ref: '#/components/schemas/EvidenceSection' advisory: $ref: '#/components/schemas/EvidenceSection' EvidenceSection: type: object properties: data: type: object hash: type: string source: type: string AttestationChainResponse: type: object required: - comparisonId - attestations properties: comparisonId: type: string attestations: type: array items: $ref: '#/components/schemas/Attestation' chainValid: type: boolean rootAttestation: type: string Attestation: type: object required: - id - type - subject - createdAt properties: id: type: string type: type: string subject: type: string predicateType: type: string createdAt: type: string format: date-time signature: type: string isValid: type: boolean ProblemDetails: type: object properties: type: type: string title: type: string status: type: integer detail: type: string instance: type: string