grundschutz-oscal-viewer 1.6.40 — release announcement: CMMI Maturity Level field on annotations (six levels 0 Incomplete … 5 Innovating), the exhaustive fifteen-version --seed-demo / test sequence walking the maturity level, full endpoint Bruno coverage and proptest/fuzz/cargo-mutants/Miri verification, and a parse_args complexity refactor; the release also carries fifteen Low-severity hardening fixes tracked in ndaal-sa-2026-234 through -248

Edit JSON

Metadata

Tracking IDndaal-sa-2026-233
Categorycsaf_security_advisory
CSAF Version2.1
Statusfinal
Version1.1.0
Publisherndaal Gesellschaft für Sicherheit in der Informationstechnik mbH & Co KG
TLPCLEAR
Initial Release2026-07-07T00:00:00.000Z
Current Release2026-07-08T00:00:00.000Z

Vulnerabilities (1)

CVETitleCVSS v3CVSS v4
-grundschutz-oscal-viewer 1.6.40 changeset — Maturity Level feature + test coverage + parse_args complexity fix (the 5 Low-severity hardening fixes in the same 1.6.40 cut are tracked in ndaal-sa-2026-234..238)0.00.0

Raw JSON

{
  "$schema": "https://docs.oasis-open.org/csaf/csaf/v2.1/schema/csaf.json",
  "document": {
    "category": "csaf_security_advisory",
    "csaf_version": "2.1",
    "distribution": {
      "tlp": {
        "label": "CLEAR"
      }
    },
    "lang": "en",
    "notes": [
      {
        "category": "summary",
        "text": "grundschutz-oscal-viewer 1.6.40 is a feature, test-coverage and code-quality release on top of the 1.6.39 line, published to crates.io and released on GitLab as v1.6.40 on 2026-07-08. It adds a CMMI-style Maturity Level picker to the annotation editor (/annotations) with the six levels 0 Incomplete, 1 Performed, 2 Managed, 3 Established, 4 Predictable and 5 Innovating (default 1 Performed); each saved annotation version stores its own maturity level in a new maturity column added by an idempotent migration. The opt-in --seed-demo seeder and its canonical test sequence were extended to fifteen append-only versions whose V10–V15 walk the maturity level. It further expands the automated test surface (full router-endpoint Bruno coverage, Maturity property tests, a fuzz_store_maturity cargo-fuzz target, Playwright maturity coverage) and refactors parse_args to clear a cyclomatic-complexity gate. The release also carries fifteen Low-severity hardening fixes tracked individually in ndaal-sa-2026-234 through ndaal-sa-2026-248. None of these introduce or fix a vulnerability in the shipped viewer; 1.6.40 is scored CVSS 0.0 (NONE)."
      },
      {
        "category": "description",
        "text": "This advisory is the consolidated release announcement for grundschutz-oscal-viewer 1.6.40. The release was published to crates.io (`cargo install grundschutz-oscal-viewer`, or `cargo binstall grundschutz-oscal-viewer` for the prebuilt GitLab binary) and tagged on GitLab as v1.6.40 (commit bc74db8c) on 2026-07-08. It bundles the CMMI Maturity Level annotation feature, the fifteen-version demo/test sequence (V10–V15 walk the maturity level 2 → 3 → 4 → 5 → 1 → 4, with Level 5 named “Innovating”), and fifteen Low-severity hardening fixes individually tracked in ndaal-sa-2026-234 through ndaal-sa-2026-248. 1.6.40 collects no scored findings of its own. The complete change set is reproduced verbatim from the project CHANGELOG [1.6.40] section below.\n\n## [1.6.40] - 2026-07-08\n\n### Added\n\n- **Maturity level on annotations.** The annotation editor\n  (`/annotations`) now has a **Maturity Level** picker between the Modal and\n  Status controls, with the six CMMI-style levels — 0 Incomplete, 1\n  Performed, 2 Managed, 3 Established, 4 Predictable, 5 Innovating. The\n  default is **Level 1 — Performed**. Each saved version stores its level (a\n  new `maturity` column on the `versions` table, added to existing databases\n  by an idempotent migration); the picker pre-selects the shown version's\n  level. `Store::save` keeps its signature and defaults to Performed;\n  `Store::save_with_maturity` is the new explicit path.\n\n### Security\n\n- **Upload malware-scan fail-open closed (`scan.rs`).** The scanner subprocess\n  wrapper piped stdout but never drained it, so a scan that wrote more than the\n  OS pipe buffer (~64 KiB) — e.g. a YARA run reporting many matches — blocked on\n  its own write, was force-killed at the 30 s deadline, and treated as **clean**,\n  admitting exactly the most heavily-flagged uploads. stdout is now drained on a\n  dedicated thread so the child finishes and its exit code decides the verdict.\n  Advisory `ndaal-sa-2026-234`.\n- **`GSV_ALLOW_NON_LOOPBACK` is now trimmed (`main.rs`).** A whitespace-polluted\n  falsy value such as `\"0\\n\"` (from a heredoc or file) compared unequal to `\"0\"`\n  and silently opted into a non-loopback bind, weakening the loopback safety\n  guard. The value is trimmed before the `\"\"`/`\"0\"` check. Advisory\n  `ndaal-sa-2026-238`.\n- **Word-form falsy env values now respected (`cli.rs`, `main.rs`).** Building on\n  the trim fix above, `GSV_ALLOW_NON_LOOPBACK=false` (or `no`/`off`) still lifted\n  the loopback guard, because the only excluded values were `\"\"` and `\"0\"`. Every\n  boolean `GSV_*` toggle now parses through one helper (`cli::env_flag_on`) whose\n  falsy set is `{\"\", \"0\", \"false\", \"no\", \"off\"}` (case-insensitive, trimmed), so\n  an operator writing `=false` to *disable* the opt-out no longer accidentally\n  enables it. The same helper governs `GSV_NO_FETCH_YARA_RULES`. Advisory\n  `ndaal-sa-2026-239`.\n- **Meilisearch HTTP exchanges are now time-bounded (`meili.rs`).** The optional\n  Meilisearch client had no wall-clock timeout, so a backend that accepted the\n  connection but never replied (or stalled mid-body) hung the `/search` handler\n  forever — and the documented fall-back to the built-in engine, which only\n  fires on a returned error, never engaged. Every exchange (connect + send +\n  receive) is now capped at 10 s, so a stall degrades to the built-in engine.\n  Meilisearch is opt-in (`--meili-url`). Advisory `ndaal-sa-2026-244`.\n\n### Fixed\n\n- **NUL bytes survive a SQL dump/restore (`store.rs`).** A note body with a NUL\n  was emitted as a raw quoted literal, which terminates SQL parsing and made\n  the whole `.sql` dump unrestorable (silently-corrupt backup). NUL-bearing text\n  is now emitted as a hex `CAST(x'…' AS TEXT)`, as `sqlite3 .dump` does, so it\n  round-trips. Advisory `ndaal-sa-2026-235`.\n- **Multi-member `.sql.gz` restore no longer drops rows (`dbdump.rs`).**\n  `GzDecoder` decoded only the first gzip member of a concatenated stream and\n  reported success; switched to `MultiGzDecoder` so every member is restored.\n  Advisory `ndaal-sa-2026-236`.\n- **A `*.md` subdirectory no longer aborts template import (`template_import.rs`).**\n  A directory or symlink named `*.md` passed the name filter, then failed\n  `dir.read()`, and that error aborted the entire import permanently on every\n  start-up. Enumeration now skips non-regular-file entries. Advisory\n  `ndaal-sa-2026-237`.\n- **A malformed `GSV_PORT` is now a hard error (`cli.rs`, `main.rs`).** A typo\n  like `GSV_PORT=8443x`, or an out-of-range value, was silently discarded and the\n  server bound the *default* port, so the operator believed the service was on one\n  port while it listened on another. `GSV_PORT` now parses through\n  `cli::parse_port_env`, which errors (naming the bad value) instead of defaulting\n  — mirroring how an invalid `--port` flag is already rejected. Advisory\n  `ndaal-sa-2026-240`.\n- **The accept loop no longer busy-spins on a persistent accept error (`main.rs`).**\n  A failed `accept()` was logged and retried immediately; under descriptor\n  exhaustion (EMFILE/ENFILE) that spun a CPU core at 100% and starved request\n  handling. A 50 ms back-off is now applied after a failed accept. Advisory\n  `ndaal-sa-2026-241`.\n- **CSV mapping parser keeps carriage returns inside quoted fields (`mappings.rs`).**\n  `split_csv_records` dropped every `\\r` to tolerate CRLF line endings, which also\n  deleted a `\\r` that was *data* inside a quoted, multi-line value. `\\r` is now\n  dropped only outside quotes; inside a quoted field it is preserved, so an\n  embedded CRLF round-trips intact. Advisory `ndaal-sa-2026-242`.\n- **`--log-format` now overrides `GSV_LOG_FORMAT` (`cli.rs`, `main.rs`).** The flag\n  and the env var were tested independently, so `--log-format text` did not\n  override `GSV_LOG_FORMAT=json` — inconsistent with every other flag/env pair.\n  The decision now routes through `cli::json_log_format`, where the flag wins.\n  Cosmetic (log output only); advisory `ndaal-sa-2026-243` (CVSS 0.0).\n- **Meilisearch is marked ready only once its documents are queryable (`meili.rs`).**\n  Indexing enqueues an async task (HTTP 202) but `meili_ready` flipped `true`\n  immediately, so a search in the brief startup window returned an empty 200 —\n  which, not being an error, did not fall back to the built-in engine.\n  `index_documents` now polls the enqueued task to `succeeded` before returning.\n  Advisory `ndaal-sa-2026-245`.\n- **Annotation search no longer matches superseded revisions or misses umlaut\n  queries (`store.rs`).** The unencrypted search path matched any revision via\n  SQL `instr(lower(...))` (ASCII-only) while the encrypted path matched only the\n  latest revision in Unicode-aware Rust — so results diverged by store type: a\n  hit could point at a page that no longer contained the term, and `MÜLL` failed\n  to match `müll`. Both store types now share one latest-revision, Unicode-folded\n  path. Advisory `ndaal-sa-2026-246`.\n- **A failed same-stamp database dump no longer deletes a prior dump (`dbdump.rs`).**\n  Cleanup after a partway-failed dump removed every `annotations-<stamp>.*` file\n  by prefix glob, so a second dump that collided on the same timestamp failed on\n  the no-clobber snapshot and then deleted the earlier, completed dump. Cleanup\n  now snapshots the directory before writing and removes only files this call\n  created (its stamp prefix AND absent beforehand). Advisory `ndaal-sa-2026-247`.\n- **Capped annotation search returns the most recent matches (`store.rs`).** The\n  optional local search returned up to 50 matches ordered by ascending control\n  id with no surfaced total, so a note just edited on a higher-id control was\n  silently pushed out of the capped results. Results are now ordered most-\n  recently-edited first (`created_at`, then the monotonic version id), so the cap\n  keeps the newest matches; the doc comment now matches. No data loss, no\n  security impact. Advisory `ndaal-sa-2026-248` (CVSS 0.0).\n\n### Changed\n\n- **`/controls` text filter hoists its needle out of the scan (`filter.rs`).**\n  `matches_text` re-lowercased and re-tokenised the constant query string once\n  per catalogue entry (~998 short allocations per `q=` request). `apply` now\n  lowercases/tokenises once and reuses it; both the single-entry and bulk paths\n  share one tokeniser so they cannot diverge. Behaviour-identical (pinned by\n  `filter::apply_matches_the_per_entry_predicate_including_text`); perf only.\n- **`--seed-demo` now reproduces the exhaustive fifteen-version editor sequence\n  (`demo.rs`).** Previously each target got a light three-version note with one\n  generated badge. It now mirrors `tests/test_annotation_sequence.rs`: a\n  fifteen-version history where V1–V9 interleave the eight real unDraw graphics\n  (art / around-the-world / airport / analysis, SVG + PNG) with the five\n  reusable templates (QS checklist, arc42 DE/EN, MADR DE/EN), and V10–V15 walk\n  the CMMI maturity level (2 → 3 → 4 → 5 → 1 → 4); V2 and V15 are released, the\n  rest drafts. The graphics and templates are embedded\n  (`include_bytes!`/`include_str!`), so the binary stays self-contained. Because\n  assets are stored per target, a full ~1000-control seed is large (~1 GB) —\n  `--seed-demo` remains an explicit opt-in and is non-destructive (existing\n  annotations are never touched).\n\n### Tests\n\n- **Regression tests for the security/correctness fixes above**\n  (`scan::run_capturing_drains_large_stdout`, `store::sql_dump_round_trips_a_nul_byte`,\n  `dbdump::gunzip_capped_decodes_a_concatenated_multi_member_gzip`,\n  `template_import::a_subdirectory_named_dot_md_does_not_abort_the_import`,\n  `cli::env_flag_on_treats_falsy_and_whitespace_as_off`,\n  `cli::parse_port_env_errors_on_garbage_and_never_silently_defaults`,\n  `cli::json_log_format_lets_the_flag_override_the_env`,\n  `mappings::split_csv_records_keeps_a_cr_inside_a_quoted_field`,\n  `dbdump::a_failed_same_stamp_dump_preserves_a_prior_dumps_files`,\n  `store::search_returns_the_most_recently_edited_matches_within_the_limit`).\n- **De-flaked `template_import::over_cap_directory_is_truncated_to_the_limit`.**\n  The truncation-guard test drove the production `import_from_dir`, spawning ~500\n  external ClamAV/YARA subprocesses; under the parallel suite that surfaced as a\n  spurious `dir.read` `NotFound`. It now injects an always-clean scanner via\n  `import_from_dir_with` — same code path, deterministic and fast — since the\n  malware scan is irrelevant to the guard it pins.\n- **Regression tests for the Meilisearch and search fixes**\n  (`meili::timed_turns_an_over_budget_stall_into_a_meili_error`,\n  `meili::timed_passes_a_ready_future_through_unchanged` — under a paused tokio\n  clock; `meili::parse_task_state_classifies_meilisearch_statuses`,\n  `meili::parse_task_uid_reads_the_enqueue_response`,\n  `store::search_matches_only_the_latest_revision_and_folds_unicode_case`).\n- **Bruno endpoint coverage.** The `test/bruno` dev collection now exercises\n  every router endpoint. A new `collections/core/` group covers the read and\n  JSON surfaces (`/`, `/controls` + practice filter, `/control/{id}` +\n  `raw.json` + 404, `/practices`, `/practice/{id}`, `/stats`, `/metadata`,\n  `/catalog.json`, `/search` + `/search/partial`) and a new\n  `collections/info/` group covers the remaining info dialogs (`/about`,\n  `/license`, `/system-info`, `/privacy`, `/security`, `/imprint`,\n  `/changelog`). The annotations save test now sends and asserts the new\n  `maturity` field round-trips. Full suite: 43 requests, 159 assertions,\n  green against the development environment.\n- **Property + fuzz coverage for maturity.** `tests/proptest_invariants.rs`\n  gains two properties: `Maturity::from_token` is total and its digit tokens\n  are disjoint from the `TargetKind`/`Modal`/`Status` word tokens, and every\n  level round-trips through `save_with_maturity`. `fuzz_store_tokens` now\n  includes `Maturity` in its four-way disjointness invariant, and a new\n  `fuzz_store_maturity` target fuzzes the maturity-carrying save/read path\n  (both build clean; 45s smoke run, 1925 execs, no crash).\n- **Playwright maturity coverage.** The annotation-editor spec now selects a\n  non-default level, saves, and asserts it persists on reload, plus a new\n  test that the picker offers all six CMMI levels in order. 9/9 green across\n  chromium, firefox, and webkit."
      },
      {
        "category": "details",
        "text": "Change set: src/store.rs (a new Maturity enum with as_str / label / from_token / all, a maturity column on the versions table with an idempotent ALTER-TABLE migration, and Store::save_with_maturity as the explicit write path; Store::save keeps its signature and defaults to Performed), src/routes/annotation.rs (a maturity_options builder plus the save handler parsing the maturity form field), src/templates/pages/annotations.html (the Maturity Level <select> between Modal and Status), tests/test_annotations.rs + tests/proptest_invariants.rs (a render/ordering test and two Maturity properties: token totality/disjointness and a save_with_maturity round-trip), fuzz/fuzz_targets/fuzz_store_maturity.rs (new) + fuzz/fuzz_targets/fuzz_store_tokens.rs (Maturity added to the token-disjointness invariant), test/bruno/collections/core/ (13 new .bru files) + test/bruno/collections/info/ (7 new .bru files) bringing the dev Bruno suite to full router-endpoint coverage, test/playwright/tests/annotations.spec.ts (maturity select + persistence + a six-level structure test), src/cli.rs (parse_args refactored to a bool_flag_target dispatch table, dropping its cyclomatic complexity from 16 to ~8), the CHANGELOG 1.6.40 section and the README / user-guide / administrator-guide annotation sections, plus this announcement. Verification (1.6.40): cargo fmt --check, cargo clippy --workspace --all-targets --all-features -D warnings, cargo test --workspace --all-features and --doc, htmlhint + markuplint on the changed template, rust-doctor (0 errors / 0 warnings after the parse_args fix), the two Maturity proptests, the fuzz_store_maturity smoke run, the cargo-mutants maturity pass (100% kill), the Miri maturity run (0 UB), the Bruno suite (43 requests / 159 assertions) and the Playwright annotation suite (9/9 across chromium / firefox / webkit) all pass."
      },
      {
        "category": "general",
        "text": "Recommended action: no action is required for security reasons — 1.6.40 discloses no vulnerability and is scored CVSS 0.0 (NONE). Operators who want the CMMI Maturity Level field, the enriched --seed-demo corpus, or any of the fifteen Low-severity hardening fixes (ndaal-sa-2026-234 through -248) may upgrade to 1.6.40: `cargo binstall grundschutz-oscal-viewer` for the prebuilt GitLab release binary (tag v1.6.40), or `cargo install grundschutz-oscal-viewer` to build from crates.io. No configuration change is required; the maturity column is added to an existing annotation database automatically by an idempotent migration on first start."
      },
      {
        "category": "other",
        "text": "CVSS applicability: this announcement is scored CVSS v3.1 0.0 (NONE) and v4.0 0.0 (NONE) - it is a feature / test-coverage / code-quality changeset, not a newly disclosed vulnerability, and it collects no scored findings. The new Maturity Level input is enum-validated (allow-list of 0-5, default-on-unknown) and stored as a bound SQLite parameter, so it adds no injection or panic surface; this was confirmed by proptest, a dedicated cargo-fuzz target, a 100%-kill cargo-mutants pass and a 0-UB Miri run. Status: final."
      },
      {
        "category": "legal_disclaimer",
        "text": "THIS DOCUMENT IS PROVIDED ON AN 'AS IS' BASIS AND DOES NOT IMPLY ANY KIND OF GUARANTEE OR WARRANTY. ndaal Gesellschaft fuer Sicherheit in der Informationstechnik mbH & Co KG DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS INFORMATION INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS."
      }
    ],
    "publisher": {
      "category": "vendor",
      "contact_details": "security@ndaal.eu",
      "issuing_authority": "ndaal Gesellschaft für Sicherheit in der Informationstechnik mbH & Co KG Security Team",
      "name": "ndaal Gesellschaft für Sicherheit in der Informationstechnik mbH & Co KG",
      "namespace": "https://ndaal.eu/csaf"
    },
    "references": [
      {
        "category": "self",
        "summary": "This advisory in CSAF 2.1 format",
        "url": "https://gitlab.com/vPierre/ndaal_public_bsi_grundschutz_oscal_viewer/-/raw/main/csaf/2026/233/ndaal-sa-2026-233.json"
      },
      {
        "category": "external",
        "summary": "CHANGELOG (1.6.40 section)",
        "url": "https://gitlab.com/vPierre/ndaal_public_bsi_grundschutz_oscal_viewer/-/raw/main/CHANGELOG.md"
      },
      {
        "category": "external",
        "summary": "grundschutz-oscal-viewer on crates.io",
        "url": "https://crates.io/crates/grundschutz-oscal-viewer"
      },
      {
        "category": "external",
        "summary": "ndaal_public_bsi_grundschutz_oscal_viewer repository",
        "url": "https://gitlab.com/vPierre/ndaal_public_bsi_grundschutz_oscal_viewer"
      },
      {
        "category": "external",
        "summary": "Maturity Level feature commit (3d97dca6)",
        "url": "https://gitlab.com/vPierre/ndaal_public_bsi_grundschutz_oscal_viewer/-/commit/3d97dca6683875be713d272584281384dfa6f506"
      },
      {
        "category": "external",
        "summary": "Low-severity hardening fix shipped in 1.6.40 (ndaal-sa-2026-234)",
        "url": "https://gitlab.com/vPierre/ndaal_public_bsi_grundschutz_oscal_viewer/-/raw/main/csaf/2026/234/ndaal-sa-2026-234.json"
      },
      {
        "category": "external",
        "summary": "Low-severity hardening fix shipped in 1.6.40 (ndaal-sa-2026-235)",
        "url": "https://gitlab.com/vPierre/ndaal_public_bsi_grundschutz_oscal_viewer/-/raw/main/csaf/2026/235/ndaal-sa-2026-235.json"
      },
      {
        "category": "external",
        "summary": "Low-severity hardening fix shipped in 1.6.40 (ndaal-sa-2026-236)",
        "url": "https://gitlab.com/vPierre/ndaal_public_bsi_grundschutz_oscal_viewer/-/raw/main/csaf/2026/236/ndaal-sa-2026-236.json"
      },
      {
        "category": "external",
        "summary": "Low-severity hardening fix shipped in 1.6.40 (ndaal-sa-2026-237)",
        "url": "https://gitlab.com/vPierre/ndaal_public_bsi_grundschutz_oscal_viewer/-/raw/main/csaf/2026/237/ndaal-sa-2026-237.json"
      },
      {
        "category": "external",
        "summary": "Low-severity hardening fix shipped in 1.6.40 (ndaal-sa-2026-238)",
        "url": "https://gitlab.com/vPierre/ndaal_public_bsi_grundschutz_oscal_viewer/-/raw/main/csaf/2026/238/ndaal-sa-2026-238.json"
      }
    ],
    "title": "grundschutz-oscal-viewer 1.6.40 — release announcement: CMMI Maturity Level field on annotations (six levels 0 Incomplete … 5 Innovating), the exhaustive fifteen-version --seed-demo / test sequence walking the maturity level, full endpoint Bruno coverage and proptest/fuzz/cargo-mutants/Miri verification, and a parse_args complexity refactor; the release also carries fifteen Low-severity hardening fixes tracked in ndaal-sa-2026-234 through -248",
    "tracking": {
      "current_release_date": "2026-07-08T00:00:00.000Z",
      "generator": {
        "engine": {
          "name": "ndaal CSAF Generator",
          "version": "1.0.0"
        }
      },
      "id": "ndaal-sa-2026-233",
      "initial_release_date": "2026-07-07T00:00:00.000Z",
      "revision_history": [
        {
          "date": "2026-07-07T00:00:00.000Z",
          "number": "1.0.0",
          "summary": "Initial advisory: 1.6.40 Maturity Level feature / test-coverage / code-quality changeset announcement. CVSS v3.1 0.0 / v4.0 0.0 (NONE). Status: final."
        },
        {
          "date": "2026-07-08T00:00:00.000Z",
          "number": "1.1.0",
          "summary": "Reissued as the consolidated 1.6.40 release announcement reusing the CHANGELOG [1.6.40] section verbatim; corrected the hardening-fix range to ndaal-sa-2026-234 through -248; added the fifteen-version --seed-demo/test sequence and the CMMI Level 5 “Innovating” rename; recorded the shipped crates.io publish and GitLab v1.6.40 tag (commit bc74db8c). CVSS unchanged: v3.1 0.0 / v4.0 0.0 (NONE)."
        }
      ],
      "status": "final",
      "version": "1.1.0"
    }
  },
  "product_tree": {
    "branches": [
      {
        "category": "vendor",
        "name": "ndaal Gesellschaft für Sicherheit in der Informationstechnik mbH & Co KG",
        "branches": [
          {
            "category": "product_name",
            "name": "grundschutz-oscal-viewer",
            "branches": [
              {
                "category": "product_version_range",
                "name": "vers:cargo/<1.6.40",
                "product": {
                  "name": "grundschutz-oscal-viewer before 1.6.40.",
                  "product_id": "CSAFPID-0001"
                }
              },
              {
                "category": "product_version",
                "name": "1.6.40",
                "product": {
                  "name": "grundschutz-oscal-viewer 1.6.40 — Maturity Level feature / test-coverage cut.",
                  "product_id": "CSAFPID-0002"
                }
              }
            ]
          }
        ]
      }
    ]
  },
  "vulnerabilities": [
    {
      "notes": [
        {
          "category": "description",
          "text": "This advisory announces the 1.6.40 changeset landed on main (commits 3d97dca6 the Maturity Level feature, f6b04e79 the Bruno/proptest/fuzz/Playwright coverage, 913e5e43 the parse_args complexity refactor); the versioned publish to crates.io and the GitLab release tag follow via the release pipeline. 1.6.40 collects no scored findings. The new Maturity Level form field is not an added attack surface: its submitted value is parsed through an enum allow-list (Maturity::from_token accepts only the digits 0-5) and falls back to the default 1 (Performed) on any other input rather than being trusted, and it is persisted exclusively as a bound SQLite parameter, so it is never interpreted as SQL and cannot widen an injection surface. The maturity write/read path was exercised by two proptest properties, a new fuzz_store_maturity libFuzzer target, a cargo-mutants pass over the maturity surface (100% kill of viable mutants) and a Miri run over the FFI-free maturity logic (0 undefined behaviour). The Bruno additions and the parse_args refactor are test-only / internal-behaviour-preserving changes. The running viewer's default-secure posture (127.0.0.1 bind, TLS 1.3 only, no authentication, single-operator local tool) is unchanged."
        },
        {
          "category": "details",
          "text": "Change set: src/store.rs (Maturity enum + maturity column + idempotent migration + Store::save_with_maturity), src/routes/annotation.rs (maturity_options + save-handler form parsing), src/templates/pages/annotations.html (the Maturity Level <select>), tests/test_annotations.rs + tests/proptest_invariants.rs (render/ordering test + two Maturity properties), fuzz/fuzz_targets/fuzz_store_maturity.rs (new) + fuzz/fuzz_targets/fuzz_store_tokens.rs (Maturity in the token-disjointness invariant), test/bruno/collections/core/ (13 .bru) + test/bruno/collections/info/ (7 .bru), test/playwright/tests/annotations.spec.ts (maturity select/persistence + six-level structure test), src/cli.rs (parse_args -> bool_flag_target dispatch table, cyclomatic 16 -> ~8), CHANGELOG 1.6.40 and the README / user-guide / administrator-guide annotation sections. Verification: fmt, clippy -D warnings, cargo test --workspace --all-features + --doc, htmlhint + markuplint, rust-doctor 0/0, the two Maturity proptests, the fuzz_store_maturity smoke, the cargo-mutants maturity pass (100% kill), the Miri maturity run (0 UB), Bruno (43 requests / 159 assertions) and Playwright (9/9) all green."
        }
      ],
      "product_status": {
        "known_affected": [
          "CSAFPID-0001"
        ],
        "fixed": [
          "CSAFPID-0002"
        ]
      },
      "remediations": [
        {
          "category": "vendor_fix",
          "details": "Upgrade to grundschutz-oscal-viewer 1.6.40 (CSAFPID-0002) once published; no security-motivated action is required.",
          "product_ids": [
            "CSAFPID-0002"
          ]
        }
      ],
      "metrics": [
        {
          "content": {
            "cvss_v3": {
              "version": "3.1",
              "vectorString": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:N",
              "baseScore": 0.0,
              "baseSeverity": "NONE",
              "attackVector": "NETWORK",
              "attackComplexity": "HIGH",
              "privilegesRequired": "NONE",
              "userInteraction": "NONE",
              "scope": "UNCHANGED",
              "confidentialityImpact": "NONE",
              "integrityImpact": "NONE",
              "availabilityImpact": "NONE"
            },
            "cvss_v4": {
              "version": "4.0",
              "vectorString": "CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N",
              "baseScore": 0.0,
              "baseSeverity": "NONE",
              "attackVector": "NETWORK",
              "attackComplexity": "HIGH",
              "attackRequirements": "PRESENT",
              "privilegesRequired": "NONE",
              "userInteraction": "NONE",
              "vulnConfidentialityImpact": "NONE",
              "vulnIntegrityImpact": "NONE",
              "vulnAvailabilityImpact": "NONE",
              "subConfidentialityImpact": "NONE",
              "subIntegrityImpact": "NONE",
              "subAvailabilityImpact": "NONE"
            }
          },
          "products": [
            "CSAFPID-0002"
          ]
        }
      ],
      "threats": [
        {
          "category": "impact",
          "details": "No security impact from the changeset itself (CVSS 0.0 / NONE); 1.6.40 collects no scored findings. The new Maturity Level input is enum-validated (allow-list 0-5, default-on-unknown) and stored as a bound SQLite parameter, adding no injection or panic surface."
        }
      ],
      "title": "grundschutz-oscal-viewer 1.6.40 changeset — Maturity Level feature + test coverage + parse_args complexity fix (the 5 Low-severity hardening fixes in the same 1.6.40 cut are tracked in ndaal-sa-2026-234..238)"
    }
  ]
}

Are you sure?