User Guide

<!-- SPDX-License-Identifier: Apache-2.0 -->
<!-- SPDX-FileCopyrightText: 2026 ndaal GmbH & Co KG, Cologne -->

# User Guide

How to manage CSAF security advisories with ndaal CSAF-CRUD, through
both the web interface and the JSON API. For configuration and
operations see the [Administrator Guide](Administrator_Guide.md); for
the complete command/endpoint catalogue see
[REFERENCE.md](../REFERENCE.md).

## What this application does

ndaal CSAF-CRUD lets you create, edit, validate, export, and import
CSAF 2.0 / 2.1 security advisories (security advisories, VEX
documents, and informational advisories). It records dual CVSS v3.1
and v4.0 scores and supports TLP 2.0, German Verschlusssache, and
NATO classification labels.

## Getting started

Open the web UI in a browser:

```text
https://127.0.0.1:8180/
```

The development server uses a self-signed certificate, so your
browser shows a one-time warning — accept the exception to continue.

The dashboard at `/` shows document counts by category, a configured
summary, and the most recent audit-log activity.

> Note: this release has no login. Every page and API route is open
> to anyone who can reach the server. See the
> [Administrator Guide](Administrator_Guide.md#authentication-and-access-control).

## Web interface

### Navigation bar

Every page starts with the same white navigation bar. The logo at the
left links to the dashboard. The page links (Dashboard, CSAF, Import,
Export) follow it, each with an icon; the current page is underlined and
marked for screen readers. Settings, the Theme menu, the Language menu
and the Info menu sit at the right.

- **1024 px wide and above:** all of these controls show in one row,
  with no menu button.
- **Below 1024 px:** the controls collapse behind a menu button (a
  hamburger icon). Its accessible name is "Toggle navigation", which is
  currently English in every interface language. The logo stays
  visible.

To reach the page links on a narrow screen:

1. Select the menu button. With the keyboard, move focus to it and
   press Enter or Space.
2. In the list that opens, choose a page link, or open the Theme,
   Language or Info menu.
3. Select the menu button again to close the list.

The menu buttons need no JavaScript, so they also work when scripting is
disabled. With scripting on, **Escape** closes an open menu and returns
focus to its button, and selecting anywhere else closes it. On a wide
screen the language list is laid out in columns of ten; on a narrow
screen it is one scrollable column.

In releases 1.5.23 and 1.5.24 current browsers showed only the logo on a
wide window. That is fixed in 1.5.25; see "Only the
logo shows in the navigation bar" in the Troubleshooting Guide.

### Light, dark and system theme

The **Theme** menu offers three choices: **System** (follow your
operating system), **Light** and **Dark**. Pick one and the page changes
at once. The choice is stored in a cookie in your browser, so it applies
only to you, on every page, for a year. Without a choice, the page uses
the default an administrator set under Settings. Without JavaScript,
pick an option and select **Apply**.

### Keyboard and accessibility

The interface follows the ndaal design standard, which targets WCAG 2.2
level AA. Every control can be used with the keyboard; the focused
control shows a two-ring outline. Controls are at least 44 px high,
status is never shown by colour alone (success, error and note boxes also
carry an icon and a word for screen readers), and the layout reflows to a
320 px wide window without sideways page scrolling. Wide tables scroll
inside their own region, which you can focus and scroll with the arrow
keys. The page respects your operating system's reduced-motion,
high-contrast and forced-colours settings. Destructive actions such as
deleting an advisory ask for confirmation in a dialog that you can
dismiss with **Escape**.

### Listing advisories

`/csaf` shows a paginated table (Tracking ID, Title, Category,
Status, CVSS v3, CVSS v4, Release Date). Use the Previous/Next
controls to page through results.

### Creating an advisory

1. Go to `/csaf` and choose **New**, or open `/csaf/new`.
2. Fill in the form. Required fields are marked; the main groups are:
   - **Document:** tracking ID, title, category
     (`csaf_security_advisory`, `csaf_vex`,
     `csaf_informational_advisory`), status (`draft`, `interim`,
     `final`), version, initial and current release dates.
   - **Classification:** TLP label (required) plus optional
     Verschlusssache and NATO labels when enabled in settings.
   - **Publisher:** name, namespace, category, contact details.
   - **Product:** vendor, product name, product ID, product status.
   - **Vulnerability:** CVE, title, description, CWE — required for a
     security advisory, optional for VEX and informational.
   - **Scores:** CVSS v3.1 and v4.0 base score, severity, and vector.
3. Submit. The document is validated, stored, and (if export is
   configured) written to disk with hash sidecars.
4. On a validation failure the form is redisplayed with your input
   preserved and an error banner listing each problem field. Fix and
   resubmit.

### Viewing, editing, deleting

- `/csaf/{id}` shows the metadata, the vulnerability table, and the
  raw JSON, with buttons to edit, download JSON, or delete.
- `/csaf/{id}/edit` reloads the form pre-filled from the stored
  document, including the classification labels.
- Delete asks for confirmation, then removes the document and records
  an audit entry. The confirmation prompt, like the one on
  **Reset to defaults**, comes from the page script. With scripting
  disabled the browser does not ask, and the request goes through
  straight away.

### Downloading JSON

`/csaf/{id}/json` returns the raw CSAF JSON document for the advisory.

### Settings

`/settings` controls runtime behaviour: CSAF export mode (2.0 / 2.1),
theme, the import/export/dump/log directories, the naming convention,
publisher defaults, the default TLP / Verschlusssache / NATO labels,
where classification labels are written, and the five hash-sidecar
toggles. **Reset to defaults** restores everything. The Theme setting
here is only the default for visitors who have not chosen a theme in the
header's Theme menu.

### Import and export (admin pages)

- `/admin/import` scans the import directory and stores every valid
  `*.json` advisory (`provider-metadata.json` is skipped). The result
  page reports imported, skipped, and per-file errors. Import is
  lenient — a document with OASIS conformance warnings is still
  admitted, with the warnings logged.
- `/admin/export` writes every stored document to the export
  directory with the enabled sidecars. Export is strict — see
  validation below.
- The export page also offers **Dump Database** (a consistent
  snapshot) and **Export Audit Log** (Markdown, CSV, JSON, SARIF).

Every exported document, database dump, and audit-log export is
written with up to five checksum sidecars next to it: `.sha-256`,
`.sha-512`, `.sha3-512`, `.blake3-512`, and `.shake256-512`. Switch
each one on or off in **Settings**. The export page shows which are
enabled. While an import, export, database dump, or audit-log export
runs, its button is disabled and shows a busy label.

### Info pages

`/info/about`, `/info/license`, `/info/system`, `/info/privacy`, and
`/info/security` describe the application, its licence, host and
runtime details, data handling, and the security posture.

## The advisory lifecycle and validation

Validation runs on create, update, import, and export. Findings have
two severities:

- **Error (hard):** blocks the operation. In the web UI the form
  redisplays with the errors; via the API you get `422` with an
  `errors` array.
- **Warning (soft):** reported but non-blocking.

Export adds a second, stricter gate: when the `oasis-validator`
feature is enabled (default), the official OASIS CSAF 2.1 suite must
also pass, or the export of that document is refused. A common cause
is setting `csaf_mode` to `2.0` while a document is structurally 2.1
(it carries `metrics` / `cvss_v4`): relabelling it `2.0` makes it
non-conformant and export refuses it. Keep `csaf_mode` aligned with
your documents.

You can re-validate any stored document on demand with
`GET /api/v1/csaf/{id}/validate`, which returns `valid`,
`error_count`, `warning_count`, and the `findings`.

## Classification labels

Three label families are supported:

- **TLP 2.0:** `CLEAR`, `GREEN`, `AMBER`, `AMBER+STRICT`, `RED`.
- **Verschlusssache (German):** `VS-NfD`, `VS-Vertr.`, `Geh.`,
  `Str. Geh.`.
- **NATO:** `NR`, `NC`, `NS`, `CTS`.

Where the labels are written into the exported CSAF is controlled by
`classification_storage_mode`: into `document.distribution.text`,
into `document.notes[]`, or `both`. On edit, the labels are read back
from the stored document so they round-trip. See
[classification.md](classification.md) for the full feature detail.

## JSON API

The HATEOAS JSON API lives under `/api/v1`. Responses are JSON with a
`_links` object; errors are RFC 9457 `application/problem+json`. The
full contract is [specs/openapi.yaml](../specs/openapi.yaml).

List advisories:

```bash
curl -k "https://127.0.0.1:8180/api/v1/csaf?page=1&per_page=20"
```

Retrieve one:

```bash
curl -k https://127.0.0.1:8180/api/v1/csaf/ndaal-sa-2026-003
```

Create one:

```bash
curl -k -X POST https://127.0.0.1:8180/api/v1/csaf \
  -H "Content-Type: application/json" \
  --data @advisory.json
# 201 Created, Location: /api/v1/csaf/<tracking-id>
```

Validate one:

```bash
curl -k https://127.0.0.1:8180/api/v1/csaf/ndaal-sa-2026-003/validate
```

Other endpoints: `PUT`/`DELETE` on `/api/v1/csaf/{id}`,
`/api/v1/audit-log`, `/api/v1/settings`, `/api/v1/system/info`, and
`/api/v1/system/health`. See
[REFERENCE.md](../REFERENCE.md#http-api).

`-k` (insecure) is needed only while the development self-signed
certificate is in use.

## Command-line interface

`ndaal-csaf-cli` performs the same import/export/validate operations
headlessly:

```bash
ndaal-csaf-cli stats
ndaal-csaf-cli validate path/to/advisory.json
ndaal-csaf-cli import  --directory ./data_import
ndaal-csaf-cli export  --directory ./data_export
```

Pass `--config <PATH>` to use a non-default configuration file. See
[REFERENCE.md](../REFERENCE.md#cli-commands) for flags and exit codes.

Are you sure?