<!-- markdownlint-disable MD013 -->
<!--
SPDX-License-Identifier: Apache-2.0
SPDX-FileCopyrightText: 2026 ndaal Gesellschaft für Sicherheit in der Informationstechnik mbH & Co KG, Cologne
-->
<!-- markdownlint-enable MD013 -->
# BSI Grundschutz++ OSCAL Viewer
> This file is the English original. Deutsche Fassung:
> [LIESMICH.md](LIESMICH.md) · Version française :
> [LISEZMOI.md](LISEZMOI.md)
A safe, single-binary, offline viewer for the official **BSI
Grundschutz++** catalog, written in Rust. The complete OSCAL catalog
from the BSI [Stand-der-Technik-Bibliothek](https://github.com/BSI-Bund/Stand-der-Technik-Bibliothek)
is embedded in the executable at compile time: **copying the binary to
any machine is a full installation** — no install step, no database, no
network access required.
The web UI design and the Info menu are carried over from the ndaal
`vulnerability-lookup-rs` project (hyper + Askama + Bootstrap 5 + HTMX,
all assets embedded — no CDN).
## Features
- **Single binary, fully offline** — the complete Anwenderkatalog
Grundschutz++ (OSCAL 1.1.3, 1000 controls, 20 practices) plus all UI
assets (Bootstrap, Bootstrap Icons, HTMX, Roboto fonts) ship inside
the executable.
- **Filters** — practice (domain), requirement level
(MUSS / SOLLTE / KANN), security level (`normal-SdT` / `erhöht`),
effort level (0–5), tag and free text; filters combine and survive
pagination.
- **Full-text search** — a built-in engine works out of the box and is the
default. A [Meilisearch](https://www.meilisearch.com/) instance is
**opt-in** for typo-tolerant ranked search: enable it by passing a URL
(`--meili-url http://localhost:7700` or `MEILI_URL=…`). When enabled the
index is populated automatically at startup, with automatic fallback to
the built-in engine whenever Meilisearch is unset or unreachable. The
Meilisearch client is rustls-protected (HTTPS for any non-loopback host,
TLS 1.3 only).
- **Export** — the navbar **Export** menu writes the whole catalog, a
single practice or a single control as **JSON, Markdown, ODT or PDF — or a
ZIP bundle** of those formats
(Markdown/ODT via the pure-Rust `lo_writer`; PDF rendered directly with an
embedded TrueType font so German umlauts are faithful; the ZIP built with the
zero-dependency `lo_zip`), each with
selectable checksum **sidecars** (`.sha-256`, `.sha-512`, `.sha3-512`,
`.blake3-512`, `.shake256-512`). Files land in `--export-dir` (default
`grundschutz-oscal-viewer/export` under your home directory — `$HOME` on
Linux/macOS, `%USERPROFILE%` on Windows) via a capability-scoped,
non-overwriting writer.
- **Annotations** *(opt-in)* — the navbar **Annotations** workspace adds
Markdown notes to the **catalog, any practice or any control**, with a
Source ⇄ Rendered preview, a `MUSS`/`SOLLTE`/`KANN` modal (defaulting from
the control), a **CMMI Maturity Level** picker (0 Incomplete … 5 Innovating,
default 1 Performed), a draft/release toggle, **append-only ISO-8601
versioning**,
and PNG/SVG image attachments (served only as downloads). Markdown rendering
is XSS-safe (`markdown` crate) and uploads pass a strict MIME + structural
validator. Enable with `--db <FILE>` / `GSV_DB`; the notes live in a local
SQLite file (statically-linked vendored SQLite — still a single binary).
Without `--db` the viewer stays entirely read-only. To explore the workspace
with ready-made data, add `--seed-demo` (opt-in, non-destructive): it seeds a
demonstration annotation — a fifteen-version Draft → Release history plus
generated demo images — for the whole catalog, every practice and every
control. The ≤ 15 patch-management controls (`DET.5.10`, `KONF.8.1.1`,
`DEV.5.1`, … selected by Grundschutz++ catalog title) additionally carry a
**CISA BOD 26-04** remediation walk (V16–V24): the trilingual *Prioritizing
Security Updates Based on Risk* playbook and its colour decision-tree
graphic, so those controls show 24 versions. The playbook ships as
importable templates in
`templates/CISA_BOD_26-04_Remediation_Timelines_template_{EN,DE,FR}.md`.
- **Catalog change tracking** — when the embedded catalog is refreshed to
a newer BSI edition, every control whose requirement content changed
(title, statement, guidance, modal verb, security/effort level, tags or
parameters) is flagged **Updated** and shows its previous content for
comparison; brand-new controls are flagged **New**.
- **Every JSON byte visible** — each control page renders statement
(with OSCAL parameter substitution), guidance, props with namespaces,
parameters, tags and nested sub-controls **plus** the raw OSCAL JSON;
`/catalog.json` serves the embedded catalog byte-for-byte and
`/control/<id>/raw.json` each control.
- **Cross-framework mappings** — each control page shows, per framework,
the corresponding control in that framework (reference id + name), with
a similarity score and an "auto-generated — verify before use" caveat.
The navbar **Settings** menu lists ~150 frameworks (with flags) in a
searchable, scrollable panel with All / None / Reset; a default subset
(ISO 27001, NIS2, PCI DSS, GDPR, DORA, CRA, BSI Kompendium / C5 /
Mindeststandard Cloud, NIS2 IR, OWASP ASVS 5, EU AI Act, ISO 42001,
IEC 62443) is on, the rest off. **All** mapping data — the generated
Grundschutz++ ↔ framework crosswalk (~181k rows across 139 frameworks)
plus the hand-curated crosswalks (GitHub Security Controls, TISAX ISA
2027) — is **embedded in the binary** (Apache-2.0, ndaal's own work), so
every control shows its cross-framework mappings out of the box, with no
flag and no external pack. You can *optionally* load extra or custom CSVs
at runtime with `--mappings-dir`, merged on top of the embedded set (see
[framework mappings pack](documentation/framework_mappings_pack.md)).
- **Statistics** — catalog-wide distributions for modal verbs, security
levels, effort levels and practices.
- **Info menu** — About, System info, Privacy, Security, License,
Imprint and Changelog, as in the reference design.
- **HTTPS only** — TLS 1.3 via [rustls](https://github.com/rustls/rustls)
(aws-lc-rs provider with `prefer-post-quantum`, so the
X25519MLKEM768 post-quantum hybrid key-exchange group is offered
first). A self-signed certificate for localhost is generated at every
start-up (45-day validity); there is no plaintext-HTTP fallback.
- **Safe by construction** — `#![forbid(unsafe_code)]`, panicking
shortcuts denied by lint policy, capability-based filesystem access
(cap-std) for the only two write paths (`--export` and the Export
menu), localhost binding by default, defence-in-depth security headers
on every response.
- **Multiplatform** — pure-Rust dependency tree (no OpenSSL, no C TLS
stack — rustls all the way); builds on Linux, macOS and Windows,
x86_64 and aarch64.
- **Trilingual interface (English / German / French)** — the UI chrome
(navigation, settings, headings, table headers, labels and buttons) is
translated. A **language switcher** in the navbar pins the choice in a
`gsv_lang` cookie; without it the browser's `Accept-Language` header is
honoured, with English as the fallback. The embedded BSI catalog content
and the terms of art (`MUSS` / `SOLLTE` / `KANN`, `erhöht`,
`normal-SdT`) intentionally stay in their source language, as does the
German legal Impressum.
- **Encryption at rest** *(opt-in)* — `--encryption-key-file <FILE>`
(`GSV_ENCRYPTION_KEY_FILE`) seals annotation bodies and image assets with
**XChaCha20-Poly1305** (AEAD) before they reach SQLite, so a stolen `.db`
file — or any `.db` / `.sql` / `.sql.gz` dump of it — yields ciphertext.
The key is 32 raw bytes or 64 hex characters. Plaintext and encrypted rows
coexist via a self-describing marker, so the key can be added to an existing
database with **no migration**; a configured-but-unreadable key is a hard
start-up error rather than a silent plaintext fallback. The key can be
**rotated without downtime**: pass the previous key as
`--retired-encryption-key-file` and it stays available for reading while new
writes use the new key (no re-encryption pass, no change to stored data).
Pure-Rust RustCrypto, so it cross-compiles to every target.
- **Database dumps + restore** — the **Export** menu also writes a consistent
SQLite snapshot (`VACUUM INTO`) and a portable SQL text dump (optionally
gzipped to `.sql.gz`) of the annotation database, each with checksum
sidecars, into `--dump-dir` (a chosen sub-path is confined under that
root). A management list **downloads** or **deletes** existing dumps.
`--restore-dump <FILE>` rebuilds a database from a `.db` snapshot or a
`.sql` / `.sql.gz` text dump — offline and non-destructive (the target
must not already exist).
- **Searchable annotations** — with `--db` enabled, `/search` surfaces a
*Matching annotations* section alongside the catalog hits; the search
decrypts in memory, so it keeps working under encryption-at-rest.
- **Operations-ready** — health probes `GET /healthz` (liveness) and
`/readyz` (readiness, `200` once the catalog is loaded and, with
`--db`, the store answers `SELECT 1`) for load balancers and
orchestrators; `--tls-cert` / `--tls-key` present a CA-issued certificate
instead of the ephemeral self-signed one; every request's `Host` is pinned
to the bind address (anti-DNS-rebinding), so `--allowed-host` /
`GSV_ALLOWED_HOSTS` is needed only behind a real hostname or reverse proxy;
`--log-format json` emits structured, newline-delimited logs for aggregation.
- **Self-update** *(explicit, checksum-verified)* — `--check-update` reports
whether a newer release exists on
`gitlab.com/vPierre/ndaal_public_bsi_grundschutz_oscal_viewer` (read-only, an
unreachable host is never fatal), and `--self-update` downloads the matching
target-triple binary, **verifies its SHA-256 against the committed
`release/SHA256SUMS`** at the tag, and atomically replaces the running
executable over TLS 1.3 (the same aws-lc-rs stack as the server — no second
crypto provider, no TLS 1.2). It pins the exact release it checksummed, never
downgrades, and never installs on a checksum mismatch. Opt out with
`--no-self-update` / `GSV_NO_SELF_UPDATE` for package-managed or locked-down
installs.
- **Accessibility** — a *Skip to main content* link is the first focusable
element (jumping past the navbar to a focusable `<main>` landmark), and the
HTMX live-search results are an `aria-live` region announced by screen
readers.
## Framework mappings
All 156 frameworks selectable from the navbar **Settings** menu — same
order, same icons the app shows. ✅ marks the 14 that are **on by
default**; the rest are off until you enable them (individually, or with the
**All** button). A toggle reveals rows on a control page whenever that
framework has mapping data — and all of it is **embedded in the binary**:
the generated Grundschutz++ ↔ framework crosswalk (~181k rows across 139
frameworks) plus the hand-curated crosswalks (GitHub Security Controls,
TISAX ISA 2027). No external pack is needed; `--mappings-dir` only *adds*
optional extra CSVs on top (see
[framework mappings pack](documentation/framework_mappings_pack.md)).
<!-- markdownlint-disable MD013 -->
| # | Framework (as shown in Settings) | Toggle slug | Default |
| --: | --- | --- | :-: |
| 1 | ISO 27001:2013 & 27001:2022 🌐 | `iso27001` | ✅ |
| 2 | NIST Cyber Security Framework (CSF) v1.1 🇺🇸 | `nist-cyber-security-framework-csf-v1` | — |
| 3 | NIST Cyber Security Framework (CSF) v2.0 🇺🇸 | `nist-cyber-security-framework-csf-v2` | — |
| 4 | NIS2 🇪🇺 | `nis2` | ✅ |
| 5 | SOC2 🇺🇸 | `soc2` | — |
| 6 | PCI DSS 4.0.1 💳 | `pci-dss-4` | ✅ |
| 7 | CMMC v2 🇺🇸 | `cmmc-v2` | — |
| 8 | PSPF 🇦🇺 | `pspf` | — |
| 9 | General Data Protection Regulation (GDPR): Full text and checklist from GDPR.EU 🇪🇺 | `gdpr` | ✅ |
| 10 | Essential Eight 🇦🇺 | `essential-eight` | — |
| 11 | NYDFS 500 with 2023-11 amendments 🇺🇸 | `nydfs-500-with-2023-11-amendments` | — |
| 12 | DORA (Act, RTS, ITS and GL) 🇪🇺 | `dora` | ✅ |
| 13 | NIST AI Risk Management Framework 🇺🇸🤖 | `nist-ai-risk-management-framework` | — |
| 14 | NIST SP 800-53 rev5 🇺🇸 | `nist-sp-800-53-rev5` | — |
| 15 | Règles OIV - Secteur « Activités civiles de l'Etat » (2019) 🇫🇷 | `r-gles-oiv-secteur-activit-s` | — |
| 16 | CCB CyberFundamentals Framework 🇧🇪 | `ccb-cyberfundamentals-framework` | — |
| 17 | NIST SP-800-66 (HIPAA) 🏥 | `nist-sp-800-66-hipaa` | — |
| 18 | HDS/HDH 🇫🇷 | `hds-hdh` | — |
| 19 | OWASP Application Security Verification Standard (ASVS) 4 🐝🖥️ | `owasp-application-security-verification` | — |
| 20 | RGS v2.0 🇫🇷 | `rgs-v2-0` | — |
| 21 | AirCyber ✈️🌐 | `aircyber` | — |
| 22 | Cyber Resilience Act (CRA) 🇪🇺 | `cra` | ✅ |
| 23 | TIBER-EU 🇪🇺 | `tiber-eu` | — |
| 24 | NIST Privacy Framework 🇺🇸 | `nist-privacy-framework` | — |
| 25 | TISAX (VDA ISA) v5.1 and v6.0 🚘 | `tisax-vda-isa-v5-1-and` | — |
| 26 | TISAX (VDA ISA) 2027 🚘 | `tisax-vda-isa-2027` | — |
| 27 | ANSSI hygiene guide 🇫🇷 | `anssi-hygiene-guide` | — |
| 28 | Essential Cybersecurity Controls (ECC) 🇸🇦 | `essential-cybersecurity-controls-ecc` | — |
| 29 | CIS Controls v8* 🌐 | `cis-controls-v8` | — |
| 30 | CSA CCM (Cloud Controls Matrix)* ☁️ | `csa-ccm-cloud-controls-matrix` | — |
| 31 | FADP (Federal Act on Data Protection) 🇨🇭 | `fadp-federal-act-on-data-protection` | — |
| 32 | NIST SP 800-171 rev2 (2021) 🇺🇸 | `nist-sp-800-171-rev2-2021` | — |
| 33 | ANSSI : recommandations de sécurité pour un système d'IA générative 🇫🇷🤖 | `anssi-recommandations-de-s-curit-pour` | — |
| 34 | NIST SP 800-218: Secure Software Development Framework (SSDF) 🖥️ | `nist-sp-800-218-secure-software` | — |
| 35 | GSA FedRAMP rev5 ☁️🇺🇸 | `gsa-fedramp-rev5` | — |
| 36 | Cadre Conformité Cyber France (3CF) v1 (2021) ✈️🇫🇷 | `cadre-conformit-cyber-france-3cf-v1` | — |
| 37 | ANSSI : SecNumCloud ☁️🇫🇷 | `anssi-secnumcloud` | — |
| 38 | Cadre Conformité Cyber France (3CF) v2 (2024) ✈️🇫🇷 | `cadre-conformit-cyber-france-3cf-v2` | — |
| 39 | ANSSI : outil d'autoévaluation de gestion de crise cyber 💥🇫🇷 | `anssi-outil-d-auto-valuation-de` | — |
| 40 | BSI: IT-Grundschutz-Kompendium 🇩🇪 | `bsi-kompendium` | ✅ |
| 41 | NIST SP 800-171 rev3 (2024) 🇺🇸 | `nist-sp-800-171-rev3-2024` | — |
| 42 | ENISA: 5G Security Controls Matrix 🇪🇺 | `enisa-5g-security-controls-matrix` | — |
| 43 | OWASP Mobile Application Security Verification Standard (MASVS) 🐝📱 | `owasp-mobile-application-security` | — |
| 44 | Agile Security Framework (ASF) - baseline - by intuitem 🤗 | `agile-security-framework-asf-baseline-by` | — |
| 45 | ISO 27001:2013 🌐 (For legacy and migration) | `iso-27001-2013-for-legacy-and` | — |
| 46 | EU AI Act 🇪🇺🤖 | `eu-ai-act` | ✅ |
| 47 | FBI CJIS 🇺🇸👮 | `fbi-cjis` | — |
| 48 | Operational Technology Cybersecurity Controls (OTCC) 🇸🇦 | `operational-technology-cybersecurity` | — |
| 49 | Secure Controls Framework (SCF) 🇺🇸🌐 | `secure-controls-framework-scf` | — |
| 50 | NCSC - Cyber Assessment Framework (CAF) v3.2 🇬🇧 | `ncsc-cyber-assessment-framework-caf-v3` | — |
| 51 | California Consumer Privacy Act (CCPA) 🇺🇸 | `california-consumer-privacy-act-ccpa` | — |
| 52 | California Consumer Privacy Act Regulations 🇺🇸 | `california-consumer-privacy-act` | — |
| 53 | NCSC Cyber Essentials 🇬🇧 | `ncsc-cyber-essentials` | — |
| 54 | Directive Nationale de la Sécurité des Systèmes d'Information (DNSSI) Maroc 🇲🇦 | `directive-nationale-de-la-s-curit` | — |
| 55 | Part-IS ✈️🇪🇺 | `part-is` | — |
| 56 | ENS Esquema Nacional de seguridad 🇪🇸 | `ens-esquema-nacional-de-seguridad` | — |
| 57 | Korea ISA ISMS-P 🇰🇷 | `korea-isa-isms-p` | — |
| 58 | Swiss ICT minimum standard 🇨🇭 | `swiss-ict-minimum-standard` | — |
| 59 | Adobe Common Controls Framework (CCF) v5 🌐 | `adobe-common-controls-framework-ccf-v5` | — |
| 60 | BSI Cloud Computing Compliance Criteria Catalogue (C5) 🇩🇪 | `bsi-c5` | ✅ |
| 61 | Référentiel d'Audit de la Sécurité des Systèmes d'Information, ANCS Tunisie 🇹🇳 | `r-f-rentiel-d-audit-de` | — |
| 62 | ECB Cyber resilience oversight expectations for financial market infrastructures 🇪🇺 | `ecb-cyber-resilience-oversight` | — |
| 63 | Mindeststandard-des-BSI-zur-Nutzung-externer-Cloud-Dienste (Version 2.1) 🇩🇪 | `bsi-ms-cloud` | ✅ |
| 64 | Formulaire d'évaluation de la maturité - niveau fondamental (DGA) 🇫🇷 | `formulaire-d-valuation-de-la-maturit` | — |
| 65 | NIS2 technical and methodological requirements 2024/2690 🇪🇺 | `nis2-ir` | ✅ |
| 66 | Saudi Arabian Monetary Authority (SAMA) Cybersecurity Framework 🇸🇦 | `saudi-arabian-monetary-authority-sama` | — |
| 67 | Guide de sécurité des données (CNIL) 🇫🇷 | `guide-de-s-curit-des-donn` | — |
| 68 | International Traffic in Arms Regulations (ITAR) 🇺🇸 | `international-traffic-in-arms` | — |
| 69 | Federal Trade Commission (FTC) Standards for Safeguarding Customer Information 🇺🇸 | `federal-trade-commission-ftc-standards` | — |
| 70 | OWASP's checklist for LLM governance and security 🌐 | `owasp-s-checklist-for-llm-governance` | — |
| 71 | Recommandations pour les architectures des systèmes d'information sensibles ou à diffusion restreinte (ANSSI) 🇫🇷 | `recommandations-pour-les-architectures` | — |
| 72 | CIS benchmark for Kubernetes v1.10 🌐 | `cis-benchmark-for-kubernetes-v1-10` | — |
| 73 | De tekniske minimumskrav for statslige myndigheder 🇩🇰 | `de-tekniske-minimumskrav-for-statslige` | — |
| 74 | Google SAIF framework 🤖 | `google-saif-framework` | — |
| 75 | Recommandations relatives à l'administration sécurisée des SI (ANSSI) 🇫🇷 | `recommandations-relatives-l` | — |
| 76 | Prudential Standard CPS 230 - Operational Risk Management (APRA) 🇦🇺 | `prudential-standard-cps-230-operational` | — |
| 77 | Prudential Standard CPS 234 - Information Security (APRA) 🇦🇺 | `prudential-standard-cps-234-information` | — |
| 78 | Vehicle Cyber Security Audit (VCSA) v1.1 🚘 | `vehicle-cyber-security-audit-vcsa-v1` | — |
| 79 | Cisco Cloud Controls Framework (CCF) v3.0 ☁️🌐 | `cisco-cloud-controls-framework-ccf-v3` | — |
| 80 | FINMA - Circular 2023/01 - Operational risks and resilience - Banks 🇨🇭 | `finma-circular-2023-01-operational-risks` | — |
| 81 | Post-Quantum Cryptography (PQC) Migration Roadmap (May 2025) 🔐 | `post-quantum-cryptography-pqc-migration` | — |
| 82 | Cloud Sovereignty Framework - 1.2.1 - Oct 2025 🇪🇺 | `cloud-sovereignty-framework-1-2-1` | — |
| 83 | ISO 22301:2019 outline - Business continuity management systems 🌐 | `iso-22301-2019-outline-business` | — |
| 84 | CCB CyberFundamentals Framework 2025 🇧🇪 | `ccb-cyberfundamentals-framework-2025` | — |
| 85 | Prestataires de détection des incidents de sécurité (PDIS) - Référentiel d'exigences 🇫🇷 | `prestataires-de-d-tection-des-incidents` | — |
| 86 | Vendor Due Diligence - simple baseline - intuitem 🌐 | `vendor-due-diligence-simple-baseline` | — |
| 87 | Points de contrôle Active Directory (AD) - ANSSI 🇫🇷 | `points-de-contr-le-active-directory` | — |
| 88 | ISO 42001:2023 outline - Artificial Intelligence Management System, including Annex A 🤖🌐 | `iso-42001-2023-outline-artificial` | ✅ |
| 89 | India's Digital Personal Data Protection Act (DPDPA) - 2023 🇮🇳 | `india-s-digital-personal-data-protection` | — |
| 90 | E-ITS (Estonia's national cyber security standard) - 2024 🇪🇪 | `e-its-estonia-s-national-cyber` | — |
| 91 | Microsoft cloud security benchmark v1 - ☁️🌐 | `microsoft-cloud-security-benchmark-v1` | — |
| 92 | Baseline informatiebeveiliging Overheid 2 (BIO2) 🇳🇱 | `baseline-informatiebeveiliging-overheid` | — |
| 93 | ANSSI : Questionnaire MonAideCyber 🇫🇷 | `anssi-questionnaire-monaidecyber` | — |
| 94 | ITSP.10.171 - Protecting specified information in non-Government of Canada systems and organizations 🇨🇦 | `itsp-10-171-protecting-specified` | — |
| 95 | CISA Vendor Supply Chain Risk Management (SCRM) Template 🇺🇸 | `cisa-vendor-supply-chain-risk-management` | — |
| 96 | European Sustainability Reporting Standards (ESRS) 🇪🇺 | `european-sustainability-reporting` | — |
| 97 | ITIL 4 Management Practices 🌐 | `itil-4-management-practices` | — |
| 98 | NOREA - DORA in Control Framework v3.0 🇪🇺 | `norea-dora-in-control-framework-v3` | — |
| 99 | NIS-1 transposition FR 🇫🇷 | `nis-1-transposition-fr` | — |
| 100 | PSSI État 🇫🇷 | `pssi-tat` | — |
| 101 | Checklist de dossier d'homologation 🇫🇷 | `checklist-de-dossier-d-homologation` | — |
| 102 | Cahier des charges Label EBIOS RM v3.1 🇫🇷 | `cahier-des-charges-label-ebios-rm` | — |
| 103 | SecNumCloud v3.2 Annexe 2 : recommandations aux commanditaires ☁️🇫🇷 | `secnumcloud-v3-2-annexe-2` | — |
| 104 | CCB CyberFundamentals Small - Self assessment 🇧🇪 | `ccb-cyberfundamentals-small-self` | — |
| 105 | Mitre ATT&CK v18.1 - Threat catalog 🌐 | `mitre-att-ck-v18-1-threat` | — |
| 106 | Mitre D3FEND - Reference controls 🌐 | `mitre-d3fend-reference-controls` | — |
| 107 | OWASP Top 10 Web - Threat catalog 🐝🌐 | `owasp-top-10-web-threat-catalog` | — |
| 108 | OWASP MAS Threat Modelling Guide - Threat catalog 🐝📱 | `owasp-mas-threat-modelling-guide-threat` | — |
| 109 | CISA Cybersecurity Performance Goals (CPG) v2.0 🇺🇸 | `cisa-cybersecurity-performance-goals-cpg` | — |
| 110 | ANSSI : Référentiel Cyber France pour la réglementation NIS2 (ReCyF) 🇫🇷 | `anssi-r-f-rentiel-cyber-france` | — |
| 111 | Cadre Conformité Cyber France (3CF) v3.1 (2026) ✈️🇫🇷 | `cadre-conformit-cyber-france-3cf-v3` | — |
| 112 | Règles OIV - Secteur « Transport aérien » (2016) ✈️🇫🇷 | `r-gles-oiv-secteur-transport-a` | — |
| 113 | IEC 62443 series — parts 2-1, 2-4, 3-2, 3-3, 4-1, 4-2 🏭🌐 | `iec-62443-series-parts-2-1` | ✅ |
| 114 | CER Directive (Critical Entities Resilience) 🇪🇺 | `cer-directive-critical-entities` | — |
| 115 | EUDI ARF — EU Digital Identity Wallet High-Level Requirements (Annex 2.02) 🇪🇺 | `eudi-arf-eu-digital-identity-wallet` | — |
| 116 | UK Defence Standard 05-138 Issue 4 🇬🇧 | `uk-defence-standard-05-138-issue` | — |
| 117 | Référentiel HAS - Certification des établissements de santé pour la qualité des soins 🇫🇷🏥 | `r-f-rentiel-has-certification-des` | — |
| 118 | Personal Data Protection Law (PDPL) 🇸🇦 | `personal-data-protection-law-pdpl` | — |
| 119 | NCSC - Cyber Assessment Framework (CAF) v4.0 🇬🇧 | `ncsc-cyber-assessment-framework-caf-v4` | — |
| 120 | Algemene Beveiligingseisen voor Rijksoverheidsopdrachten (ABRO) 2026 🇳🇱 | `algemene-beveiligingseisen-voor` | — |
| 121 | PGSSI-S (Politique Générale de Sécurité des Systèmes d'Information de Santé) 🇫🇷 | `pgssi-s-politique-g-n-rale` | — |
| 122 | ANSSI : Recommandations de configuration d'un système GNU/Linux 🇫🇷 | `anssi-recommandations-de-configuration-d` | — |
| 123 | PSSI-MCAS (Politique de sécurité des systèmes d'information pour les ministères chargés des affaires sociales) 🇫🇷 | `pssi-mcas-politique-de-s-curit` | — |
| 124 | ANSSI : Recommandations pour la protection des systèmes d'information essentiels 🇫🇷 | `anssi-recommandations-pour-la-protection` | — |
| 125 | ANSSI : Recommandations de sécurité pour l'architecture d'un système de journalisation 🇫🇷 | `anssi-recommandations-de-s-curit-pour-2` | — |
| 126 | ANSSI : Recommandations de sécurité relatives à TLS 🇫🇷 | `anssi-recommandations-de-s-curit` | — |
| 127 | New Zealand Information Security Manual (NZISM) 🇳🇿 | `new-zealand-information-security-manual` | — |
| 128 | Clausier de sécurité numérique du Club RSSI Santé 🇫🇷 | `clausier-de-s-curit-num-rique` | — |
| 129 | Référentiel National de Sécurité de l'Information (RNSI), MPT Algérie 🇩🇿 | `r-f-rentiel-national-de-s` | — |
| 130 | Misure minime di sicurezza ICT per le pubbliche amministrazioni, AGID Italia 🇮🇹 | `misure-minime-di-sicurezza-ict-per` | — |
| 131 | Framework Nazionale CyberSecurity v2, FNCS Italia 🇮🇹 | `framework-nazionale-cybersecurity-v2` | — |
| 132 | Framework Nazionale per la Cybersecurity e la Data Protection, ACN Italia 🇮🇹 | `framework-nazionale-per-la-cybersecurity` | — |
| 133 | PSSIE du Bénin, ANSSI Bénin 🇧🇯 | `pssie-du-b-nin-anssi-b` | — |
| 134 | IGI 1300 / II 901 - Liste des exigences pour la mise en oeuvre d'un SI classifié (ANSSI) 🇫🇷 | `igi-1300-ii-901-liste-des` | — |
| 135 | Référentiel Général de Sécurité 2.0 - Annexe B2 🇫🇷 | `r-f-rentiel-g-n-ral` | — |
| 136 | Recommandations sur la sécurisation des systèmes de contrôle d'accès physique et de vidéoprotection 🇫🇷 | `recommandations-sur-la-s-curisation-des` | — |
| 137 | Recommandations pour un usage sécurisé d'(Open)SSH 🇫🇷 | `recommandations-pour-un-usage-s-curis` | — |
| 138 | Recommandations de sécurité relatives à IPsec pour la protection des flux réseau 🇫🇷 | `recommandations-de-s-curit-relatives` | — |
| 139 | Recommandations relatives à l'interconnexion d'un système d'information à internet 🇫🇷 | `recommandations-relatives-l-2` | — |
| 140 | Guides des mécanismes cryptographiques 🇫🇷 | `guides-des-m-canismes-cryptographiques` | — |
| 141 | Swift Customer Security Controls Framework (CSCF) v2025 🏦🌐 | `swift-customer-security-controls` | — |
| 142 | OWASP Application Security Verification Standard (ASVS) 5 🐝🖥️ | `owasp-asvs5` | ✅ |
| 143 | NIST 800-82 (OT) - appendix 🏭🤖 | `nist-800-82-ot-appendix` | — |
| 144 | RBI Master Direction 2023 - india 🏦🇮🇳 | `rbi-master-direction-2023-india` | — |
| 145 | Loi 05-20 relative à la cybersécurité (Maroc) 🇲🇦 | `loi-05-20-relative-la-cybers` | — |
| 146 | Lithuanian NIS2 Cybersecurity Law (Kibernetinio saugumo įstatymas) 🇱🇹 | `lithuanian-nis2-cybersecurity-law` | — |
| 147 | Prestataire d'audit de sécurité des systèmes d'information (PASSI) 🇫🇷 | `prestataire-d-audit-de-s-curit` | — |
| 148 | ANS Programme CaRE - Domaine 2 (Continuité et reprise d'activité, sauvegarde) 🇫🇷🏥 | `ans-programme-care-domaine-2-continuit` | — |
| 149 | ANS HospiConnect HOP'EN2 (Sécurisation de l'accès au SIH) 🇫🇷🏥 | `ans-hospiconnect-hop-en2-s-curisation` | — |
| 150 | Loi n° 09-08 relative à la protection des personnes physiques 🇲🇦 | `loi-n-09-08-relative-la` | — |
| 151 | Checklist des exigences de la Loi n° 09-08 🇲🇦 | `checklist-des-exigences-de-la-loi` | — |
| 152 | Référentiel des exigences de qualification des prestataires de services cloud ☁️🇲🇦 | `r-f-rentiel-des-exigences-de` | — |
| 153 | AI Defense Matrix 🤖🌐 | `ai-defense-matrix` | — |
| 154 | Zero Trust for Operational Technology (ZT OT) 🇺🇸🏭 | `zero-trust-for-operational-technology-zt` | — |
| 155 | T.C. CBDDO Bilgi ve İletişim Güvenliği Rehberi (BİGR) 🇹🇷 | `t-c-cbddo-bilgi-ve-leti` | — |
| 156 | GitHub Security Controls 🐙 | `github-security-controls` | — |
<!-- markdownlint-enable MD013 -->
## Installation
You do **not** have to be a developer to run the viewer — it is a single,
self-contained file. Pick the easiest option for you.
### Option A — `cargo binstall` or `cargo install` (if you have Rust)
If [Rust](https://www.rust-lang.org/tools/install) 1.93 or newer is installed,
the fastest path is [`cargo binstall`](https://github.com/cargo-bins/cargo-binstall),
which downloads the prebuilt binary instead of compiling it:
```sh
cargo binstall grundschutz-oscal-viewer # downloads the prebuilt binary, no build
```
Or build it from source with plain `cargo install` (compiles locally):
```sh
cargo install grundschutz-oscal-viewer
grundschutz-oscal-viewer # then open https://127.0.0.1:8228
```
Either way the binary lands in `~/.cargo/bin` (already on your `PATH`). The
difference is just how it gets there: `binstall` fetches the prebuilt binary,
while `install` compiles it from source.
### Option B — download the ready-made binary (no Rust needed)
Every release ships prebuilt binaries on the
[**Releases** page](https://gitlab.com/vPierre/ndaal_public_bsi_grundschutz_oscal_viewer/-/releases).
Download the **one file** that matches your system:
| Your system | File to download |
| --- | --- |
| **Windows** 10/11 (most PCs) | `…-x86_64-pc-windows-gnu.exe` |
| **Windows** on ARM | `…-aarch64-pc-windows-gnullvm.exe` |
| **Linux** (most PCs/servers) | `…-x86_64-unknown-linux-musl` |
| **Linux** on ARM (e.g. Raspberry Pi 64-bit) | `…-aarch64-unknown-linux-musl` |
| **macOS** Intel | `…-x86_64-apple-darwin` |
| **macOS** Apple Silicon (M1/M2/M3/M4) | `…-aarch64-apple-darwin` |
Not sure? On Windows take the first row; on a Mac from 2020 or later take Apple
Silicon; on a normal Linux PC take the first Linux row. (Optionally verify the
download against the release's `SHA256SUMS`.)
#### Windows
1. Download `…-x86_64-pc-windows-gnu.exe` (rename it to
`grundschutz-oscal-viewer.exe` if you like).
2. Double-click it. If SmartScreen says *"Windows protected your PC"*, click
**More info → Run anyway** — the binary is just unsigned.
3. A console window opens and stays open while the viewer runs.
#### Linux
```sh
chmod +x grundschutz-oscal-viewer-*-unknown-linux-musl # make it runnable (once)
./grundschutz-oscal-viewer-*-unknown-linux-musl # run it
```
#### macOS
```sh
chmod +x grundschutz-oscal-viewer-*-apple-darwin # once
xattr -d com.apple.quarantine grundschutz-oscal-viewer-*-apple-darwin # clear the download flag
./grundschutz-oscal-viewer-*-apple-darwin # run it
```
If macOS still says the developer cannot be verified, open **System Settings →
Privacy & Security**, scroll down, and click **Open Anyway**.
### Open it in your browser
However you started it, the viewer prints `listening on https://127.0.0.1:8228`
and keeps running. Now:
1. Open a web browser (Chrome, Firefox, Edge, Safari…).
2. Go to **<https://127.0.0.1:8228>**.
3. It is HTTPS with a self-signed certificate regenerated at every start, so the
browser warns **once** — click **Advanced → Proceed / Continue to
127.0.0.1**. This is expected and safe: the connection never leaves your
computer.
4. You should see the home page:

To **stop** the viewer, press `Ctrl-C` in its window (or just close it). Nothing
is written to disk and no data leaves your machine.
> Port 8228 already in use? Start with another one:
> `grundschutz-oscal-viewer --port 9000` → then open `https://127.0.0.1:9000`.
## Build
Requires Rust 1.93 or newer.
```sh
cargo build --release
# binary: target/release/grundschutz-oscal-viewer (~8 MB, self-contained)
```
### Cross-platform release builds
With [cargo-zigbuild](https://github.com/rust-cross/cargo-zigbuild) and
Zig installed, one command builds all six supported platforms and
writes the binaries plus a `SHA256SUMS` file into `dist/`:
```sh
just build-all # or: bash release/build_release_targets.sh
```
| Target | Artifact |
| --- | --- |
| Linux x86_64 (static, musl) | `grundschutz-oscal-viewer-<v>-x86_64-unknown-linux-musl` |
| Linux aarch64 (static, musl) | `grundschutz-oscal-viewer-<v>-aarch64-unknown-linux-musl` |
| Windows x86_64 | `grundschutz-oscal-viewer-<v>-x86_64-pc-windows-gnu.exe` |
| Windows aarch64 | `grundschutz-oscal-viewer-<v>-aarch64-pc-windows-gnullvm.exe` |
| macOS x86_64 | `grundschutz-oscal-viewer-<v>-x86_64-apple-darwin` |
| macOS aarch64 (Apple Silicon) | `grundschutz-oscal-viewer-<v>-aarch64-apple-darwin` |
## Usage
```sh
grundschutz-oscal-viewer # serve https://127.0.0.1:8228 (TLS 1.3)
grundschutz-oscal-viewer --port 9000 # custom port (env: GSV_PORT)
grundschutz-oscal-viewer --bind 0.0.0.0 # non-localhost bind (env: GSV_BIND) — your decision
grundschutz-oscal-viewer --export DIR # write the embedded catalog JSON into DIR, then exit
grundschutz-oscal-viewer --export-dir DIR # target dir for the Export menu (env: GSV_EXPORT_DIR)
grundschutz-oscal-viewer --meili-url "" # disable the default-on Meilisearch client
grundschutz-oscal-viewer --db notes.db --encryption-key-file key.bin # encrypt annotations at rest (env: GSV_ENCRYPTION_KEY_FILE)
grundschutz-oscal-viewer --encryption-key-file new.bin --retired-encryption-key-file old.bin # rotate the key; old values stay readable
grundschutz-oscal-viewer --db notes.db --clamav-db-dir /srv/clamav-db # offline ClamAV signature DB for clamscan (env: GSV_CLAMAV_DB_DIR)
grundschutz-oscal-viewer --tls-cert cert.pem --tls-key key.pem # present a CA certificate (env: GSV_TLS_CERT / GSV_TLS_KEY)
grundschutz-oscal-viewer --log-format json # structured JSON logs (env: GSV_LOG_FORMAT)
grundschutz-oscal-viewer --restore-dump dump.sql.gz --db new.db # rebuild a database from a dump, then exit
grundschutz-oscal-viewer --check-update # is a newer GitLab release available? (read-only)
grundschutz-oscal-viewer --self-update # download + SHA-256-verify + replace this binary, then exit
grundschutz-oscal-viewer --version
grundschutz-oscal-viewer --help
```
`--self-update` fetches the latest release for the running target triple from
`gitlab.com/vPierre/ndaal_public_bsi_grundschutz_oscal_viewer` over TLS 1.3
(the same aws-lc-rs stack as the server), verifies the download against the
committed `release/SHA256SUMS`, and atomically replaces the executable. It
never downgrades and never installs on a checksum mismatch; `--check-update`
only reports and treats an unreachable host as "up to date, could not check",
never an error. Set `--no-self-update` (or `GSV_NO_SELF_UPDATE=1`) to forbid
`--self-update` entirely — for package-managed or locked-down installs.
`--export` (one-shot) and the **Export** menu both open their target
directory as a capability (`cap-std`) and create files with `create_new`
semantics: existing files are never overwritten and no path outside the
directory can be reached. The Export menu additionally offers Markdown,
ODT and PDF (via the pure-Rust `lo_writer`), a ZIP bundle of the selected
formats (via `lo_zip`), plus checksum sidecars, scoped to the whole catalog,
one practice or one control.
The viewer serves **HTTPS only** (TLS 1.3, rustls). The self-signed
certificate is regenerated at every start-up, so a browser shows a
one-time "not trusted" warning on first connect — expected for a
self-signed localhost cert. Use `curl -k` for scripted access.
### Meilisearch (opt-in)
```sh
# Meilisearch is disabled by default; pass a URL to enable it:
grundschutz-oscal-viewer --meili-url http://localhost:7700 # enable a local instance
grundschutz-oscal-viewer --meili-url http://localhost:7700 --meili-key <key> # add an API key if it needs one
grundschutz-oscal-viewer --meili-url https://host:7700 # remote instance over TLS
# without --meili-url / MEILI_URL the built-in search is used
# or via environment: MEILI_URL / MEILI_MASTER_KEY
```
At startup the viewer pushes all controls into the `grundschutz` index
and switches `/search` to Meilisearch once indexing succeeded. If the
instance is unreachable, the built-in search stays active — the binary
never depends on it. The Meilisearch client is **rustls-protected**: it
uses HTTPS for any non-loopback host and negotiates **TLS 1.3 only**;
plain `http://` is accepted exclusively for loopback addresses
(`localhost` / `127.0.0.1` / `::1`). The viewer's own listener serves
TLS 1.3 via rustls.
## Routes
| Route | Content |
| --- | --- |
| `/healthz`, `/readyz` | Liveness / readiness probes (plain text, un-gated) |
| `/lang/{code}` | UI language switcher (`en` / `de` / `fr`) — sets the `gsv_lang` cookie, returns to the referring page |
| `/` | Hero search, stat cards, practice overview |
| `/controls` | Filterable, paginated control list |
| `/control/{id}` | Every field of one control + raw OSCAL JSON |
| `/control/{id}/raw.json` | Raw control JSON |
| `/practices`, `/practice/{id}` | Practice (domain) pages |
| `/search`, `/search/partial` | Full-text search (page / HTMX partial) |
| `/stats` | Catalog-wide distributions |
| `/metadata` | OSCAL metadata + back-matter, raw |
| `/export`, `/export/run`, `/export/download` | Export menu (JSON / Markdown / ODT / PDF / ZIP bundle + sidecars); *run* writes server-side, *download* streams to the browser |
| `/export/dumps/download`, `/export/dumps/delete` | Database-dump management (download / same-origin delete), confined to the dump root |
| `/catalog.json` | The embedded catalog, byte-for-byte |
| `/about`, `/system-info`, `/privacy`, `/security`, `/license`, `/imprint`, `/changelog` | Info menu |
| `/annotations`, `/annotations/...` | Annotations workspace (save / preview / upload / asset / template) — only with `--db` |
| `/readme`, `/administrator`, `/user` | Embedded README + guides |
| `/static/{path}` | Embedded UI assets (Bootstrap, icons, HTMX, fonts, logo) |
## Documentation
| Document | What it covers |
| --- | --- |
| [Tutorials](documentation/tutorials/README.md) | Task-oriented how-tos: cut a release, bump the version, write a CSAF advisory, run the quality gates, author a bash gate, back up/restore the database, verify TLS 1.3 / PQC, run the security scans |
| [User Guide](documentation/user_guide.md) | Day-to-day use of the viewer (also served at `/user`) |
| [Administrator Guide](documentation/administrator_guide.md) | Deployment, CLI flags, secrets, backup/restore (also at `/administrator`) |
| [Architecture (arc42)](documentation/arc42/README.md) | The 12-section arc42 architecture view |
| [Threat model (STRIDE)](documentation/threat-model/stride.md) | Per-boundary STRIDE analysis, data classification and encryption-at-rest policy, residual-risk register; ships a Microsoft Threat Modeling Tool companion (`.tm7`) |
| [Security decisions](documentation/security/cryptographic-inventory.md) | The OWASP ASVS registry documents: [cryptographic inventory](documentation/security/cryptographic-inventory.md) and [validation architecture](documentation/security/validation-architecture.md) |
| [Codebase wiki](documentation/wiki/en/overview.md) | Navigable code map for contributors and AI agents: [overview](documentation/wiki/en/overview.md), [codemap](documentation/wiki/en/codemap.md), and per-module entity/concept/pattern pages |
| [Framework mappings pack](documentation/framework_mappings_pack.md) | How the mappings are embedded and built (with `--mappings-dir` as an optional extra) |
| [Test sequences](documentation/test_sequences.md) | The layered test suite (Rust, Bruno, Playwright, fuzz, live scans) |
| [Changelog](CHANGELOG.md) | Release history (also served at `/changelog`) |
## Development
```sh
just test # unit + integration tests
just clippy # warnings denied (pedantic)
just qa # all quality gates: fmt, clippy, tests, doc, audit,
# deny, machete, rust-doctor, htmlhint, oxlint
just fuzz-smoke # 10 s per fuzz target (nightly + cargo-fuzz)
just fuzz fuzz_query_parse # one target, 60 s
```
Quality gates are configured at the repository root: `clippy.toml`,
`rustfmt.toml`, `audit.toml`, `deny.toml`, `rust-doctor.toml`,
`.oxlintrc.json` — plus `src/templates/.htmlhintrc` for the template
lint.
### Test coverage
Line coverage is **90.17%** (4 732 / 5 248 lines, `cargo-tarpaulin` 0.37.0),
measured across the workspace with `vendor/` excluded so third-party sources
do not count either way. A blocking `--fail-under 80` gate guards it:
```sh
bash tests/scripts/test_cargo_tarpaulin.sh # writes documentation/rust/cargo_tarpaulin/
```
Excluding `src/main.rs` the library sits at **97.48%** (4 675 / 4 796).
`main.rs` is 57/452 on its own and accounts for 395 of the 516 uncovered
lines — socket binding, TLS listener setup and `process::exit` paths that no
hermetic unit test can reach without a real listener.
The run caps `PROPTEST_CASES`/`QUICKCHECK_TESTS` at 2, because tarpaulin's
instrumentation is 10-50x slower than native and the default 256 cases across
90 properties does not terminate in reasonable time. Line coverage is
identical at 2 cases or 256; the full 256-case run still happens in the
normal `test` and proptest gates.
Beyond the unit/integration suite, several dimensions are layered on:
- **Property-based invariants** (`tests/proptest_invariants.rs`) mirror the
`cargo fuzz` targets, so every untrusted-input parser's invariant is
checked on each `cargo test` run — not only under a dedicated fuzz session.
- **Fuzzing** — `cargo fuzz` targets under `fuzz/fuzz_targets/` cover every
byte-input surface (`just fuzz-smoke`).
- **Model checking** — `kani-harness/` proves 3 harnesses exhaustively over
the whole input space, where proptest only samples: `paginate` is total and
returns a well-formed window for any `(len, page)` in `usize × usize`, the
empty-catalog corner is pinned exactly, and `parse_args` on empty input
yields all defaults. It is a **standalone workspace** that `#[path]`-includes
only the rusqlite-free pure modules — `cargo kani` on the main crate cannot
work, because `libsqlite3-sys`'s build script uses the unstable `cfg_select`
feature that Kani's pinned nightly rejects. Run it from that directory, or
via the opt-in gate:
```sh
QG_RUN_KANI=1 bash tests/scripts/test_cargo_kani.sh # nightly-only, slow
```
String-processing functions are deliberately left to proptest and libFuzzer:
symbolic `&str` forces CBMC to model heap allocation plus full UTF-8 decode,
a state space that exhausts memory rather than terminating.
- **Known-answer & policy tests** — each of the five checksum hash families
(`src/sidecar.rs`) carries a fixed NIST / FIPS-202 / BLAKE3 known-answer
vector; annotation Markdown is checked against an XSS-protocol battery
(`javascript:` / `vbscript:` / `data:` stripped from links and images); and
the start-up TLS certificate is parsed back to enforce the Elliptic-curve
(no-RSA) signature policy and its `localhost` / `127.0.0.1` / `::1` SANs.
- **API / HATEOAS contract** — `tests/scripts/test_hateoas_compliance_grundschutz.sh`
drives the running server (`:8228`) and checks the hypermedia navigation
(in-band nav links, live control -> detail -> `raw.json`, htmx fragments)
and the JSON contract (`/catalog.json`, `/control/{id}/raw.json`); it
self-skips when no server is listening.
### Browser-driven E2E tests
`test/playwright/` runs the whole app through a real Chromium, Firefox,
and WebKit — HTMX live search, framework-toggle persistence, the
language switcher, and the annotation editor's create/preview/save
flow — against a throwaway `--seed-demo` instance it manages itself:
```sh
test/playwright/run.sh # self-skips if no system Chrome is found
```
See `documentation/test_sequences.md` §7 for the full spec-file
breakdown and the config's documented deviations (self-signed dev TLS,
serialized execution over the shared database).
### Framework mappings generator
`scripts/generate_ciso_mappings/` builds the generated crosswalk that is
committed under `data/mappings/` and embedded in the binary (see
[framework mappings pack](documentation/framework_mappings_pack.md)
§4): it reads the public framework requirement catalogues (via the CISO
Assistant community library) as **input**, matches `data/frameworks.csv`
slugs to library files, and embeds both sides with a multilingual
sentence-transformer to produce one similarity-scored CSV per framework.
```sh
cd scripts/generate_ciso_mappings
python3 -m venv .venv && source .venv/bin/activate && pip install -r requirements.txt
python3 download_libraries.py --cache-dir .cache
python3 generate_pack.py --repo-root ../.. --cache-dir .cache --output-dir /path/to/pack
```
The output is a **similarity analysis** — ndaal's own **Apache-2.0** work
product (the requirement catalogues are only read as input to compute the
scores; no third-party source code is reused). The generated CSVs are
committed under `data/mappings/` and embedded in the binary. See the
script's own README for the platform note on pinning `torch==2.2.2`
(the last release with macOS x86_64/Intel wheels) and how to run its
unit tests offline.
## Data source and licenses
- **Code:** Apache-2.0 (see `LICENSE`).
- **Embedded catalog:** Anwenderkatalog Grundschutz++ from the BSI
[Stand-der-Technik-Bibliothek](https://github.com/BSI-Bund/Stand-der-Technik-Bibliothek),
© Bundesamt für Sicherheit in der Informationstechnik, licensed
**CC-BY-SA-4.0** (see `data/grundschutz-plus-plus-catalog.json.license`).
- **Embedded UI assets** (REUSE `.license` sidecars next to each file,
full license texts in `LICENSES/`):
- Bootstrap 5.3.3 and Bootstrap Icons 1.11.3 — MIT,
© The Bootstrap Authors
- htmx 2.0.4 — 0BSD, © Big Sky Software
- Roboto — Apache-2.0, © Google LLC
- Roboto Mono — OFL-1.1, © The Roboto Mono Project Authors
To refresh the embedded catalog, run `scripts/update_catalog.sh` (or
download the current file manually) and rebuild:
```sh
scripts/update_catalog.sh
cargo build --release
```
## Author
Pierre Gronau, ndaal Gesellschaft für Sicherheit in der
Informationstechnik mbH & Co KG, Cologne — <Pierre.Gronau@ndaal.eu>