feat: Add operations runbooks and UI API models for Sprint 3500.0004.x

Operations documentation:
- docs/operations/reachability-runbook.md - Reachability troubleshooting guide
- docs/operations/unknowns-queue-runbook.md - Unknowns queue management guide

UI TypeScript models:
- src/Web/StellaOps.Web/src/app/core/api/proof.models.ts - Proof ledger types
- src/Web/StellaOps.Web/src/app/core/api/reachability.models.ts - Reachability types
- src/Web/StellaOps.Web/src/app/core/api/unknowns.models.ts - Unknowns queue types

Sprint: SPRINT_3500_0004_0002 (UI), SPRINT_3500_0004_0004 (Docs)
This commit is contained in:
StellaOps Bot
2025-12-20 22:22:09 +02:00
parent efe9bd8cfe
commit da315965ff
5 changed files with 1719 additions and 0 deletions

View File

@@ -0,0 +1,180 @@
/**
* Proof and manifest models for Sprint 3500.0004.0002 - T6.
* Supports proof ledger visualization and score replay operations.
*/
// ============================================================================
// Manifest Models
// ============================================================================
/**
* Hash entry in the scan manifest.
*/
export interface ManifestHashEntry {
readonly label: string;
readonly algorithm: 'sha256' | 'sha384' | 'sha512';
readonly value: string;
readonly source: 'sbom' | 'layer' | 'config' | 'composition';
}
/**
* Scan manifest with all input hashes for proof computation.
*/
export interface ScanManifest {
readonly manifestId: string;
readonly scanId: string;
readonly imageDigest: string;
readonly createdAt: string;
readonly hashes: readonly ManifestHashEntry[];
readonly merkleRoot: string;
readonly compositionManifestUri?: string;
}
// ============================================================================
// Merkle Tree Models
// ============================================================================
/**
* Node in a Merkle tree visualization.
*/
export interface MerkleTreeNode {
readonly nodeId: string;
readonly hash: string;
readonly label?: string;
readonly isLeaf: boolean;
readonly isRoot: boolean;
readonly children?: readonly MerkleTreeNode[];
readonly level: number;
readonly position: number;
}
/**
* Complete Merkle tree structure for visualization.
*/
export interface MerkleTree {
readonly root: MerkleTreeNode;
readonly leafCount: number;
readonly depth: number;
}
// ============================================================================
// Proof Bundle Models
// ============================================================================
export type ProofVerificationStatus = 'verified' | 'failed' | 'pending' | 'unknown';
export type SignatureStatus = 'valid' | 'invalid' | 'expired' | 'unknown';
/**
* DSSE signature information.
*/
export interface DsseSignature {
readonly keyId: string;
readonly algorithm: string;
readonly status: SignatureStatus;
readonly signedAt?: string;
readonly expiresAt?: string;
readonly issuer?: string;
}
/**
* Rekor transparency log entry.
*/
export interface RekorLogEntry {
readonly logId: string;
readonly logIndex: number;
readonly integratedTime: string;
readonly logUrl: string;
readonly bodyHash: string;
}
/**
* Proof bundle containing all verification artifacts.
*/
export interface ProofBundle {
readonly bundleId: string;
readonly scanId: string;
readonly createdAt: string;
readonly merkleRoot: string;
readonly dsseEnvelope: string; // Base64-encoded DSSE envelope
readonly signatures: readonly DsseSignature[];
readonly rekorEntry?: RekorLogEntry;
readonly verificationStatus: ProofVerificationStatus;
readonly downloadUrl: string;
}
/**
* Proof verification result from the backend.
*/
export interface ProofVerificationResult {
readonly bundleId: string;
readonly verified: boolean;
readonly merkleRootValid: boolean;
readonly signatureValid: boolean;
readonly rekorInclusionValid?: boolean;
readonly verifiedAt: string;
readonly errors?: readonly string[];
readonly warnings?: readonly string[];
}
// ============================================================================
// Score Replay Models
// ============================================================================
export type ScoreReplayStatus = 'pending' | 'running' | 'completed' | 'failed';
/**
* Score component with breakdown.
*/
export interface ScoreComponent {
readonly name: string;
readonly weight: number;
readonly rawScore: number;
readonly weightedScore: number;
readonly details?: string;
}
/**
* Complete score breakdown.
*/
export interface ScoreBreakdown {
readonly totalScore: number;
readonly components: readonly ScoreComponent[];
readonly computedAt: string;
}
/**
* Score comparison result showing drift between original and replayed.
*/
export interface ScoreDrift {
readonly componentName: string;
readonly originalScore: number;
readonly replayedScore: number;
readonly delta: number;
readonly driftPercent: number;
readonly significant: boolean;
}
/**
* Score replay request.
*/
export interface ScoreReplayRequest {
readonly scanId: string;
readonly useCurrentPolicy?: boolean;
}
/**
* Score replay response with comparison.
*/
export interface ScoreReplayResult {
readonly replayId: string;
readonly scanId: string;
readonly status: ScoreReplayStatus;
readonly startedAt: string;
readonly completedAt?: string;
readonly originalScore: ScoreBreakdown;
readonly replayedScore?: ScoreBreakdown;
readonly drifts?: readonly ScoreDrift[];
readonly hasDrift: boolean;
readonly proofBundle?: ProofBundle;
readonly error?: string;
}

View File

@@ -0,0 +1,192 @@
/**
* Reachability analysis models for Sprint 3500.0004.0002 - T6.
* Supports the reachability explain widget with call graph visualization.
*/
// ============================================================================
// Call Graph Models
// ============================================================================
/**
* Type of node in the call graph.
*/
export type CallGraphNodeType =
| 'entrypoint'
| 'method'
| 'function'
| 'class'
| 'module'
| 'vulnerable'
| 'external';
/**
* Node in the call graph.
*/
export interface CallGraphNode {
readonly nodeId: string;
readonly type: CallGraphNodeType;
readonly name: string;
readonly qualifiedName: string;
readonly filePath?: string;
readonly lineNumber?: number;
readonly package?: string;
readonly isVulnerable: boolean;
readonly isEntrypoint: boolean;
readonly metadata?: Record<string, string>;
}
/**
* Edge in the call graph representing a call relationship.
*/
export interface CallGraphEdge {
readonly edgeId: string;
readonly sourceId: string;
readonly targetId: string;
readonly callType: 'direct' | 'indirect' | 'dynamic' | 'virtual';
readonly weight?: number;
readonly confidence?: number;
}
/**
* Complete call graph structure.
*/
export interface CallGraph {
readonly graphId: string;
readonly language: string;
readonly nodes: readonly CallGraphNode[];
readonly edges: readonly CallGraphEdge[];
readonly nodeCount: number;
readonly edgeCount: number;
readonly createdAt: string;
readonly digest: string;
}
// ============================================================================
// Reachability Path Models
// ============================================================================
/**
* Step in a reachability path.
*/
export interface ReachabilityPathStep {
readonly stepIndex: number;
readonly node: CallGraphNode;
readonly callType?: 'direct' | 'indirect' | 'dynamic' | 'virtual';
readonly confidence: number;
}
/**
* A reachability path from entrypoint to vulnerable function.
*/
export interface ReachabilityPath {
readonly pathId: string;
readonly entrypoint: CallGraphNode;
readonly vulnerable: CallGraphNode;
readonly steps: readonly ReachabilityPathStep[];
readonly pathLength: number;
readonly overallConfidence: number;
readonly isShortestPath: boolean;
}
// ============================================================================
// Confidence Scoring Models
// ============================================================================
/**
* Factor contributing to reachability confidence.
*/
export interface ConfidenceFactor {
readonly factorName: string;
readonly weight: number;
readonly score: number;
readonly contribution: number;
readonly description: string;
}
/**
* Complete confidence breakdown.
*/
export interface ConfidenceBreakdown {
readonly overallScore: number;
readonly factors: readonly ConfidenceFactor[];
readonly computedAt: string;
}
// ============================================================================
// Reachability Explanation Models
// ============================================================================
export type ReachabilityVerdict = 'reachable' | 'unreachable' | 'uncertain' | 'no_data';
/**
* Reachability explanation for a CVE.
*/
export interface ReachabilityExplanation {
readonly explanationId: string;
readonly cveId: string;
readonly vulnerablePackage: string;
readonly vulnerableFunction?: string;
readonly verdict: ReachabilityVerdict;
readonly confidence: ConfidenceBreakdown;
readonly paths: readonly ReachabilityPath[];
readonly callGraph?: CallGraph;
readonly entrypointsAnalyzed: number;
readonly shortestPathLength?: number;
readonly analysisTime: string;
readonly createdAt: string;
}
/**
* Request for reachability analysis.
*/
export interface ReachabilityAnalysisRequest {
readonly scanId: string;
readonly cveId: string;
readonly includeCallGraph?: boolean;
readonly maxPaths?: number;
readonly maxDepth?: number;
}
/**
* Summary of reachability analysis for a scan.
*/
export interface ReachabilitySummary {
readonly scanId: string;
readonly totalCves: number;
readonly reachableCves: number;
readonly unreachableCves: number;
readonly uncertainCves: number;
readonly noDataCves: number;
readonly analysisCompletedAt?: string;
readonly callGraphAvailable: boolean;
}
// ============================================================================
// Visualization Export Models
// ============================================================================
/**
* Export format for call graph visualization.
*/
export type ExportFormat = 'png' | 'svg' | 'json' | 'dot';
/**
* Request to export call graph visualization.
*/
export interface ExportGraphRequest {
readonly explanationId: string;
readonly format: ExportFormat;
readonly highlightPath?: string;
readonly width?: number;
readonly height?: number;
}
/**
* Export result.
*/
export interface ExportGraphResult {
readonly format: ExportFormat;
readonly dataUrl?: string; // For PNG/SVG
readonly data?: string; // For JSON/DOT
readonly filename: string;
}

View File

@@ -0,0 +1,163 @@
/**
* Unknowns registry models for Sprint 3500.0004.0002 - T6.
* Supports the unknowns queue component with band-based prioritization.
*/
// ============================================================================
// Band Classification
// ============================================================================
/**
* Band classification for unknown packages.
* HOT: Recently discovered, high occurrence, needs immediate attention
* WARM: Moderate priority, stable occurrence
* COLD: Low priority, rare or old unknowns
*/
export type UnknownBand = 'HOT' | 'WARM' | 'COLD';
/**
* Status of an unknown package in the registry.
*/
export type UnknownStatus = 'pending' | 'escalated' | 'resolved' | 'ignored';
/**
* Resolution action taken for an unknown.
*/
export type ResolutionAction =
| 'mapped_to_cve'
| 'marked_not_vulnerable'
| 'added_to_allowlist'
| 'false_positive'
| 'vendor_confirmed'
| 'other';
// ============================================================================
// Unknown Entry Models
// ============================================================================
/**
* Package information for an unknown.
*/
export interface UnknownPackage {
readonly name: string;
readonly version: string;
readonly ecosystem: string; // npm, pypi, maven, nuget, etc.
readonly purl?: string;
}
/**
* Scan occurrence information.
*/
export interface UnknownOccurrence {
readonly scanId: string;
readonly imageDigest: string;
readonly imageName: string;
readonly detectedAt: string;
}
/**
* An unknown entry in the registry.
*/
export interface UnknownEntry {
readonly unknownId: string;
readonly package: UnknownPackage;
readonly band: UnknownBand;
readonly status: UnknownStatus;
readonly rank: number; // Priority rank within band
readonly occurrenceCount: number;
readonly firstSeenAt: string;
readonly lastSeenAt: string;
readonly ageInDays: number;
readonly relatedCves?: readonly string[];
readonly assignee?: string;
readonly notes?: string;
readonly recentOccurrences: readonly UnknownOccurrence[];
}
// ============================================================================
// API Request/Response Models
// ============================================================================
/**
* Filter options for listing unknowns.
*/
export interface UnknownsFilter {
readonly band?: UnknownBand;
readonly status?: UnknownStatus;
readonly ecosystem?: string;
readonly scanId?: string;
readonly imageDigest?: string;
readonly assignee?: string;
readonly limit?: number;
readonly offset?: number;
readonly sortBy?: 'rank' | 'age' | 'occurrenceCount' | 'lastSeen';
readonly sortOrder?: 'asc' | 'desc';
}
/**
* Paginated response for unknowns listing.
*/
export interface UnknownsListResponse {
readonly items: readonly UnknownEntry[];
readonly total: number;
readonly limit: number;
readonly offset: number;
readonly hasMore: boolean;
}
/**
* Summary statistics for unknowns.
*/
export interface UnknownsSummary {
readonly hotCount: number;
readonly warmCount: number;
readonly coldCount: number;
readonly totalCount: number;
readonly pendingCount: number;
readonly escalatedCount: number;
readonly resolvedToday: number;
readonly oldestUnresolvedDays: number;
}
/**
* Request to escalate an unknown.
*/
export interface EscalateUnknownRequest {
readonly unknownId: string;
readonly reason: string;
readonly assignTo?: string;
readonly priority?: 'low' | 'medium' | 'high' | 'critical';
}
/**
* Request to resolve an unknown.
*/
export interface ResolveUnknownRequest {
readonly unknownId: string;
readonly action: ResolutionAction;
readonly mappedCve?: string;
readonly notes?: string;
}
/**
* Bulk action request for multiple unknowns.
*/
export interface BulkUnknownsRequest {
readonly unknownIds: readonly string[];
readonly action: 'escalate' | 'resolve' | 'ignore' | 'assign';
readonly resolutionAction?: ResolutionAction;
readonly assignee?: string;
readonly notes?: string;
}
/**
* Result of a bulk action.
*/
export interface BulkUnknownsResult {
readonly successCount: number;
readonly failureCount: number;
readonly failures?: readonly {
readonly unknownId: string;
readonly error: string;
}[];
}