Assessor_2.0/src/compliance/ui/FindingBsiRequirements.tsx

41 lines
2.6 KiB
TypeScript

import { TriangleIcon } from '../../components/TriangleIcon';
import type { BsiOverview } from '../bsi-grundschutz';
import './compliance.css';
const byNumber = (left: string, right: string) => left.localeCompare(right, 'en', { numeric: true });
/**
* Finding Reader section of the BSI Grundschutz++ requirements derived from this finding's accepted ISO
* controls through the BSI mapping (Phase 13F, stage 1). Derived only; it is no decision of its own.
* User decision 2026-10-08: it starts collapsed like `Context`, groups the requirements under their ISO
* control as one-line rows and shows a statement only when its requirement is opened.
*/
export function FindingBsiRequirements({ bsi }: { bsi?: BsiOverview }) {
if (!bsi?.requirements.length) return null;
const isoControls = [...new Set(bsi.requirements.flatMap(requirement => requirement.via.map(via => via.isoControlId)))].sort(byNumber);
const count = bsi.requirements.length;
return <details className="finding-context finding-bsi-requirements" data-bsi-requirements>
<summary className="finding-context__header"><TriangleIcon direction="right"/><h4>BSI requirements</h4>
<span className="finding-context__meta">{count} {count === 1 ? 'requirement' : 'requirements'} derived from the accepted ISO controls</span></summary>
<div className="finding-context__content">
{isoControls.map(isoControlId => {
const reached = bsi.requirements.filter(requirement => requirement.via.some(via => via.isoControlId === isoControlId));
// One BSI mapping entry per ISO control, so its relationship holds for the whole group.
const relationship = reached[0].via.find(via => via.isoControlId === isoControlId)!.relationship;
return <div key={isoControlId} className="finding-bsi-requirements__group" data-iso-control={isoControlId}>
<p className="finding-bsi-requirements__route">A.{isoControlId} · {relationship}</p>
<ul>{reached.map(requirement => <li key={requirement.requirementId} data-requirement={requirement.requirementId}>
<details className="finding-bsi-requirements__item">
<summary><TriangleIcon direction="right"/><strong>{requirement.requirementId}</strong><span>{requirement.title}</span></summary>
<p className="finding-bsi-requirements__statement">{requirement.statement}</p>
</details>
</li>)}</ul>
</div>;
})}
<p className="finding-bsi-requirements__source">
Via BSI mapping {bsi.crosswalkVersion} · catalog {bsi.catalogVersion.slice(0, 10)} · Source: BSI Stand-der-Technik-Bibliothek, CC BY-SA 4.0
</p>
</div>
</details>;
}