Skip to content

FRD-069: OSCAL Evidence Viewer Widget

FieldValue
OwnerDavid Holmes
StatusDraft
Last Updated2026-05-26
Target Releasev2.0.0 (P2)
T-Shirt SizeL
TypeWidget

Document Summary

A hierarchical evidence viewer for OSCAL (Open Security Controls Assessment Language) compliance data. Displays control families and individual controls in a collapsible tree, links to evidence artifacts, shows last-assessed timestamps, and supports PDF export. Extends the existing ComplianceScoreCard from summary scores into detailed control-level views.


Introduction

Overview

Organizations subject to NIST 800-53, FedRAMP, SOC 2, or ISO 27001 need to review and present compliance evidence at the control level. The existing ComplianceScoreCard provides a summary view of scores by framework. This widget drills into the control hierarchy, showing individual controls, their assessment status, evidence links, and assessment dates.

Goals

  • Display OSCAL control families as a collapsible tree.
  • Show individual controls within each family with status, evidence links, and last-assessed date.
  • Support multiple compliance frameworks (NIST 800-53, FedRAMP, SOC 2, ISO 27001).
  • Provide PDF export of the evidence view for auditors.
  • Ship Storybook stories with representative compliance data.

Non-Goals

  • OSCAL data parsing from raw XML/JSON (consumer normalizes data before passing).
  • Evidence artifact storage or upload.
  • Control implementation guidance or remediation workflows.
  • Automated assessment execution.

Scope

In Scope

ItemDescription
OscalEvidenceViewer componentCollapsible tree of control families and controls
Control family nodesExpandable headers showing family ID, name, and summary counts
Control nodesIndividual controls with status badge, evidence links, and last-assessed date
Evidence linksClickable links to evidence artifacts (URLs)
PDF exportButton to export the current view as a PDF document
Framework selectorDropdown to switch between loaded frameworks
Storybook storiesNIST, FedRAMP, SOC2, WithEvidence, EmptyFamily, Loading

Out of Scope

ItemRationale
OSCAL XML/JSON parsingConsumer normalizes data; parsing varies by source
Evidence uploadSeparate workflow
Remediation trackingSeparate widget or tool
Assessment executionBackend concern

Users and Pain Points

UserPain Point
Compliance officersNo standard UI for reviewing control-level evidence across frameworks
AuditorsEvidence scattered across systems; need a consolidated view for assessment
SRE/security teamsComplianceScoreCard only shows summary; no drill-down to controls

Definitions

TermDefinition
OSCALOpen Security Controls Assessment Language; NIST standard for machine-readable compliance data
Control familyA grouping of related security controls (e.g., AC - Access Control)
ControlAn individual security requirement (e.g., AC-2 Account Management)
EvidenceAn artifact (document, screenshot, log) demonstrating compliance with a control
AssessmentThe evaluation of a control against its requirements at a point in time

Current State

compliance-score-card.tsx shows a score summary for a compliance framework with total/passing/failing/not-applicable counts and an overall score. It supports NIST 800-53, FedRAMP, SOC 2, and ISO 27001 via the ComplianceFramework type. No control-level detail view exists. Teams build custom compliance dashboards or use external GRC tools.


Proposed Solution

Create an OscalEvidenceViewer widget at src/components/widgets/sre-devops/oscal-evidence-viewer.tsx that:

  1. Accepts a ControlFamily[] data structure representing the control hierarchy.
  2. Renders a collapsible tree with control families as parent nodes and controls as children.
  3. Each control shows: control ID, name, status badge, evidence link count, and last-assessed date.
  4. Evidence links are clickable and open in a new tab.
  5. Family headers show aggregate counts (passing/failing/not-assessed).
  6. Toolbar includes a framework selector and a PDF export button.
  7. PDF export generates a printable summary of the control tree.

Requirements

The widget must handle large control catalogs (NIST 800-53 has 20 families and 300+ controls) without performance issues. The collapsible tree must be keyboard-navigable.


Functional Requirements

IDRequirementPriority
FR-01Render control families as collapsible tree nodesMust
FR-02Render individual controls within families with ID, name, and statusMust
FR-03Display status badge per control (passing, failing, not-assessed, not-applicable)Must
FR-04Show evidence links per control with count indicatorMust
FR-05Evidence links open in new tabMust
FR-06Display last-assessed date per controlMust
FR-07Family headers show aggregate counts (e.g., “12 passing, 3 failing, 2 not assessed”)Must
FR-08Provide expand-all and collapse-all controlsShould
FR-09Toolbar framework selector switches between loaded frameworksShould
FR-10PDF export button generates a printable documentMust
FR-11Search/filter controls by ID or nameShould
FR-12Support a loading prop with skeleton treeShould
FR-13Show empty state when no controls are loadedMust

Non-Functional Requirements

IDRequirement
NFR-01Renders 20 families with 300+ total controls without perceptible lag
NFR-02PDF export completes within 5 seconds for a full NIST 800-53 catalog
NFR-03Full light/dark theme support
NFR-04Bundle size under 8 KB gzipped (excluding PDF generation library)

API / Interface Requirements

import type { ComplianceFramework } from "@/components/widgets/sre-devops/compliance-score-card";
type ControlStatus = "passing" | "failing" | "not-assessed" | "not-applicable";
interface EvidenceLink {
id: string;
label: string;
url: string;
type?: "document" | "screenshot" | "log" | "report";
}
interface Control {
id: string; // e.g. "AC-2"
name: string; // e.g. "Account Management"
status: ControlStatus;
evidence: EvidenceLink[];
lastAssessedAt?: string; // ISO 8601
notes?: string;
}
interface ControlFamily {
id: string; // e.g. "AC"
name: string; // e.g. "Access Control"
controls: Control[];
}
interface OscalEvidenceViewerProps {
framework: ComplianceFramework;
families: ControlFamily[];
loading?: boolean;
emptyMessage?: string;
onExportPdf?: () => void; // consumer-handled PDF generation
onSelectControl?: (controlId: string) => void;
className?: string;
}

Accessibility Requirements

IDRequirement
A11Y-01Tree uses role="tree" with role="treeitem" per family and control
A11Y-02Expand/collapse toggles via Enter/Space; arrow keys navigate tree
A11Y-03Status badges have accessible text labels
A11Y-04Evidence links have descriptive text (not just “Link”)
A11Y-05Search input has a visible label
A11Y-06Family aggregate counts are announced when a family node receives focus
A11Y-07PDF export button has descriptive aria-label

Content and Documentation Requirements

  • Storybook doc page explaining OSCAL context, relationship to ComplianceScoreCard, and data structure.
  • Stories: NIST80053, FedRAMP, SOC2, WithEvidence, EmptyFamilies, SearchFilter, Loading.
  • JSDoc on all exported types.
  • Example data files for each supported framework.

Dependencies

DependencyTypeNotes
ComplianceFrameworkInternalReuse type from compliance-score-card.tsx
BadgeInternalControl status badges
ButtonInternalToolbar actions
InputInternalSearch field
PDF generationExternal/ConsumerConsumer provides PDF generation; widget calls onExportPdf

Risks and Tradeoffs

RiskImpactMitigation
Large control catalogs (300+)Slow initial renderLazy-render collapsed families; only render children on expand
OSCAL data normalization variesInconsistent data shapesDocument expected data structure clearly; validate at runtime
PDF export complexityHeavy dependencyDelegate PDF generation to consumer via callback; provide print CSS as alternative
Framework-specific control IDsDisplay confusionShow framework label alongside control IDs

Open Questions

  1. Should the widget support inline evidence preview (e.g., document thumbnails) or link-only?
  2. Do we need a “Download all evidence” bulk action?
  3. Should the PDF export be handled internally (using a library like jsPDF) or always delegated to the consumer?
  4. Should controls support sub-controls (e.g., AC-2(1), AC-2(2))?

Acceptance Criteria

  • Control families render as collapsible tree nodes.
  • Individual controls show ID, name, status, evidence links, and last-assessed date.
  • Status badges display correct semantic colors.
  • Evidence links are clickable and open in new tabs.
  • Family headers show aggregate status counts.
  • Expand-all and collapse-all work correctly.
  • Search filters controls by ID or name.
  • PDF export triggers the onExportPdf callback.
  • Empty and loading states render correctly.
  • All Storybook stories render without errors.
  • Passes axe accessibility audit with zero violations.
  • Unit tests cover tree rendering, expand/collapse, search, and status display.

LLM Handoff Instructions

When implementing this FRD:

  1. Create src/components/widgets/sre-devops/oscal-evidence-viewer.tsx.
  2. Import ComplianceFramework from ./compliance-score-card.
  3. Build a recursive tree component for families and controls.
  4. Use role="tree" and role="treeitem" ARIA attributes for accessibility.
  5. Create src/components/widgets/sre-devops/oscal-evidence-viewer.stories.tsx with example data for each framework.
  6. Create src/components/widgets/sre-devops/oscal-evidence-viewer.test.tsx.
  7. Lazy-render control children (only mount when family is expanded) for performance.
  8. PDF export: call onExportPdf callback. Optionally add @media print CSS for browser print support.
  9. Reference compliance-score-card.tsx for framework label mapping and color conventions.

Decision Log

DateDecisionRationale
2026-05-26Consumer normalizes OSCAL data before passing to widgetOSCAL XML/JSON parsing is complex and varies by source; keep widget focused on presentation
2026-05-26PDF export delegated to consumer via callbackAvoids bundling heavy PDF libraries; consumer chooses their preferred approach
2026-05-26Reuse ComplianceFramework typeEnsures consistency with ComplianceScoreCard

Document History

DateVersionAuthorChanges
2026-05-260.1David HolmesInitial draft