# ndaal CSAF-CRUD
Secure, single-binary Rust CRUD application for managing
**CSAF 2.0 and 2.1** security advisories. Provides both a
server-side rendered web UI (Hyper + Bootstrap 5 + HTMX) and a
RESTful HATEOAS JSON API under `/api/v1/`.
## Features
- **CSAF 2.0 / 2.1** — Full serde types, strict validation, dual
CVSS v3.1 + v4.0 scoring.
- **Classification support** — TLP 2.0 colour-coded selector
(`CLEAR/GREEN/AMBER/AMBER+STRICT/RED`), German Verschlusssache
(`VS-NfD / VS-Vertr. / Geh. / Str. Geh.`), and NATO
(`NR / NC / NS / CTS`). Written into the exported CSAF either
to `document.distribution.text`, to `document.notes[]`, or to
both — configurable per-deployment.
- **Dual embedded database** — `redb` for CSAF documents,
`rusqlite` (bundled) for user management and audit logging.
- **Zero external dependencies** — Single binary deployment.
- **Protocols** — HTTP/1.1, HTTP/2, and HTTP/3 (QUIC) over
TLS 1.3 via rustls, ring crypto, and quinn.
- **Self-signed certificates** — Auto-generated via rcgen with
45-day validity (Let's Encrypt short-lived cert model).
- **Sidecar hashes** — SHA-256 and SHA3-512 generated for every
export, optional via settings.
- **HATEOAS API** — HAL-like `_links` in every response, RFC 9457
Problem Details error format.
- **Menu structure** — CSAF (CRUD), Administration
(Import/Export), Settings, Info (About/License/System/Privacy/
Security).
- **Audit logging** — Every create/update/delete/import/export
recorded with ISO 8601 timestamps.
- **Argon2id** password hashing per RFC 9106.
## Quick start
```bash
# Build
cargo build --release
# Start the server (TLS on 8180 for TCP, 8181 for QUIC)
cargo run --bin csaf-crud
# Use the CLI
cargo run --bin csaf-cli -- import --directory test/csaf
cargo run --bin csaf-cli -- validate \
test/csaf/2026/003/ndaal-sa-2026-003.json
cargo run --bin csaf-cli -- stats
```
## Workspace layout
```text
crates/
├── csaf-models/ # CSAF 2.0/2.1 serde types, SQLite pool,
│ # User, AuditLog, Settings models
├── csaf-core/ # redb storage, validation, sidecar generation,
│ # import/export, configuration
├── csaf-crud/ # Hyper server, HATEOAS API, HTML routes,
│ # router, static files, TLS 1.3 setup
└── csaf-cli/ # clap-based CLI: import, export, validate,
# stats
```
## Testing
```bash
# All tests (unit + integration + crate integration)
cargo test --workspace
# Just integration tests
cargo test --workspace --test csaf_crud_cycle
cargo test --workspace --test test_crate_integrations
# Load test (oha, HTTP/2 keep-alive, --insecure for dev cert)
OHA_REQUESTS=1000 OHA_CONCURRENCY=20 test/loadtest/run.sh
```
### Load-test results — port 8180, HTTP/2 over TLS 1.3
1000 requests per endpoint × 20 concurrent connections,
`oha --http2 --insecure` against the locally-built release
binary on Apple Silicon (MacBook). All seven endpoints hit
**100 % success**:
| `/api/v1` | 12,037 | 0.74 ms |
| `/api/v1/settings` | 11,844 | 0.77 ms |
| `/api/v1/system/health` | 9,070 | 0.96 ms |
| `/api/v1/csaf?page=1&per_page=5` | 8,781 | 1.64 ms |
| `/` (HTML dashboard) | 7,918 | 2.19 ms |
| `/api/v1/audit-log?page=1&per_page=5` | 7,572 | 2.24 ms |
| `/static/img/logo.png` | 3,076 | 1.50 ms |
Port **8181** (HTTP/3 over QUIC) is not exercised by this
load test — `oha`, `h2load`, and Homebrew `curl` on this
platform do not include an HTTP/3 / QUIC client. A dedicated
QUIC-capable tool (e.g. `h2load` built with `nghttp3` +
`ngtcp2`, or `neqo-client`) is needed to benchmark 8181.
Test coverage includes:
- **Unit tests** per crate (178 tests; +22 for classification)
- **Integration tests** — 10 end-to-end CRUD cycle tests
- **Crate integration tests** — 57 tests reusing patterns from
redb, rusqlite, sha2/sha3, argon2, serde_json, chrono, regex,
uuid, matchit, rustls/rcgen, and sysinfo
- **Fuzzing** — 16 `cargo-fuzz` targets including three new
classification-focused targets
(`fuzz_classification_extract`,
`fuzz_classification_writeback`,
`fuzz_tlp_color_class`). Parser changes are gated on 300 s
per-target in CI.
Total: 245 tests passing.
## Quality toolchain
```bash
cargo fmt --all
cargo clippy --workspace --all-targets --all-features -- \
-D warnings
cargo test --workspace
cargo audit
cargo deny check
cargo machete --with-metadata
cargo llvm-cov --lcov --output-path target/coverage/lcov.info \
--workspace --all-features
rust-doctor
cargo kani --workspace --output-format=terse # nightly
```
Linters applied: `cargo clippy` (pedantic + nursery),
`yamllint`, `ryl`, `markdownlint`, `rumdl`, `hadolint`,
`htmlhint`, `oxlint`, `ruff`, `bandit`.
### rust-doctor — workspace health score
```text
┌────────────────────────────────────────────────────────┐
│ rust-doctor │
│ │
│ 100 / 100 Great │
│ │
│ ████████████████████████████████████████ │
│ │
│ Security: 100 │
│ Reliability: 100 │
│ Maintainability: 100 │
│ Performance: 100 │
│ Dependencies: 100 │
│ │
│ ✓ 0 error(s) ✓ 0 warning(s) ℹ 1 info(s) 38 files │
└────────────────────────────────────────────────────────┘
```
Run `rust-doctor` locally (or `rust-doctor --json` for machine
output) to reproduce. The session-level cache lives in each
crate's `.rust-doctor-cache.json`; any regression greater than
5 points vs the previous run blocks the merge. Configuration
is in [`rust-doctor.toml`](rust-doctor.toml); every ignore rule
carries a justification comment referencing the equivalent
`[workspace.lints.clippy]` entry in
[`Cargo.toml`](Cargo.toml).
## Security
- TLS 1.3 only (no TLS 1.2 fallback)
- rustls with ring crypto provider (no OpenSSL)
- Argon2id for password hashing (RFC 9106)
- Parameterized SQL queries throughout
- No unsafe code (`#![deny(unsafe_code)]`)
- SHA-256 and SHA3-512 integrity sidecars
## Source code
Canonical repository (used by crates.io and `cargo install`):
- <https://gitlab.com/vPierre/ndaal_public_csaf_crud>
Please open issues and merge requests there. No GitHub mirror is
maintained; treat any third-party GitHub copy as untrusted.
## License
Apache-2.0. See SPDX headers in each source file.
## Author
Pierre Gronau · ndaal Gesellschaft für Sicherheit in der
Informationstechnik mbH & Co KG · Cologne