41 lines
2.6 KiB
TypeScript
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>;
|
|
}
|