Files
git.stella-ops.org/docs/modules/web/smart-diff-ui-architecture.md
StellaOps Bot 634233dfed feat: Implement distro-native version comparison for RPM, Debian, and Alpine packages
- Add RpmVersionComparer for RPM version comparison with epoch, version, and release handling.
- Introduce DebianVersion for parsing Debian EVR (Epoch:Version-Release) strings.
- Create ApkVersion for parsing Alpine APK version strings with suffix support.
- Define IVersionComparator interface for version comparison with proof-line generation.
- Implement VersionComparisonResult struct to encapsulate comparison results and proof lines.
- Add tests for Debian and RPM version comparers to ensure correct functionality and edge case handling.
- Create project files for the version comparison library and its tests.
2025-12-22 09:50:12 +02:00

16 KiB

Smart-Diff UI Architecture

Version: 1.0 Status: Draft Last Updated: 2025-12-22 Sprint Reference: SPRINT_4200_0002_0003

Overview

The Smart-Diff UI provides a dedicated comparison experience for analyzing material risk changes between container image versions. It implements a "diff-first" approach to vulnerability triage, enabling users to focus on what changed rather than reviewing entire vulnerability lists.

Design Principles

1. Diff-First Triage

The primary question in any release is: "What changed that affects risk?" The UI defaults to showing delta information rather than full vulnerability lists.

2. Proof-Carrying Evidence

Every verdict and comparison includes cryptographic evidence. Users can verify determinism, trace decisions to policy rules, and replay computations.

3. Baseline Transparency

Comparisons require explicit baselines with auditor-friendly rationale. The system never uses "magic" to select baselines without explanation.

4. Role-Based Defaults

Different personas (Developer, Security, Audit) see different default views while retaining access to all information.

Component Architecture

┌─────────────────────────────────────────────────────────────────────────────┐
│                         SMART-DIFF UI ARCHITECTURE                           │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                        COMPARE VIEW CONTAINER                        │   │
│  │  ┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐   │   │
│  │  │  Baseline        │  │  Trust           │  │  Export          │   │   │
│  │  │  Selector        │  │  Indicators      │  │  Actions         │   │   │
│  │  └──────────────────┘  └──────────────────┘  └──────────────────┘   │   │
│  │           │                     │                     │              │   │
│  │  ┌────────────────────────────────────────────────────────────────┐ │   │
│  │  │                    DELTA SUMMARY STRIP                         │ │   │
│  │  │  [+N added] [-N removed] [~N changed] [Policy: v1.2] [Feed: 2h]│ │   │
│  │  └────────────────────────────────────────────────────────────────┘ │   │
│  │                                                                      │   │
│  │  ┌───────────────────────────────────────────────────────────────┐  │   │
│  │  │                    THREE-PANE LAYOUT                          │  │   │
│  │  │  ┌──────────┐  ┌────────────────┐  ┌────────────────────────┐ │  │   │
│  │  │  │Categories│  │    Items       │  │      Proof Panel       │ │  │   │
│  │  │  │          │  │                │  │                        │ │  │   │
│  │  │  │ ● SBOM   │  │ CVE-2024-1234  │  │ ┌────────────────────┐ │ │  │   │
│  │  │  │ ● Reach  │  │ lodash@4.17.20 │  │ │ Witness Path       │ │ │  │   │
│  │  │  │ ● VEX    │  │ +reachable     │  │ │ main() → parse()   │ │ │  │   │
│  │  │  │ ● Policy │  │ Priority: 0.85 │  │ │ → vuln_func()      │ │ │  │   │
│  │  │  │ ● Unknwn │  │                │  │ └────────────────────┘ │ │  │   │
│  │  │  │          │  │ CVE-2024-5678  │  │ ┌────────────────────┐ │ │  │   │
│  │  │  │          │  │ requests@2.28  │  │ │ VEX Merge          │ │ │  │   │
│  │  │  │          │  │ +KEV           │  │ │ vendor: affected   │ │ │  │   │
│  │  │  │          │  │ Priority: 0.95 │  │ │ distro: not_aff    │ │ │  │   │
│  │  │  │          │  │                │  │ │ → Result: affected │ │ │  │   │
│  │  │  │          │  │                │  │ └────────────────────┘ │ │  │   │
│  │  │  └──────────┘  └────────────────┘  └────────────────────────┘ │  │   │
│  │  └───────────────────────────────────────────────────────────────┘  │   │
│  │                                                                      │   │
│  │  ┌────────────────────────────────────────────────────────────────┐ │   │
│  │  │                    ACTIONABLES PANEL                           │ │   │
│  │  │  ┌─────────────────────────────────────────────────────────┐   │ │   │
│  │  │  │ What to do next:                                        │   │ │   │
│  │  │  │ 1. [CRITICAL] Upgrade lodash → 4.17.21                  │   │ │   │
│  │  │  │ 2. [HIGH] Add VEX statement for urllib3 (not affected)  │   │ │   │
│  │  │  │ 3. [MEDIUM] Resolve unknown: missing SBOM for module A  │   │ │   │
│  │  │  └─────────────────────────────────────────────────────────┘   │ │   │
│  │  └────────────────────────────────────────────────────────────────┘ │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Component Hierarchy

CompareViewComponent
├── BaselineSelectorComponent
│   └── BaselineRationaleComponent
├── TrustIndicatorsComponent
│   ├── DeterminismHashDisplay
│   ├── PolicyVersionDisplay
│   ├── FeedSnapshotDisplay
│   ├── SignatureStatusDisplay
│   └── PolicyDriftIndicator
├── DeltaSummaryStripComponent
├── ThreePaneLayoutComponent
│   ├── CategoriesPaneComponent
│   ├── ItemsPaneComponent
│   └── ProofPaneComponent
│       ├── WitnessPathComponent
│       ├── VexMergeExplanationComponent
│       └── EnvelopeHashesComponent
├── ActionablesPanelComponent
└── ExportActionsComponent

State Management

Signals-Based State

The compare view uses Angular signals for reactive state management:

// Core state
currentTarget = signal<CompareTarget | null>(null);
baselineTarget = signal<CompareTarget | null>(null);
delta = signal<DeltaVerdictResponse | null>(null);

// UI state
selectedCategory = signal<string | null>(null);
selectedItem = signal<DeltaItem | null>(null);
viewMode = signal<'side-by-side' | 'unified'>('side-by-side');
userRole = signal<'developer' | 'security' | 'audit'>('developer');

// Computed state
filteredItems = computed(() => {
  const cat = this.selectedCategory();
  const items = this.delta()?.Items ?? [];
  return cat ? items.filter(i => i.category === cat) : items;
});

deltaSummary = computed(() => this.delta()?.Summary);
trustIndicators = computed(() => this.delta()?.TrustIndicators);

Data Flow

┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   Route     │───►│  Component  │───►│   Service   │
│  Params     │    │   Init      │    │   Calls     │
└─────────────┘    └─────────────┘    └─────────────┘
                          │                   │
                          ▼                   ▼
                   ┌─────────────┐    ┌─────────────┐
                   │   Signals   │◄───│   Backend   │
                   │   Update    │    │   Response  │
                   └─────────────┘    └─────────────┘
                          │
                          ▼
                   ┌─────────────┐
                   │  Computed   │
                   │   Values    │
                   └─────────────┘
                          │
                          ▼
                   ┌─────────────┐
                   │  Template   │
                   │   Render    │
                   └─────────────┘

API Integration

Backend Endpoints

Endpoint Purpose
GET /api/v1/baselines/recommendations/{digest} Get recommended baselines
GET /api/v1/baselines/rationale/{base}/{head} Get baseline selection rationale
POST /api/v1/delta/compute Compute delta (idempotent)
GET /api/v1/delta/{deltaId} Get delta results
GET /api/v1/delta/{deltaId}/trust-indicators Get trust indicators
GET /api/v1/actionables/delta/{deltaId} Get actionable recommendations
GET /api/v1/evidence/delta/{deltaId}/items/{itemId} Get item evidence
GET /api/v1/evidence/delta/{deltaId}/witness-paths Get witness paths
GET /api/v1/evidence/delta/{deltaId}/vex-merge/{vulnId} Get VEX merge explanation

Service Layer

@Injectable({ providedIn: 'root' })
export class CompareService {
  constructor(private http: HttpClient) {}

  getRecommendedBaselines(digest: string): Observable<BaselineRecommendationsResponse> {
    return this.http.get<BaselineRecommendationsResponse>(
      `/api/v1/baselines/recommendations/${digest}`
    );
  }

  computeDelta(request: DeltaComputeRequest): Observable<DeltaVerdictResponse> {
    return this.http.post<DeltaVerdictResponse>('/api/v1/delta/compute', request);
  }

  getActionables(deltaId: string): Observable<ActionablesResponse> {
    return this.http.get<ActionablesResponse>(`/api/v1/actionables/delta/${deltaId}`);
  }

  getItemEvidence(deltaId: string, itemId: string): Observable<DeltaItemEvidenceResponse> {
    return this.http.get<DeltaItemEvidenceResponse>(
      `/api/v1/evidence/delta/${deltaId}/items/${itemId}`
    );
  }
}

Routing

// app.routes.ts additions
{
  path: 'releases/:releaseId',
  children: [
    { path: '', redirectTo: 'detail', pathMatch: 'full' },
    { path: 'detail', component: ReleaseFlowComponent },
    {
      path: 'compare',
      component: CompareViewComponent,
      data: { requireBaseline: false }
    },
    {
      path: 'compare/:baselineId',
      component: CompareViewComponent,
      data: { requireBaseline: true }
    }
  ]
},
{
  path: 'compare',
  children: [
    {
      path: ':currentDigest',
      component: CompareViewComponent
    },
    {
      path: ':currentDigest/:baselineDigest',
      component: CompareViewComponent
    }
  ]
}

Role-Based Views

Default Tab by Role

Role Default Tab Visible Features
Developer Actionables Actionables, Witness Paths, Upgrade Suggestions
Security Claims VEX Merge, Policy Reasoning, Claim Sources, Actionables
Audit Attestations Signatures, Replay, Evidence Pack, Envelope Hashes

Implementation

const ROLE_DEFAULTS: Record<UserRole, RoleConfig> = {
  developer: {
    defaultTab: 'actionables',
    showFeatures: ['actionables', 'witness-paths', 'upgrade-suggestions'],
    expandedPanels: ['actionables']
  },
  security: {
    defaultTab: 'claims',
    showFeatures: ['vex-merge', 'policy-reasoning', 'claim-sources', 'actionables'],
    expandedPanels: ['vex-merge', 'policy']
  },
  audit: {
    defaultTab: 'attestations',
    showFeatures: ['signatures', 'replay', 'evidence-pack', 'envelope-hashes'],
    expandedPanels: ['trust-indicators', 'signatures']
  }
};

Trust Indicators

Determinism Verification

The UI displays and enables verification of:

  1. Determinism Hash - SHA-256 of normalized delta output
  2. Policy Version/Hash - Active policy at scan time
  3. Feed Snapshot - Vulnerability feed timestamp and hash
  4. Signature Status - DSSE envelope verification result

Degraded Mode

When signature verification fails, the UI:

  • Displays a prominent warning banner
  • Disables "Approve" actions
  • Shows detailed verification failure reason
  • Provides replay command for local verification

Accessibility

Keyboard Navigation

  • Tab / Shift+Tab: Navigate between panes
  • Arrow Up/Down: Navigate items within pane
  • Enter: Select item / expand detail
  • Escape: Close expanded detail
  • C: Copy replay command (when focused on trust indicators)

Screen Reader Support

  • ARIA labels on all interactive elements
  • Live regions for delta summary updates
  • Semantic heading structure

Performance Considerations

Lazy Loading

  • Evidence panel loads on-demand when item selected
  • Witness paths collapse by default (expand on click)
  • VEX merge details in expansion panel

Caching

  • Delta computations cached by (base_hash, head_hash, policy_hash)
  • Baseline recommendations cached per session
  • Trust indicators cached with delta

Virtual Scrolling

For large deltas (> 100 items), the items pane uses virtual scrolling:

<cdk-virtual-scroll-viewport itemSize="56" class="items-viewport">
  <mat-list-item *cdkVirtualFor="let item of filteredItems()">
    <!-- item content -->
  </mat-list-item>
</cdk-virtual-scroll-viewport>

Testing Strategy

Unit Tests

  • Component behavior (selection, filtering, expansion)
  • Computed signal derivations
  • Role-based view switching

Integration Tests

  • API service calls and response handling
  • Navigation and routing
  • State persistence across route changes

E2E Tests

  • Full comparison workflow
  • Baseline selection and rationale display
  • Export functionality
  • Role-based default verification